diff --git a/.claude/agents/cf-api-reviewer.md b/.claude/agents/cf-api-reviewer.md new file mode 100644 index 000000000..a7d4cc633 --- /dev/null +++ b/.claude/agents/cf-api-reviewer.md @@ -0,0 +1,68 @@ +--- +name: cf-api-reviewer +description: Reviews Cute Framework public API headers for convention compliance. Use after creating or significantly modifying any file in include/. +color: cyan +--- + +You are a specialized code reviewer for Cute Framework's public C/C++ API conventions. + +**Generated files** — `*_shd.h` files are produced by `build/cute-shaderc` and must not be reviewed or edited. All other `include/cute_*.h` files, including `cute_shader_bytecode.h`, are hand-written and subject to these rules. + +When asked to review a header file, check ALL of the following and report only actual violations (not passing items): + +**1. Copyright header** — First comment block must match exactly: +``` +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ +``` + +**2. Include guard** — Must be `CF__H` where `` = filename with `cute_` stripped, uppercased. +- `cute_physics.h` → `CF_PHYSICS_H` +- `cute.h` → `CF_H` + +**3. extern "C" wrapping** — All C declarations must be inside: +```c +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus +... +#ifdef __cplusplus +} +#endif // __cplusplus +``` + +**4. Function naming** — Every public C function must start with `cf_`. + +**5. Type naming** — Every public struct, enum, and typedef must start with `CF_`. + +**6. Lifecycle verbs** — Creation uses `cf_make_`, destruction uses `cf_destroy_`. Flag other patterns. + +**7. Deprecation pattern** — Deprecated symbols must have `@deprecated` in their doc comment. The deprecated name must be a `CF_INLINE` forwarder to the new name (or vice versa). + +**8. Documentation** — All public declarations must have `/** ... */` block comments (never `///`). Each interior line starts with ` * `. Required tags and order: +1. `@function` / `@struct` / `@enum` — declaration kind, value is the symbol name +2. `@category` — functional grouping (e.g. `graphics`, `audio`, `sprite`, `allocator`) +3. `@brief` — one-line description +4. `@param` — one per parameter, name padded so descriptions align; omit if none +5. `@return` — omit for `void` +6. `@remarks` — optional extended notes; continuation lines align with first word; embedded code uses fenced ` ```c ``` ` blocks +7. `@example` — optional; title follows `>`, code lines are indented (no backtick fences) +8. `@related` — space-separated symbol list on one line (highly recommended) + +Additional inline annotation rules: +- Struct members: `/* @member Description. */` before each field; typedef followed by `// @end` +- Enum values in X-macro blocks: `/* @entry Description. */` before each `CF_ENUM(...)` line, with `/* @end */` as the final entry + +**9. C++ wrappers** — A `namespace Cute` section should exist for non-trivial APIs. + +**10. Include style** — CF headers use quotes (`"cute_defines.h"`), system/SDL use angle brackets (``). + +**11. cute.h** — If the header is new, check that it's been added to `include/cute.h` in a manner consistent with that file's existing grouping/ordering (do not assume strict global alphabetical order). + +**12. CMakeLists.txt** — If a new source file `cute_.cpp` was created, check that it's been added to the `CF_SRCS` source list in the repository root `CMakeLists.txt`. + +Report each violation with `filename:line` where possible. Be concise — list only violations. diff --git a/.claude/hooks/block-generated-files.py b/.claude/hooks/block-generated-files.py new file mode 100644 index 000000000..614e2c658 --- /dev/null +++ b/.claude/hooks/block-generated-files.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python3 +"""PreToolUse hook: blocks edits to generated shader headers (*_shd.h). + +Only files ending in _shd.h are generated by build/cute-shaderc. +cute_shader_bytecode.h is hand-written and NOT blocked. +""" +import sys +import json +import os + +data = json.load(sys.stdin) +tool_input = data.get("tool_input", {}) +file_path = tool_input.get("file_path", "") + +if file_path: + name = os.path.basename(file_path) + if name.endswith("_shd.h"): + print( + f"Blocked: '{name}' is a generated file. " + "Regenerate with build/cute-shaderc instead of editing manually.", + file=sys.stderr, + ) + sys.exit(2) diff --git a/.claude/hooks/check-include-guard.py b/.claude/hooks/check-include-guard.py new file mode 100644 index 000000000..6589d1d2c --- /dev/null +++ b/.claude/hooks/check-include-guard.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +"""PostToolUse hook: warns when an include/ header is missing its expected CF_*_H guard.""" +import sys +import json +import os + +data = json.load(sys.stdin) +tool_input = data.get("tool_input", {}) +file_path = tool_input.get("file_path", "") + +norm = os.path.normpath(file_path) +parts = norm.split(os.sep) +if "include" in parts and file_path.endswith(".h"): + name = os.path.basename(file_path) # e.g. "cute_graphics.h" + base = name[:-2] # strip ".h" + + if base.startswith("cute_"): + rest = base[5:] # "cute_graphics" -> "graphics" + elif base == "cute": + rest = "" + else: + rest = base + + expected = f"CF_{rest.upper()}_H" if rest else "CF_H" + + try: + with open(file_path) as f: + content = f.read() + if expected not in content: + print( + f"WARNING: {name} is missing expected include guard '{expected}'.", + file=sys.stderr, + ) + except OSError: + pass diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 000000000..b9e91f16f --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,26 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": "python3 .claude/hooks/block-generated-files.py" + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": "python3 .claude/hooks/check-include-guard.py" + } + ] + } + ] + } +} diff --git a/.claude/skills/header-api-review/SKILL.md b/.claude/skills/header-api-review/SKILL.md new file mode 100644 index 000000000..39e9dcfd3 --- /dev/null +++ b/.claude/skills/header-api-review/SKILL.md @@ -0,0 +1,211 @@ +--- +name: header-api-review +description: Cute Framework public header conventions and review checklist. Reference before creating or modifying any file in include/. +user-invocable: false +--- + +# CF Header Conventions + +## File Naming +- Public headers: `cute_.h` in `include/` +- Source files: `cute_.cpp` in `src/` + +## Generated vs Hand-Written Headers +- **Generated** (do NOT edit manually): `*_shd.h` shader bytecode files produced by `build/cute-shaderc` +- **Hand-written** (edit normally): all other `include/cute_*.h` files, including `cute_shader_bytecode.h` which defines shared shader structures and is NOT generated + +## Include Guards +Format: `CF__H` where `` is the filename with `cute_` prefix stripped, uppercased. +- `cute_graphics.h` → `CF_GRAPHICS_H` +- `cute_sprite.h` → `CF_SPRITE_H` +- `cute.h` → `CF_H` + +## Copyright Header +Every file must begin with exactly: +``` +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ +``` + +## Header Structure +```c +#ifndef CF_NAME_H +#define CF_NAME_H + +#include "cute_defines.h" +// ... other includes with quotes for CF headers, angle brackets for system/SDL ... + +//-------------------------------------------------------------------------------------------------- +// C API + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// ... C declarations ... + +#ifdef __cplusplus +} +#endif // __cplusplus + +//-------------------------------------------------------------------------------------------------- +// C++ API + +#ifdef __cplusplus +namespace Cute { +// ... C++ wrappers ... +} // namespace Cute +#endif // __cplusplus + +#endif // CF_NAME_H +``` + +## Naming Conventions +- Public C functions: `cf_` prefix, snake_case → `cf_make_sprite`, `cf_draw_line` +- Public C structs/enums/typedefs: `CF_` prefix → `CF_Sprite`, `CF_PIXEL_FORMAT_R8` +- X-macro enum patterns: `CF__DEFS` macro + `CF_` enum typedef +- Static internal functions: `s_` prefix — never in public headers +- C++ namespace: `namespace Cute`, functions snake_case without `cf_` prefix + +## Lifecycle Functions +Prefer `cf_make_(...)` for creation and `cf_destroy_(...)` for destruction. +Avoid other lifecycle verbs unless strongly motivated. + +## Deprecation Pattern +Old name stays as the real implementation; new name is a `CF_INLINE` forwarder (or vice versa). +The deprecated symbol's doc comment must include `@deprecated Use cf_new_name instead.` +```c +/** + * @function cf_old_function + * @category example + * @brief Does the thing. + * @deprecated Use cf_new_function instead. + * @related cf_new_function + */ +CF_INLINE void cf_old_function(int x) { cf_new_function(x); } +``` + +## Documentation Format + +All public declarations use `/** ... */` block comments with ` * ` on every interior line. +**Never use `///` style comments for documentation.** + +### Tag ordering (follow this sequence) +1. `@function` / `@struct` / `@enum` — declaration kind; value is the symbol name +2. `@category` — groups symbol in docs (e.g. `graphics`, `audio`, `input`, `allocator`, `sprite`) +3. `@brief` — one-line summary +4. `@param` — one entry per parameter (omit if none) +5. `@return` — return value description (omit for `void`) +6. `@remarks` — extended notes, caveats, usage details (optional) +7. `@example` — code example with `>` title (optional) +8. `@related` — space-separated list of related symbols (highly recommended) + +### Function / typedef / macro +```c +/** + * @function cf_noise2 + * @category noise + * @brief Generates a random value given a 2D coordinate. + * @param noise The noise settings. + * @param x Noise at this x-component. + * @param y Noise at this y-component. + * @return Returns a random value at the specified point. + * @remarks You're probably looking for image generation functions such as `cf_noise_pixels` or + * `cf_noise_fbm_pixels`. This function is fairly low-level. + * @related CF_Noise cf_make_noise cf_destroy_noise cf_noise2 cf_noise3 cf_noise4 + */ +CF_API float CF_CALL cf_noise2(CF_Noise noise, float x, float y); +``` + +### Struct — with `// @end` marker and `/* @member */` inline comments +```c +/** + * @struct CF_Result + * @category utility + * @brief Information about the result of a function, containing any potential error details. + * @remarks Check if a result is an error or not with `cf_is_error`. + * @related CF_Result cf_is_error cf_result_make cf_result_error cf_result_success + */ +typedef struct CF_Result +{ + /* @member Either 0 for success, or -1 for failure. */ + int code; + + /* @member String containing details about any error encountered. */ + const char* details; +} CF_Result; +// @end +``` + +### Enum — X-macro pattern with `/* @entry */` and `/* @end */` +```c +/** + * @enum CF_PlayDirection + * @category sprite + * @brief The direction a sprite plays frames. + * @related CF_PlayDirection cf_play_direction_to_string CF_Animation + */ +#define CF_PLAY_DIRECTION_DEFS \ + /* @entry Flips through the frames of an animation forwards. */ \ + CF_ENUM(PLAY_DIRECTION_FORWARDS, 0) \ + /* @entry Flips through the frames of an animation backwards. */ \ + CF_ENUM(PLAY_DIRECTION_BACKWARDS, 1) \ + /* @end */ + +typedef enum CF_PlayDirection +{ + #define CF_ENUM(K, V) CF_##K = V, + CF_PLAY_DIRECTION_DEFS + #undef CF_ENUM +} CF_PlayDirection; +``` + +### @example — indented code, title after `>` +```c + * @example > Creating a dynamic array and freeing it afterwards. + * dyna int* a = NULL; + * apush(a, 5); + * afree(a); +``` +Code lines are indented to align under the title text (no triple-backtick fences in `@example`). + +### @remarks — embedded code uses fenced blocks +When `@remarks` contains a multi-line code snippet, use fenced ` ```c ` blocks: +```c + * @remarks The members are stored tightly as an array. To access them: + * + * ```c + * for (int i = 0; i < info.num_uniforms; ++i) { + * printf("%s\n", info.uniforms[i].block_name); + * } + * ``` +``` + +### Formatting rules +- **@param alignment**: pad the parameter name so all descriptions align in a column +- **@remarks continuation**: indent continuation lines to align with the first word of the description +- **@related**: single space-separated line; include both the type (`CF_Foo`) and related functions +- **Inline code**: backticks around all symbol names in prose: `` `cf_make_noise` ``, `` `CF_Noise` `` +- **Links**: standard Markdown `[text](url)` for external docs +- **"Default X" / "Out parameter"**: suffix in `@param` descriptions where applicable + +## Includes +- CF headers: quotes → `#include "cute_defines.h"` +- System/SDL headers: angle brackets → `#include ` +- `cute_defines.h` is almost always needed (provides `CF_INLINE`, `CF_API`, `CF_GLOBAL`, etc.) + +## New Header Checklist +- [ ] Copyright header (exact format) +- [ ] Include guard `CF__H` +- [ ] `#include "cute_defines.h"` at minimum +- [ ] `extern "C" { ... }` wrapping for C section +- [ ] All public C functions: `cf_` prefix +- [ ] All public types: `CF_` prefix +- [ ] `/** ... */` Doxygen comments on all public declarations with `@function`/`@struct`/`@enum`, `@category`, `@brief`, and `@related` tags +- [ ] `namespace Cute` C++ wrappers section +- [ ] Added to `include/cute.h` umbrella include (follow that file's existing include ordering conventions) +- [ ] Source file `cute_.cpp` added to top-level `CMakeLists.txt` (in the `CF_SRCS` list) diff --git a/.github/actions/setup-cmake/action.yml b/.github/actions/setup-cmake/action.yml new file mode 100644 index 000000000..4949277d8 --- /dev/null +++ b/.github/actions/setup-cmake/action.yml @@ -0,0 +1,11 @@ +name: Setup CMake +description: Install CMake 4.2.x and Ninja (latest) via lukka/get-cmake + +runs: + using: composite + steps: + - name: Setup CMake 4.2.x + uses: lukka/get-cmake@latest + with: + cmakeVersion: 4.2.3 + ninjaVersion: latest diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6e13e79c9..e255d3330 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,17 +1,12 @@ -name: Build CF +name: Build # This workflow will work for pushes, pull requests, or a manual trigger (workflow_dispatch). on: [push, pull_request, workflow_dispatch] -env: - CMAKE_BUILD_PARALLEL_LEVEL: 2 - jobs: build: - # Matrix feature for cross-platform coverage. Names the build "Build X" - # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix - name: "Build ${{ matrix.platform.name }} (CF_RUNTIME_SHADER_COMPILATION=${{ matrix.runtime_shader_compilation.value }})" - runs-on: ${{ matrix.platform.os }} + name: "${{ matrix.os }} / ${{ matrix.compiler }} / ${{ matrix.runtime_shader_compilation == 'ON' && 'runtime-shaders' || 'precompiled-shaders' }} / ${{ matrix.framework_static == 'ON' && 'static' || 'shared' }}" + runs-on: ${{ matrix.os }} timeout-minutes: 20 defaults: @@ -22,129 +17,166 @@ jobs: # Don't cancel other in-progress jobs if one fails. fail-fast: false matrix: - runtime_shader_compilation: - - { value: "ON", cmake_arg: "-DCF_RUNTIME_SHADER_COMPILATION=ON" } - - { value: "OFF", cmake_arg: "-DCF_RUNTIME_SHADER_COMPILATION=OFF" } - platform: - # Diabled MinGW as it was having trouble with DX12 headers being out of date. - # - { name: "Windows (MinGW-w64)", os: windows-latest, artifact: 'mingw-w64', generate: '-G "MinGW Makefiles"' } - - { - name: "Windows (MSCV 17 2022)", - os: windows-2025, - artifact: "mscv17", - generate: '-G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release', - } - - { - name: "MacOS", - os: macos-latest, - artifact: "macos", - generate: '-G "Unix Makefiles"', - } - - { - name: "Linux", - os: ubuntu-latest, - artifact: "linux", - generate: '-G "Unix Makefiles"', - } - # TODO - Add iOS and Android platforms. Waiting on better support from GitHub. + os: [ubuntu-latest, macos-latest, windows-2025-vs2026] + compiler: [gcc, clang, msvc] + runtime_shader_compilation: [ON, OFF] + framework_static: [ON, OFF] + exclude: + # msvc is only available on Windows + - os: ubuntu-latest + compiler: msvc + - os: macos-latest + compiler: msvc + # gcc on Windows would require MinGW — excluded for simplicity + - os: windows-2025-vs2026 + compiler: gcc + # framework_static OFF only for specific combos + - os: ubuntu-latest + compiler: clang + framework_static: OFF + - os: macos-latest + compiler: gcc + framework_static: OFF steps: - - uses: actions/checkout@master - # Linux needs to install various dependencies. - # This could potentially be removed by using SOKOL_EXTERNAL_GL_LOADER. - - name: Install OpenGL + Audio + - name: Install Linux Dependencies run: | sudo apt-get update -qq - sudo apt-get install gcc-multilib - sudo apt-get install libasound2-dev libpulse-dev - sudo apt-get install -y --no-install-recommends libglfw3 libglfw3-dev libx11-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libxext-dev libxfixes-dev - if: matrix.platform.name == 'Linux' - - - name: Create build folder with CMake - run: | - cmake ${{ matrix.platform.generate }} ${{ matrix.runtime_shader_compilation.cmake_arg }} -B build_folder - - - name: Build project binary (non-MSVC) - run: | - cmake --build build_folder - if: matrix.platform.name != 'Windows (MSCV 17 2022)' + sudo apt-get install -y --no-install-recommends build-essential git make \ + pkg-config gnome-desktop-testing \ + libaudio-dev libfribidi-dev \ + libxkbcommon-dev libgl1-mesa-dev libgles2-mesa-dev \ + libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libthai-dev \ + liburing-dev + if: runner.os == 'Linux' - # Default for MSVC is to build with Debug, so a special case is needed to produce - # Release binaries. - - name: Build project binary (MSVC) - run: | - cmake --build build_folder --config Release - if: matrix.platform.name == 'Windows (MSCV 17 2022)' + - uses: actions/checkout@master - # Run tests for MSVC 17 - - name: Run Tests for MSVC 17 - shell: cmd - run: | - cd build_folder\Release - tests.exe - cd ..\.. - if: matrix.platform.name == 'Windows (MSCV 17 2022)' + - uses: ./.github/actions/setup-cmake - # Run tests for MinGW (no Release folder) - - name: Run Tests for MinGW - shell: cmd - run: | - cd build_folder - tests.exe - docsparser.exe - cd .. - if: matrix.platform.name == 'Windows (MinGW)' + - name: Setup CCache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: ${{ runner.os }}-${{matrix.compiler}}-${{ matrix.runtime_shader_compilation }}-${{ matrix.framework_static }} - - name: Run Tests for Apple - run: | - cd build_folder - ./tests - ./docsparser - cd .. - if: runner.os == 'macOS' + - name: Set up MSVC developer environment + uses: ilammy/msvc-dev-cmd@v1 + if: matrix.compiler == 'msvc' - - name: Run Tests for Linux + - name: Set compiler environment + shell: bash run: | - cd build_folder - ./tests - ./docsparser - cd .. - if: runner.os == 'Linux' + case "${{ matrix.compiler }}" in + gcc) + echo "CC=gcc" >> "$GITHUB_ENV" + echo "CXX=g++" >> "$GITHUB_ENV" + ;; + clang) + echo "CC=clang" >> "$GITHUB_ENV" + echo "CXX=clang++" >> "$GITHUB_ENV" + ;; + msvc) + echo "CC=cl" >> "$GITHUB_ENV" + echo "CXX=cl" >> "$GITHUB_ENV" + ;; + esac + + - name: Configure + shell: bash + run: > + cmake -B build -G Ninja + -DCMAKE_C_COMPILER_LAUNCHER=ccache + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + -DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=Embedded + -DCMAKE_POLICY_CMP0141=NEW + -DSDL_ALSA=OFF + -DSDL_CCACHE=ON + -DSDL_JACK=OFF + -DSDL_KMSDRM=OFF + -DSDL_OPENGL=OFF + -DSDL_OPENGLES=OFF + -DSDL_PIPEWIRE=OFF + -DSDL_PULSEAUDIO=OFF + -DSDL_RENDER=OFF + -DSDL_SNDIO=OFF + -DSDL_UNIX_CONSOLE_BUILD=ON + -DSDL_WAYLAND=OFF + -DSDL_X11=OFF + -DCF_RUNTIME_SHADER_COMPILATION=${{ matrix.runtime_shader_compilation }} + -DCF_FRAMEWORK_STATIC=${{ matrix.framework_static }} + + - name: Build + run: cmake --build build + + - name: Tests + run: ./build/tests - # Just call robocopy twice, one looking for libcute.a for MinGW, and one - # looking for cute.lib for MSCV. robocopy doesn't trigger a build failure for - # not finding a file. - name: Generate artifacts for Windows shell: cmd run: | robocopy include dist *.h - robocopy build_folder\Release\lib dist cute*.lib - robocopy build_folder\lib dist libcute*.a - 7z a ${{ matrix.platform.artifact }}.zip .\dist\* + if %errorlevel% geq 8 exit /b 1 + robocopy build\lib dist cute*.lib + if %errorlevel% geq 8 exit /b 1 + 7z a artifact.zip .\dist\* if: runner.os == 'Windows' - name: Generate artifacts for MacOS run: | mkdir -p ./dist cp -v ./include/*.h ./dist - cp -R -v ./build_folder/lib/libcute*.a ./dist - tar -czvf ${{ matrix.platform.artifact }}.tar.gz -C dist . + cp -R -v ./build/lib/libcute*.a ./dist + tar -czvf artifact.tar.gz -C dist . if: runner.os == 'macOS' - name: Generate artifacts for Linux run: | mkdir -p ./dist cp -v ./include/*.h ./dist - cp -v ./build_folder/lib/libcute*.a ./dist - tar -czvf ${{ matrix.platform.artifact }}.tar.gz -C dist . + cp -v ./build/lib/libcute*.a ./dist + tar -czvf artifact.tar.gz -C dist . if: runner.os == 'Linux' - # Uploads the zip archives onto the workflow. They can be downloaded for 90 days (by default). - # Find them here: https://github.com/RandyGaul/cute_framework/actions - - uses: actions/upload-artifact@v4 + - name: Upload artifact + uses: actions/upload-artifact@v4 with: + name: ${{ matrix.os }}-${{ matrix.compiler }}-shader-${{ matrix.runtime_shader_compilation == 'ON' && 'runtime-shaders' || 'precompiled-shaders' }}-${{ matrix.framework_static == 'ON' && 'static' || 'shared' }} if-no-files-found: error - name: ${{ matrix.platform.artifact }} - path: ./dist/ - if: matrix.runtime_shader_compilation.value == 'ON' + path: | + artifact.zip + artifact.tar.gz + + emscripten: + name: "emscripten / emcc / precompiled-shaders / static" + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - uses: actions/checkout@master + + - uses: ./.github/actions/setup-cmake + + - name: Setup Emscripten + uses: mymindstorm/setup-emsdk@v14 + with: + version: latest + actions-cache-folder: "emsdk-cache" + + - name: Setup CCache + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: emscripten + + - name: Verify Emscripten + run: emcc -v + + - name: Configure + run: > + emcmake cmake -B build + -DCMAKE_C_COMPILER_LAUNCHER=ccache + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + -DCF_FRAMEWORK_STATIC=ON + + - name: Build + run: cmake --build build diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml index f19b02c6a..83a6f877b 100644 --- a/.github/workflows/documentation.yml +++ b/.github/workflows/documentation.yml @@ -1,50 +1,141 @@ name: Documentation + on: push: - branches: - - master + branches: [master] + pull_request: workflow_dispatch: -env: - PYTHON_VERSION: 3.x - CMAKE_BUILD_PARALLEL_LEVEL: 2 +permissions: + contents: read + pages: write + id-token: write jobs: - build: + docs: name: Build documentation runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 + - uses: ./.github/actions/setup-cmake + - name: Install dependencies run: | sudo apt-get update -qq - sudo apt-get install -y --no-install-recommends libcairo2-dev libfreetype6-dev libffi-dev libjpeg-dev libpng-dev libz-dev - sudo apt-get install -y --no-install-recommends pngquant gcc-multilib libasound2-dev libpulse-dev \ - libglfw3 libglfw3-dev libx11-dev libxcursor-dev libxrandr-dev libxinerama-dev libxi-dev libxext-dev libxfixes-dev + sudo apt-get install -y --no-install-recommends \ + libgl1-mesa-dev libgles2-mesa-dev libegl1-mesa-dev \ + libcairo2-dev libfreetype6-dev libffi-dev libjpeg-dev libpng-dev libz-dev pngquant - - name: Set up Python runtime - uses: actions/setup-python@v5 + - name: Compile docsparser + run: gcc tools/docs_parser.c -Ilibraries -o docsparser + + - name: Generate documentation + run: ./docsparser + + - name: Setup CCache (native) + uses: hendrikmuhs/ccache-action@v1.2 with: - python-version: ${{ env.PYTHON_VERSION }} + key: docs-native - - name: Install Python dependencies + - name: Configure native build run: | - pip install mkdocs mkdocs-material "mkdocs-material[imaging]" markdown-callouts + cmake -S . -B build-native -GNinja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DCF_FRAMEWORK_BUILD_SAMPLES=OFF \ + -DCF_FRAMEWORK_BUILD_TESTS=OFF \ + -DCF_RUNTIME_SHADER_COMPILATION=OFF \ + -DCF_CUTE_SHADERC=ON \ + -DCF_BUILD_DOCSPARSER=OFF \ + -DSDL_AUDIO=OFF \ + -DSDL_CAMERA=OFF \ + -DSDL_DBUS=OFF \ + -DSDL_DIALOG=OFF \ + -DSDL_DIRECTX=OFF \ + -DSDL_HAPTIC=OFF \ + -DSDL_HIDAPI=OFF \ + -DSDL_JOYSTICK=OFF \ + -DSDL_KMSDRM=OFF \ + -DSDL_METAL=OFF \ + -DSDL_OFFSCREEN=OFF \ + -DSDL_OPENGL=OFF \ + -DSDL_OPENGLES=OFF \ + -DSDL_POWER=OFF \ + -DSDL_RENDER=OFF \ + -DSDL_SENSOR=OFF \ + -DSDL_TESTS=OFF \ + -DSDL_UNIX_CONSOLE_BUILD=ON \ + -DSDL_VULKAN=OFF \ + -DSDL_WAYLAND=OFF \ + -DSDL_X11=OFF - - name: Docs parser + - name: Build cute-shaderc + run: cmake --build build-native --target cute-shaderc + + - name: Generate precompiled shader headers run: | - cmake -B build/debug -DCF_RUNTIME_SHADER_COMPILATION=OFF -DCF_CUTE_SHADERC=OFF - cmake --build build/debug --target docsparser - cd build/debug - ./docsparser + echo "Compiling builtin shaders..." + build-native/cute-shaderc \ + -type=builtin \ + -oheader=src/data/builtin_shaders_bytecode.h + find samples -name "*.shd" | while read shd_file; do + dir=$(dirname "$shd_file") + base=$(basename "$shd_file" .shd) + echo "Compiling shader: $shd_file -> ${dir}/${base}_shd.h" + build-native/cute-shaderc \ + -type=draw \ + -varname="s_${base}_shd_bytecode" \ + -oheader="${dir}/${base}_shd.h" \ + "$shd_file" + done - - name: Build documentation - id: build + - name: Set up Emscripten + uses: mymindstorm/setup-emsdk@v14 + with: + version: latest + actions-cache-folder: "emsdk-cache" + + - name: Setup CCache (emscripten) + uses: hendrikmuhs/ccache-action@v1.2 + with: + key: docs-emscripten + + - name: Pre-build Emscripten ports run: | - mkdocs build --clean - mkdocs --version + mkdir -p "$(em-config CACHE)/sysroot/lib/pkgconfig" + embuilder build sdl3 + + - name: Configure Emscripten build + run: | + emcmake cmake -S . -B build-emscripten -GNinja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_C_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \ + -DCMAKE_CXX_SCAN_FOR_MODULES=OFF \ + -DCF_FRAMEWORK_BUILD_SAMPLES=ON \ + -DCF_FRAMEWORK_BUILD_TESTS=OFF \ + -DCF_BUILD_DOCSPARSER=OFF + + - name: Build Emscripten samples + run: cmake --build build-emscripten + + - name: Generate sample pages + run: | + ruby ./tools/generate_sample_pages.rb build-emscripten docs https://github.com/RandyGaul/cute_framework + + - name: Set up Python runtime + uses: actions/setup-python@v5 + with: + python-version: 3.x + + - name: Install Python dependencies + run: pip install -r docs/requirements.txt + + - name: Build documentation + run: zensical build --clean - name: Adjust permissions run: | @@ -53,21 +144,19 @@ jobs: done - name: Upload to GitHub Pages - id: deployment uses: actions/upload-pages-artifact@v3 with: path: site deploy: - # Add a dependency to the build job - needs: build + name: Deploy documentation + needs: docs + if: github.ref == 'refs/heads/master' && github.event_name != 'pull_request' - # Grant GITHUB_TOKEN the permissions required to make a Pages deployment permissions: - pages: write # to deploy to Pages - id-token: write # to verify the deployment originates from an appropriate source + pages: write + id-token: write - # Deploy to the github-pages environment environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} diff --git a/.gitignore b/.gitignore index 016094555..7a07ecbb1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,11 @@ build*/ docs/*/*.md +!docs/samples/*.md docs/api_reference.md !docs/topics/*.md +/docs/samples/play/ site/ +.venv/ .cache .DS_Store cute_framework.zip diff --git a/CMakeLists.txt b/CMakeLists.txt index 43187f738..34173d9c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,15 +1,16 @@ -cmake_minimum_required(VERSION 3.14) +cmake_minimum_required(VERSION 4.2) project(cute_framework DESCRIPTION "The cutest framework out there for creating 2D games in C++!" HOMEPAGE_URL "https://github.com/RandyGaul/cute_framework" LANGUAGES C CXX - VERSION 1.1.0 + VERSION 1.1.1 ) -# Must have at least C++20 and C17. +# Must have at least C++20 and C23. set(CMAKE_CXX_STANDARD 20) -set(CMAKE_C_STANDARD 17) +set(CMAKE_C_STANDARD 23) +include(cmake/StandardProjectSettings.cmake) include(GNUInstallDirs) # These are needed for how we use FetchContent. include(FetchContent) @@ -23,14 +24,20 @@ option(CF_CUTE_SHADERC "Build cute-shaderc, an offline shader compiler (requires option(CF_FRAMEWORK_APPLE_FRAMEWORK "Build CF libraries as Apple Framework" OFF) # Make sure all libraries are placed into the same output folder. -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +endif () # Platform detection. # Put Emscripten first so it doesn't fall into the generic UNIX/Linux path. if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten" OR DEFINED EMSCRIPTEN) set(EMSCRIPTEN TRUE) +elseif(CMAKE_SYSTEM_NAME STREQUAL "Android" OR DEFINED ANDROID) + set(ANDROID TRUE) + elseif(WIN32) set(WINDOWS TRUE) @@ -61,6 +68,7 @@ if(MSVC) add_definitions(-D_CRT_SECURE_NO_DEPRECATE) add_compile_options("$<$:/utf-8>") add_compile_options("$<$:/utf-8>") + add_compile_options(/MP) endif() if(CF_FRAMEWORK_WITH_HTTPS MATCHES OFF) @@ -84,7 +92,8 @@ include_directories(src libraries test) # Cute Framework shared library. set(CF_SRCS src/cute_app.cpp - src/cute_array.cpp + src/cute_binding.cpp + src/cute_ckit.cpp src/cute_audio.cpp src/cute_clipboard.cpp src/cute_multithreading.cpp @@ -94,8 +103,6 @@ set(CF_SRCS src/cute_version.cpp src/cute_json.cpp src/cute_base64.cpp - src/cute_hashtable.cpp - src/cute_string.cpp src/cute_math.cpp src/cute_draw.cpp src/cute_image.cpp @@ -104,7 +111,7 @@ set(CF_SRCS src/cute_graphics_gles.cpp src/cute_graphics_glad.cpp src/cute_aseprite_cache.cpp - src/cute_png_cache.cpp + src/cute_custom_sprite.cpp src/cute_https.cpp src/cute_joypad.cpp src/cute_symbol.cpp @@ -163,7 +170,7 @@ set(CF_PUBLIC_HDRS include/cute_json.h include/cute_base64.h include/cute_array.h - include/cute_hashtable.h + include/cute_map.h include/cute_string.h include/cute_defer.h include/cute_math.h @@ -175,7 +182,7 @@ set(CF_PUBLIC_HDRS include/cute_graphics.h include/cute_rnd.h include/cute_sprite.h - include/cute_png_cache.h + include/cute_custom_sprite.h include/cute_https.h include/cute_joypad.h include/cute_priority_queue.h @@ -206,7 +213,7 @@ set(CF_HDRS src/internal/cute_app_internal.h src/internal/cute_input_internal.h src/internal/cute_serialize_internal.h - src/internal/cute_png_cache_internal.h + src/internal/cute_custom_sprite_internal.h src/internal/cute_draw_internal.h src/internal/cute_font_internal.h src/internal/cute_graphics_internal.h @@ -250,15 +257,36 @@ if(EMSCRIPTEN) target_compile_options(cute PRIVATE -O1 -fno-rtti -fno-exceptions) endif() +if(ANDROID) + message(STATUS "Configuring CF for Android") + + # Android-specific options. + set(CF_CUTE_SHADERC OFF CACHE BOOL "" FORCE) + set(CF_FRAMEWORK_APPLE_FRAMEWORK OFF CACHE BOOL "" FORCE) + set(PHYSFS_BUILD_SHARED OFF CACHE BOOL "" FORCE) + set(PHYSFS_BUILD_STATIC ON CACHE BOOL "" FORCE) + + # Link Android-specific libraries. + # GLESv3 needed for imgui OpenGL3 backend (VAO functions). + set(CF_LINK_LIBS ${CF_LINK_LIBS} android log GLESv3 EGL) +endif() + # PhysicsFS for virtual file system and archive support. -set(PHYSFS_BUILD_SHARED $ CACHE BOOL "Build PhysFS as shared library") -set(PHYSFS_BUILD_STATIC ${CF_FRAMEWORK_STATIC} CACHE BOOL "Build PhysFS as static library") +if(CF_FRAMEWORK_STATIC) + set(PHYSFS_BUILD_SHARED OFF CACHE BOOL "Build PhysFS as shared library") + set(PHYSFS_BUILD_STATIC ON CACHE BOOL "Build PhysFS as static library") +else() + set(PHYSFS_BUILD_SHARED ON CACHE BOOL "Build PhysFS as shared library") + set(PHYSFS_BUILD_STATIC OFF CACHE BOOL "Build PhysFS as static library") +endif() set(PHYSFS_BUILD_TEST OFF CACHE BOOL "Do not build PhysFS tests.") set(PHYSFS_BUILD_DOCS OFF CACHE BOOL "Do not build PhysFS docs.") FetchContent_Declare( physfs GIT_REPOSITORY https://github.com/icculus/physfs.git GIT_TAG 9d18d36b5a5207b72f473f05e1b2834e347d8144 + UPDATE_DISCONNECTED TRUE + EXCLUDE_FROM_ALL ) FetchContent_MakeAvailable(physfs) @@ -291,9 +319,10 @@ if(LINUX) FetchContent_Declare( s2n - URL https://github.com/aws/s2n-tls/archive/refs/tags/v1.3.46.zip + URL https://github.com/aws/s2n-tls/archive/refs/tags/v1.6.3.zip DOWNLOAD_EXTRACT_TIMESTAMP ON - GIT_PROGRESS TRUE + UPDATE_DISCONNECTED TRUE + EXCLUDE_FROM_ALL ) FetchContent_MakeAvailable(s2n) set(CF_LINK_LIBS ${CF_LINK_LIBS} s2n) @@ -349,18 +378,24 @@ endif() if(NOT EMSCRIPTEN) # Emscripten provides it's own SDL3. # SDL for platform support. - set(SDL_REQUIRED_VERSION 3.2.16) + set(SDL_REQUIRED_VERSION 3.4.0) # Just don't build the shared library at all, it's not needed. set(SDL_SHARED_ENABLED_BY_DEFAULT OFF) - set(SDL_STATIC ${CF_FRAMEWORK_STATIC} CACHE BOOL "Build SDL as a static library") - set(SDL_SHARED $ CACHE BOOL "Do not build SDL as a shared library") + if(CF_FRAMEWORK_STATIC) + set(SDL_STATIC ON CACHE BOOL "Build SDL as a static library") + set(SDL_SHARED OFF CACHE BOOL "Do not build SDL as a shared library") + else() + set(SDL_STATIC OFF CACHE BOOL "Do not build SDL as a static library") + set(SDL_SHARED ON CACHE BOOL "Build SDL as a shared library") + endif() set(SDL_TEST_LIBRARY OFF CACHE BOOL "Do not build SDL tests") set(SDL_EXAMPLES OFF CACHE BOOL "Do not build SDL examples") FetchContent_Declare( SDL3 URL https://github.com/libsdl-org/SDL/releases/download/release-${SDL_REQUIRED_VERSION}/SDL3-${SDL_REQUIRED_VERSION}.zip DOWNLOAD_EXTRACT_TIMESTAMP ON - CMAKE_ARGS -DSDL_SHARED=$ -DSDL_STATIC=${CF_FRAMEWORK_STATIC} -DSDL_TEST_LIBRARY=OFF -DSDL_TESTS=OFF -DSDL_EXAMPLES=OFF + UPDATE_DISCONNECTED TRUE + EXCLUDE_FROM_ALL ) FetchContent_MakeAvailable(SDL3) if(CF_FRAMEWORK_STATIC) @@ -369,11 +404,32 @@ if(NOT EMSCRIPTEN) # Emscripten provides it's own SDL3. set(CF_LINK_LIBS ${CF_LINK_LIBS} SDL3::SDL3) endif() target_include_directories(cute PUBLIC ${SDL3_INCLUDE_DIRS}) + + # Build-time tool to extract D3D12 struct layout offsets from SDL3 private headers. + if(WIN32) + set(D3D12_LAYOUT_HEADER "${CMAKE_CURRENT_BINARY_DIR}/generated/d3d12_layout.gen.h") + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/generated") + add_executable(gen_d3d12_layout EXCLUDE_FROM_ALL tools/gen_d3d12_layout.c) + add_custom_command( + OUTPUT ${D3D12_LAYOUT_HEADER} + COMMAND gen_d3d12_layout + "${sdl3_SOURCE_DIR}/src/gpu/SDL_sysgpu.h" + "${sdl3_SOURCE_DIR}/src/gpu/d3d12/SDL_gpu_d3d12.c" + "${D3D12_LAYOUT_HEADER}" + DEPENDS gen_d3d12_layout + "${sdl3_SOURCE_DIR}/src/gpu/SDL_sysgpu.h" + "${sdl3_SOURCE_DIR}/src/gpu/d3d12/SDL_gpu_d3d12.c" + COMMENT "Generating D3D12 struct layout offsets" + ) + add_custom_target(gen_d3d12_layout_target DEPENDS ${D3D12_LAYOUT_HEADER}) + add_dependencies(cute gen_d3d12_layout_target) + target_include_directories(cute PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/generated") + endif() endif() if (CF_RUNTIME_SHADER_COMPILATION OR CF_CUTE_SHADERC) # glslang for shader compilation. - set(GLSLANG_TESTS_DEFAULT OFF CACHE BOOL "Do not build glslang tests.") + set(GLSLANG_TESTS OFF CACHE BOOL "Do not build glslang tests.") set(BUILD_SHARED_LIBS OFF CACHE BOOL "Do not build glslang as a shared lib.") set(ENABLE_OPT ON CACHE BOOL "Enable optimization.") set(SKIP_SPIRV_TOOLS_INSTALL OFF CACHE BOOL "Don't install the SPIR-V Tools as they are built.") @@ -387,70 +443,45 @@ if (CF_RUNTIME_SHADER_COMPILATION OR CF_CUTE_SHADERC) URL https://github.com/KhronosGroup/glslang/archive/refs/tags/16.0.0.zip DOWNLOAD_EXTRACT_TIMESTAMP ON PATCH_COMMAND ${Python3_EXECUTABLE} update_glslang_sources.py + EXCLUDE_FROM_ALL ) FetchContent_MakeAvailable(glslang) - - # cute-shader, a glslang wrapper - set(CUTE_SHADER_SRCS src/cute_shader/cute_shader.cpp) - add_library(cute-shader STATIC ${CUTE_SHADER_SRCS}) - target_link_libraries(cute-shader PRIVATE glslang glslang-default-resource-limits SPIRV spirv-cross-c) - install(TARGETS cute-shader - BUNDLE DESTINATION . - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - if (NOT MSVC AND NOT CF_FRAMEWORK_STATIC) - set_target_properties(cute-shader PROPERTIES POSITION_INDEPENDENT_CODE TRUE) - endif() - target_include_directories(cute-shader PUBLIC include) # For cute_shader_info.h - - # Linking cute-shader for online shader compilation (optional). - if (CF_RUNTIME_SHADER_COMPILATION) - target_compile_definitions(cute PUBLIC CF_RUNTIME_SHADER_COMPILATION) - set(CF_LINK_LIBS ${CF_LINK_LIBS} cute-shader) - endif() - - # cute-shaderc, an offline shader compiler - if (CF_CUTE_SHADERC) - set(CUTE_SHADERC_SRCS src/cute_shader/cute_shaderc.cpp) - add_executable(cute-shaderc ${CUTE_SHADERC_SRCS}) - target_link_libraries(cute-shaderc cute-shader) - install(TARGETS cute-shaderc - BUNDLE DESTINATION . - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) - endif() endif() +add_subdirectory(tools) + # SPIRV-Cross for SDL_shadercross if(NOT EMSCRIPTEN) # SPIRV-Cross is not needed for web. + # Set all SPIRV-Cross options BEFORE FetchContent_Declare/MakeAvailable set(SPIRV_CROSS_CLI OFF CACHE BOOL "Turn off building SPIRV-Cross CLI.") - FetchContent_Declare( - SPIRV_CROSS - GIT_REPOSITORY https://github.com/KhronosGroup/SPIRV-Cross.git - GIT_TAG 0a88b2d5c08708d45692b7096a0a84e7bfae366c - CMAKE_ARGS -DSPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS=ON -DSPIRV_CROSS_CLI=OFF -DSPIRV_CROSS_SHARED=OFF -DSPIRV_CROSS_STATIC=ON - UPDATE_DISCONNECTED TRUE - GIT_PROGRESS TRUE - ) set(SPIRV_CROSS_ENABLE_TESTS OFF CACHE BOOL "Disable SPIRV-Cross tests") set(SPIRV_CROSS_ENABLE_GLSLANG_INSTALL OFF CACHE BOOL "Disable GLSLANG install") - set(SPIRV_CROSS_SHARED OFF CACHE BOOL "Build SPIRV-Cross as shared library") - set(SPIRV_CROSS_STATIC ON CACHE BOOL "Do not build SPIRV-Cross as static library") + set(SPIRV_CROSS_SHARED OFF CACHE BOOL "Do not build SPIRV-Cross as shared library") + set(SPIRV_CROSS_STATIC ON CACHE BOOL "Build SPIRV-Cross as static library") set(SPIRV_CROSS_C_API ON CACHE BOOL "Enable the C API") set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON CACHE BOOL "Turn off exceptions in SPIRV-Cross.") set(SPIRV_CROSS_ENABLE_REFLECT OFF CACHE BOOL "Turn off reflection in SPIRV-Cross.") set(SPIRV_CROSS_SKIP_INSTALL OFF CACHE BOOL "Turn off install targets in SPIRV-Cross.") set(SPIRV_CROSS_ENABLE_UTIL OFF CACHE BOOL "Turn off utils in SPIRV-Cross.") set(SPIRV_CROSS_ENABLE_CPP OFF CACHE BOOL "Turn off CPP target support in SPIRV-Cross.") - set(SPIRV_CROSS_FORCE_PIC $ CACHE BOOL "Force position-independent code for all targets.") - if (WINDOWS) - set(SPIRV_CROSS_ENABLE_MSL OFF CACHE BOOL "Turn off MSL support when on windows.") + if(CF_FRAMEWORK_STATIC) + set(SPIRV_CROSS_FORCE_PIC OFF CACHE BOOL "Force position-independent code for all targets.") + else() + set(SPIRV_CROSS_FORCE_PIC ON CACHE BOOL "Force position-independent code for all targets.") + endif() + if(WINDOWS) + set(SPIRV_CROSS_ENABLE_MSL OFF CACHE BOOL "Turn off MSL support when on Windows.") endif() - if (APPLE) - set(SPIRV_CROSS_ENABLE_HLSL OFF CACHE BOOL "Turn off HLSL support when on windows.") + if(APPLE) + set(SPIRV_CROSS_ENABLE_HLSL OFF CACHE BOOL "Turn off HLSL support when on Apple.") endif() + FetchContent_Declare( + SPIRV_CROSS + GIT_REPOSITORY https://github.com/KhronosGroup/SPIRV-Cross.git + GIT_TAG 0a88b2d5c08708d45692b7096a0a84e7bfae366c + UPDATE_DISCONNECTED TRUE + EXCLUDE_FROM_ALL + ) FetchContent_MakeAvailable(SPIRV_CROSS) set(CF_LINK_LIBS ${CF_LINK_LIBS} spirv-cross-c) endif() @@ -486,7 +517,7 @@ if (CF_CUTE_SHADERC) COMMAND cute-shaderc -type=builtin -oheader=${CMAKE_CURRENT_SOURCE_DIR}/src/data/builtin_shaders_bytecode.h - DEPENDS src/cute_shader/builtin_shaders.h + DEPENDS tools/builtin_shaders.h DEPENDS cute-shaderc ) endif() @@ -497,6 +528,7 @@ target_link_libraries(cute PUBLIC ${CF_LINK_LIBS}) if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) option(CF_FRAMEWORK_BUILD_TESTS "Build the cute framework unit tests." ON) option(CF_FRAMEWORK_BUILD_SAMPLES "Build the cute framework sample programs." ON) + option(CF_BUILD_DOCSPARSER "Build the docsparser documentation generator." ON) # Cute unit tests executable (optional, defaulted to also build). if (CF_FRAMEWORK_BUILD_TESTS) @@ -513,6 +545,7 @@ endif() target_include_directories(cute PUBLIC $) target_include_directories(cute PUBLIC $) target_include_directories(cute PUBLIC $) +target_include_directories(cute PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tools) # Install target install(TARGETS cute diff --git a/README.md b/README.md index 559730631..836c78f9f 100644 --- a/README.md +++ b/README.md @@ -121,14 +121,17 @@ And that's it! Regenerate your project and you will be able to build your new sa All of CF's docs for the [CF website](https://randygaul.github.io/cute_framework/) are located here in GitHub [under the docs folder](https://github.com/RandyGaul/cute_framework/tree/master/docs). Editing any of these docs is a good way to contribute to CF through a pull-request. -Please note that the reference pages for functions/structs are automatically generated by CF's [docs parser + generator](https://github.com/RandyGaul/cute_framework/blob/master/samples/docs_parser.cpp). You can run this executable to regenerate all of the docs files for all of CF. If you wish to edit any of the reference pages be sure to edit the appropriate file in CF's actual source code and regenerate the docs by running the CF sample [docs_parser](https://github.com/RandyGaul/cute_framework/blob/master/samples/docs_parser.cpp). +Please note that the reference pages for functions/structs are automatically generated by CF's [docs parser + generator](https://github.com/RandyGaul/cute_framework/blob/master/tools/docs_parser.c). You can run this executable to regenerate all of the docs files for all of CF. If you wish to edit any of the reference pages be sure to edit the appropriate file in CF's actual source code and regenerate the docs by running [docs_parser](https://github.com/RandyGaul/cute_framework/blob/master/tools/docs_parser.c). -CF uses [MkDocs](https://www.mkdocs.org/) with the [Material theme](https://squidfunk.github.io/mkdocs-material/) to generate the website, so you can run `mkdocs serve` to preview the docs locally. +CF uses [Zensical](https://zensical.org/) to generate the website, so you can run `zensical serve` to preview the docs locally. -MkDocs requirements: +Zensical setup: ``` -pip install mkdocs mkdocs-material "mkdocs-material[imaging]" markdown-callouts +python -m venv .venv +source .venv/bin/activate +pip install -r docs/requirements.txt +zensical serve ```

diff --git a/cmake/StandardProjectSettings.cmake b/cmake/StandardProjectSettings.cmake new file mode 100644 index 000000000..1ad4f275f --- /dev/null +++ b/cmake/StandardProjectSettings.cmake @@ -0,0 +1,20 @@ +# Set a default build type if none was specified +if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'Debug' as none was specified.") + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) + + # Set the possible values of build type for cmake-gui, ccmake + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo") +endif () + +find_program(CCACHE ccache) +if (CCACHE) + message(STATUS "Using ccache") + set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE}) + set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE}) +else () + message(STATUS "Ccache not found") +endif () + +# Generate compile_commands.json to make it easier to work with clang based tools +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) diff --git a/docs/javascripts/redirect.js b/docs/javascripts/redirect.js new file mode 100644 index 000000000..e335f0c8b --- /dev/null +++ b/docs/javascripts/redirect.js @@ -0,0 +1,70 @@ +// Redirect handler for old docsify-style URLs to new MkDocs URLs +// Old format: /#/topics/game_loop_and_time?id=fixed-timestep +// New format: /topics/game_loop_and_time/#fixed-timestep +// Old format: /#/coroutine +// New format: /api_reference/#coroutine + +(function() { + 'use strict'; + + var hash = window.location.hash; + + // Only process if we have a hash starting with #/ + if (!hash || !hash.startsWith('#/')) { + return; + } + + // Remove the leading #/ + var path = hash.substring(2); + + // Parse out any ?id= anchor + var anchor = ''; + var idMatch = path.match(/\?id=([^&]+)/); + if (idMatch) { + anchor = '#' + idMatch[1]; + path = path.replace(/\?id=[^&]+/, ''); + } + + // Remove any trailing ? or & left over + path = path.replace(/[?&]$/, ''); + + // Get the base URL (everything before the hash) + var baseUrl = window.location.origin + window.location.pathname; + // Remove trailing index.html if present + baseUrl = baseUrl.replace(/index\.html$/, ''); + // Ensure it ends with / + if (!baseUrl.endsWith('/')) { + baseUrl += '/'; + } + + var newUrl; + + // Map old paths to new paths + if (path.startsWith('topics/')) { + // Topics stay in /topics/ but use path-based routing + // Old: #/topics/game_loop_and_time?id=fixed-timestep + // New: /topics/game_loop_and_time/#fixed-timestep + newUrl = baseUrl + path + '/' + anchor; + } else { + // API items go to /api_reference/#item + // Old: #/coroutine or #/app/cf_make_app + // New: /api_reference/#coroutine or /api_reference/#cf_make_app + + // Extract just the item name (last segment) for the anchor + var segments = path.split('/'); + var itemName = segments[segments.length - 1]; + + // If there's already an anchor from ?id=, use that, otherwise use item name + if (anchor) { + newUrl = baseUrl + 'api_reference/' + anchor; + } else { + newUrl = baseUrl + 'api_reference/#' + itemName; + } + } + + // Clean up any double slashes (except in protocol) + newUrl = newUrl.replace(/([^:])\/+/g, '$1/'); + + // Redirect to the new URL + window.location.replace(newUrl); +})(); diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 000000000..2eafd8493 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +zensical +markdown-callouts diff --git a/docs/samples.md b/docs/samples.md deleted file mode 100644 index d1e2d8b65..000000000 --- a/docs/samples.md +++ /dev/null @@ -1 +0,0 @@ -The [Cute Framework](https://github.com/RandyGaul/cute_framework/tree/master/samples) samples are hosted on GitHub alongside the source code. Some of the samples using Cute Framework's C API, while some use the C++ API. Make sure to note if you're looking at one or the other! The main difference between each API is in C there are no namespaces, so most names are prepended with `cf_`. diff --git a/docs/samples/index.md b/docs/samples/index.md new file mode 100644 index 000000000..13faad4c5 --- /dev/null +++ b/docs/samples/index.md @@ -0,0 +1,30 @@ +# Samples + +Interactive samples demonstrating Cute Framework features. Click any card to play the sample in your browser. + +
+ +- **[spaceshooter](spaceshooter.md)** + + --- + + Interactive sample demonstrating spaceshooter features. + + [:octicons-arrow-right-24: Play](spaceshooter.md) + + +- **[waves](waves.md)** + + --- + + Interactive sample demonstrating waves features. + + [:octicons-arrow-right-24: Play](waves.md) + +
+ +!!! note "Source Code" + All sample source code is available in the [samples directory](https://github.com/RandyGaul/cute_framework/tree/master/samples) on GitHub. + +!!! tip "Build Locally" + To build and run samples locally, see the [Getting Started](../getting_started.md) guide and [Web Builds with Emscripten](../topics/emscripten.md) topic. diff --git a/docs/samples/spaceshooter.md b/docs/samples/spaceshooter.md new file mode 100644 index 000000000..56196bf88 --- /dev/null +++ b/docs/samples/spaceshooter.md @@ -0,0 +1,15 @@ +--- +hide: + - toc +--- + +# spaceshooter + +
+ +
+ +
+ [:material-fullscreen: Fullscreen](../play/spaceshooter.html){: .md-button target="_blank" } + [:material-code-tags: View Source](https://github.com/RandyGaul/cute_framework/blob/master/samples/spaceshooter.cpp){: .md-button target="_blank" } +
diff --git a/docs/samples/waves.md b/docs/samples/waves.md new file mode 100644 index 000000000..41d5b58f2 --- /dev/null +++ b/docs/samples/waves.md @@ -0,0 +1,15 @@ +--- +hide: + - toc +--- + +# waves + +
+ +
+ +
+ [:material-fullscreen: Fullscreen](../play/waves.html){: .md-button target="_blank" } + [:material-code-tags: View Source](https://github.com/RandyGaul/cute_framework/blob/master/samples/waves.cpp){: .md-button target="_blank" } +
diff --git a/docs/stylesheets/cute.css b/docs/stylesheets/cute.css index 9f37ee113..511d3063b 100644 --- a/docs/stylesheets/cute.css +++ b/docs/stylesheets/cute.css @@ -89,3 +89,48 @@ [data-md-color-scheme="slate"] { --md-hue: var(--cute-primary-hue); } + +/* Sample iframe container */ +.sample-container { + margin: 2em 0; + border-radius: 0.5rem; + overflow: hidden; + background: #000; + box-shadow: 0 0.2rem 0.5rem rgba(0, 0, 0, 0.2); + width: 100%; + max-width: 1024px; + aspect-ratio: 4 / 3; +} + +.sample-iframe { + width: 100%; + height: 768px; + min-height: 768px; + border: none; + display: block; +} + +.sample-controls { + display: flex; + gap: 1rem; + margin: 1rem 0 2rem 0; + flex-wrap: wrap; +} + +/* Responsive sizing */ +@media screen and (max-width: 1024px) { + .sample-container { + min-width: 100%; + } + .sample-iframe { + height: 600px; + min-height: 600px; + } +} + +@media screen and (max-width: 768px) { + .sample-iframe { + height: 480px; + min-height: 480px; + } +} diff --git a/docs/topics/android.md b/docs/topics/android.md new file mode 100644 index 000000000..f11157279 --- /dev/null +++ b/docs/topics/android.md @@ -0,0 +1,189 @@ +# Android Builds + +Android is a Java-based platform. Apps are packaged as APK files containing Java bytecode, resources, and metadata. Native C/C++ code cannot run directly - it must be compiled into a shared library (.so) and loaded by a Java app via JNI (Java Native Interface). + +Cute Framework is built on top of SDL3, which handles this complexity by providing a Java activity (`SDLActivity`) that initializes the Android environment, creates a window/surface, and loads your native code. Your game compiles to `libmain.so`, which SDL's Java code loads at startup. This is why Android builds require all this project structure - it's fundamentally a Java app that hosts your native code. + +SDL3 provides an `android-project` template with the necessary Java scaffolding. We use this template as our starting point, adding CF and your game code to it. The build process uses Gradle (Android's build system) to compile the Java wrapper, invoke CMake/NDK to cross-compile your C code for ARM, and package everything into an APK. + +> [!NOTE] +> Cute Framework supports Android 7.0 (API level 24) and newer. Vulkan is preferred with automatic fallback to OpenGL ES 3.0. + +## Prerequisites + +Install via Android Studio's SDK Manager (Tools > SDK Manager > SDK Tools): +- Android SDK (API 24+) +- NDK (Side by side) +- CMake 3.31+ (the default 3.22 has bugs with FetchContent) +- Build-Tools + +To install CMake 3.31+ from command line: +``` +sdkmanager "cmake;3.31.6" +``` + +Environment variables JAVA_HOME and ANDROID_HOME must be set for Gradle to work. Android Studio typically sets these, but if building from command line you may need to set them manually. + +## Project Structure + +The android project is based on SDL3's `android-project` template with CF and your game added: + +``` +android/ +├── build.gradle <- top-level gradle config +├── settings.gradle <- project settings +├── gradle.properties <- gradle properties +├── gradlew <- gradle wrapper (linux/mac) +├── gradlew.bat <- gradle wrapper (windows) +├── gradle/ +│ └── wrapper/ <- gradle wrapper JAR +├── app/ +│ ├── build.gradle <- app build config (edit cmake version here) +│ ├── proguard-rules.pro <- proguard rules (required) +│ ├── jni/ +│ │ ├── CMakeLists.txt <- entry point, just calls add_subdirectory(src) +│ │ ├── cute_framework/ <- COPY of cute_framework (exclude build folders) +│ │ │ ├── CMakeLists.txt +│ │ │ ├── include/ +│ │ │ ├── src/ +│ │ │ └── libraries/ +│ │ └── src/ +│ │ ├── CMakeLists.txt <- your game's cmake config +│ │ ├── main.c <- COPY of your game source +│ │ └── *.c, *.h <- COPY of other source files +│ └── src/main/ +│ ├── AndroidManifest.xml <- app manifest +│ ├── assets/ <- COPY of your game assets +│ ├── java/ <- SDL Java wrapper (from template) +│ │ └── org/libsdl/app/ +│ │ ├── SDLActivity.java +│ │ └── ... +│ └── res/ <- android resources +│ ├── mipmap-*/ <- app icons +│ └── values/ <- strings.xml, styles.xml +``` + +Paths marked COPY are synced from your main project before each build. The library in `jni/src/` must be named `main` for SDL's Java activity to load it. + +## Source Control + +The project contains scaffolding (version control) and copied source/assets (ignore). + +**Check in:** +- `build.gradle`, `settings.gradle`, `gradle.properties` +- `gradlew`, `gradlew.bat`, `gradle/` +- `app/build.gradle`, `app/proguard-rules.pro` +- `app/jni/CMakeLists.txt` +- `app/jni/src/CMakeLists.txt` +- `app/src/main/AndroidManifest.xml` +- `app/src/main/java/` +- `app/src/main/res/` + +**Add to `.gitignore`:** + +``` +# Build artifacts +android/.gradle/ +android/build/ +android/app/.cxx/ +android/app/build/ + +# Copied before build - sync from main project +android/app/jni/cute_framework/ +android/app/jni/src/*.c +android/app/jni/src/*.h +android/app/src/main/assets/ +``` + +Create a build script that syncs these paths before running gradle. This keeps your repo lean and ensures Android always builds from current source. + +## Setup + +### 1. Locate SDL3's android-project template + +Run CMake once for your desktop build - this fetches SDL3 which contains the template: +``` +cmake -B build +``` + +Then find the template at: +``` +build/_deps/sdl3-src/android-project/ +``` +Copy this folder as your Android project base. + +### 2. Copy cute_framework + +Copy `cute_framework` into `app/jni/cute_framework/`. Exclude any `build*` folders - they're large and unnecessary. + +### 3. Copy your game source and assets + +- Copy source files to `app/jni/src/` +- Copy assets to `app/src/main/assets/` + +### 4. Edit app/jni/CMakeLists.txt + +Replace contents with: +```cmake +cmake_minimum_required(VERSION 3.22) +project(GAME) +add_subdirectory(src) +``` + +### 5. Edit app/jni/src/CMakeLists.txt + +Replace contents with: +```cmake +cmake_minimum_required(VERSION 3.22) +project(mygame) + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../cute_framework cf_build) + +add_library(main SHARED + main.c +) + +target_include_directories(main PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(main PRIVATE cute) +``` +Add all your source files to the `add_library` call. The library must be named `main` to work with SDL's Android activity. + +### 6. Edit app/build.gradle + +Find the `externalNativeBuild` > `cmake` block and add the version: +```gradle +cmake { + path 'jni/CMakeLists.txt' + version '3.31.6' +} +``` + +### 7. Edit app name + +Change the app name in `app/src/main/res/values/strings.xml`. + +## Building + +On Linux/Mac, make gradlew executable first: `chmod +x gradlew` + +``` +./gradlew assembleDebug -PBUILD_WITH_CMAKE=1 +``` + +APK output: `app/build/outputs/apk/debug/app-debug.apk` + +## Installing + +``` +adb install app/build/outputs/apk/debug/app-debug.apk +``` + +## Assets + +Files in `app/src/main/assets/` are accessible via CF's virtual filesystem (e.g. `/textures/player.ase`). + +## Common Issues + +**FetchContent/URL errors**: Install CMake 3.31+ via SDK Manager. The bundled 3.22 has bugs that breaks FetchContent. + +**Crash on startup**: Check `adb logcat`. Usually missing assets or library not named `main`. \ No newline at end of file diff --git a/docs/topics/camera.md b/docs/topics/camera.md index afebafd1e..790f673ef 100644 --- a/docs/topics/camera.md +++ b/docs/topics/camera.md @@ -2,9 +2,63 @@ Everything in the [Drawing API](../topics/drawing.md) is drawn relative to the current coordinate system. We can transform this coordinate system to adjust the position, scale, and rotation of anything drawn. -## Default System +## Coordinate Systems -In games typically the center of the screen is defined as position `(0, 0)`, with the positive y-axis going up the screen. This is the default setup in CF. +CF uses two different coordinate systems: **world space** and **screen space**. + +### World Space (Drawing) + +This is the coordinate system used by all drawing functions. + +- **Origin**: Center of the screen `(0, 0)` +- **X-axis**: Positive values go **right** +- **Y-axis**: Positive values go **up** + +``` + +Y + ^ + | + | + -X <-------+-------> +X + | + | + v + -Y +``` + +### Screen Space (Input) + +This is the coordinate system used by mouse and touch input functions like [`cf_mouse_x`](../input/cf_mouse_x.md) and [`cf_mouse_y`](../input/cf_mouse_y.md). + +- **Origin**: Top-left corner of the screen `(0, 0)` +- **X-axis**: Positive values go **right** +- **Y-axis**: Positive values go **down** + +``` + (0,0)+--------------> +X + | + | + | + | + v + +Y +``` + +### Converting Between Coordinate Systems + +To convert mouse coordinates to world space for game logic, use [`cf_screen_to_world`](../draw/cf_screen_to_world.md): + +```cpp +CF_V2 mouse_world = cf_screen_to_world(cf_v2((float)cf_mouse_x(), (float)cf_mouse_y())); +``` + +To convert world coordinates to screen space, use [`cf_world_to_screen`](../draw/cf_world_to_screen.md): + +```cpp +CF_V2 screen_pos = cf_world_to_screen(world_position); +``` + +You can also get the visible screen bounds in world space with [`cf_screen_bounds_to_world`](../draw/cf_screen_bounds_to_world.md). ## Translating diff --git a/docs/topics/coroutines.md b/docs/topics/coroutines.md index 102cd06cb..7df805cd6 100644 --- a/docs/topics/coroutines.md +++ b/docs/topics/coroutines.md @@ -223,7 +223,7 @@ Coroutines are admittedly weird. Thinking of how to implement things with a coro 1. [Unity/C# with coroutines](https://docs.unity3d.com/Manual/Coroutines.html). 2. [Coroutines from the Lua programming language](https://www.lua.org/pil/9.1.html). -Elias Daler wrote a fantastic blog post on using [coroutines to implement cutscenes](https://eliasdaler.github.io/how-to-implement-action-sequences-and-cutscenes/). Check it out! He has some absolutely beautiful gifs. +Elias Daler wrote a fantastic blog post on using [coroutines to implement cutscenes](https://edw.is/how-to-implement-action-sequences-and-cutscenes/). Check it out! He has some absolutely beautiful gifs. Small functions can be reused many times in different coroutines to perform common tasks, such as waiting for a number of seconds, walking along a path, or playing a sequence of animations. diff --git a/docs/topics/custom_sprites.md b/docs/topics/custom_sprites.md new file mode 100644 index 000000000..4e92055e5 --- /dev/null +++ b/docs/topics/custom_sprites.md @@ -0,0 +1,146 @@ +# Custom Sprites + +The [Custom Sprite API](https://github.com/RandyGaul/cute_framework/blob/master/include/cute_custom_sprite.h) lets you build sprites from individual .png files with your own frame timings. This is useful when you have your own animation format or art pipeline and don't want to use .ase/.aseprite files. + +If you just want to load sprites from Aseprite files, see [`cf_make_sprite`](../sprite/cf_make_sprite.md) instead. + +## Overview + +Building a custom sprite is a three-step process: + +1. **Load PNGs** -- Load individual .png images into [`CF_Png`](../custom_sprite/cf_png.md) structs. +2. **Create Animations** -- Group PNGs into named [`CF_Animation`](../custom_sprite/cf_make_custom_sprite_animation.md) sequences with per-frame delays. +3. **Build the Sprite** -- Combine animations into an animation table and create a [`CF_Sprite`](../sprite/cf_sprite.md). + +## Step 1: Load PNGs + +Each frame of your animation is a separate .png file. Load them with [`cf_custom_sprite_load_png`](../custom_sprite/cf_custom_sprite_load_png.md): + +```c +CF_Png frames[3]; +cf_custom_sprite_load_png("/frame1.png", &frames[0]); +cf_custom_sprite_load_png("/frame2.png", &frames[1]); +cf_custom_sprite_load_png("/frame3.png", &frames[2]); +``` + +All frames within an animation must have the same dimensions. An assert will fire if they don't match. + +Images are cached internally, so loading the same path twice is fast. + +## Step 2: Create Animations + +Group your PNGs into a named animation with per-frame delays (in seconds) using [`cf_make_custom_sprite_animation`](../custom_sprite/cf_make_custom_sprite_animation.md): + +```c +float delays[] = { 0.1f, 0.1f, 0.1f }; +const CF_Animation* walk = cf_make_custom_sprite_animation( + "walk", frames, 3, delays, 3); +``` + +You can create as many animations as you need (walk, idle, attack, etc.), each from their own set of PNGs. + +## Step 3: Build the Sprite + +Combine your animations into an animation table using [`cf_make_custom_sprite_animation_table`](../custom_sprite/cf_make_custom_sprite_animation_table.md), then create the sprite with [`cf_make_custom_sprite`](../custom_sprite/cf_make_custom_sprite.md): + +```c +const CF_Animation* anims[] = { walk, idle, attack }; +const CF_AnimationTable* table = cf_make_custom_sprite_animation_table( + "player", anims, 3); +CF_Sprite sprite = cf_make_custom_sprite("player", table); +``` + +The resulting `CF_Sprite` works exactly like any other sprite in CF. You can play animations, draw it, and update it with the normal sprite functions: + +```c +cf_sprite_play(&sprite, "walk"); +cf_sprite_update(&sprite); +cf_draw_sprite(&sprite); +``` + +## Non-Looping Animations + +By default sprites loop their animation. Set `sprite.loop = false` for one-shot animations. When a non-looping animation reaches its last frame, the sprite's `finished` flag is set to `true`. This is useful for things like explosions or hit effects: + +```c +cf_sprite_play(&sprite, "explosion"); +sprite.loop = false; + +// Later, in your update loop: +cf_sprite_update(&sprite); +if (sprite.finished) { + // Animation is done, clean up. +} +``` + +Calling `cf_sprite_play` resets the `finished` flag. + +## Full Example + +Here is a complete example that loads two animations (bullet_pop and explosion) and spawns them randomly on screen. See the `customsprite` sample for the runnable version. + +```c +#include + +int main(int argc, char* argv[]) +{ + cf_make_app("Custom Sprite", 0, 0, 0, 640, 480, + CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT, argv[0]); + + // Load PNG frames. + CF_Png bullet_pngs[3]; + cf_custom_sprite_load_png("/bullet_pop1.png", &bullet_pngs[0]); + cf_custom_sprite_load_png("/bullet_pop2.png", &bullet_pngs[1]); + cf_custom_sprite_load_png("/bullet_pop3.png", &bullet_pngs[2]); + + CF_Png explosion_pngs[5]; + for (int i = 0; i < 5; i++) { + char path[32]; + snprintf(path, sizeof(path), "/explosion%d.png", i + 1); + cf_custom_sprite_load_png(path, &explosion_pngs[i]); + } + + // Create animations with per-frame delays (seconds). + float bullet_delays[] = { 0.05f, 0.05f, 0.05f }; + float explosion_delays[] = { 0.05f, 0.05f, 0.05f, 0.05f, 0.05f }; + const CF_Animation* bullet_anim = cf_make_custom_sprite_animation( + "bullet_pop", bullet_pngs, 3, bullet_delays, 3); + const CF_Animation* explosion_anim = cf_make_custom_sprite_animation( + "explosion", explosion_pngs, 5, explosion_delays, 5); + + // Build an animation table and create the sprite. + const CF_Animation* anims[] = { bullet_anim, explosion_anim }; + const CF_AnimationTable* table = cf_make_custom_sprite_animation_table( + "firework", anims, 2); + CF_Sprite sprite = cf_make_custom_sprite("firework", table); + + // Play a one-shot animation. + cf_sprite_play(&sprite, "explosion"); + sprite.loop = false; + + while (cf_app_is_running()) { + cf_app_update(NULL); + cf_sprite_update(&sprite); + + if (!sprite.finished) { + cf_draw_sprite(&sprite); + } + + cf_app_draw_onto_screen(true); + } + + cf_destroy_app(); + return 0; +} +``` + +## API Reference + +- [`CF_Png`](../custom_sprite/cf_png.md) -- A loaded PNG image. +- [`cf_png_defaults`](../custom_sprite/cf_png_defaults.md) -- Initialize an empty `CF_Png`. +- [`cf_custom_sprite_load_png`](../custom_sprite/cf_custom_sprite_load_png.md) -- Load a PNG from disk. +- [`cf_custom_sprite_load_png_from_memory`](../custom_sprite/cf_custom_sprite_load_png_from_memory.md) -- Load a PNG from memory. +- [`cf_custom_sprite_unload_png`](../custom_sprite/cf_custom_sprite_unload_png.md) -- Unload a PNG. +- [`cf_make_custom_sprite_animation`](../custom_sprite/cf_make_custom_sprite_animation.md) -- Create a named animation from PNGs. +- [`cf_make_custom_sprite_animation_table`](../custom_sprite/cf_make_custom_sprite_animation_table.md) -- Combine animations into a table. +- [`cf_make_custom_sprite`](../custom_sprite/cf_make_custom_sprite.md) -- Create a sprite from an animation table. diff --git a/docs/topics/data_structures.md b/docs/topics/data_structures.md index c66f84b12..838de274d 100644 --- a/docs/topics/data_structures.md +++ b/docs/topics/data_structures.md @@ -2,10 +2,13 @@ Data structures are used as work-horse tools to implement all kinds of features or tools in games. The data structures available include: -- [Hashtable/Map](../api_reference.md#hash) +- [Map](../api_reference.md#map) - [Array](../api_reference.md#array) - [Linked List](../api_reference.md#list) +> [!NOTE] +> CF provides shortform convenience macros for common operations (e.g. `apush`, `afree`, `map_set`, `map_get`). These are simple wrappers around the longform `cf_array_*` and `map_*` functions. The shortform macros are not individually documented but work identically to their longform counterparts. + ## Array in C Arrays operate on typed pointers in C, and automatically grow when necessary, making them _dynamic_ arrays. If you're familiar with [stretchy buffers](https://github.com/creikey/stretchy-buff), this is exactly that. @@ -15,16 +18,16 @@ Arrays operate on typed pointers in C, and automatically grow when necessary, ma ```cpp dyna int* a = NULL; apush(a, 5); -CF_ASSERT(alen(a) == 1); -alen(a)--; -CF_ASSERT(alen(a) == 0); +CF_ASSERT(asize(a) == 1); +asetlen(a, 0); +CF_ASSERT(asize(a) == 0); afree(a); ``` > [!NOTE] -> The [`dyna`](../array/dyna.md) keyword is an _optional_, but encouraged, macro that doesn't actuall do anything. It's used to markup the type and make it clear this is a dynamic array, and not just an `int*`. +> The [`dyna`](../array/dyna.md) keyword is an _optional_, but encouraged, macro that doesn't actually do anything. It's used to markup the type and make it clear this is a dynamic array, and not just an `int*`. -The array will automatically grow as elements are pushed. Whenever you want to fetch a particular element just use `a[i]` like any other pointer. When done, free up the array with [`afree`](../array/afree.md). +The array will automatically grow as elements are pushed. Whenever you want to fetch a particular element just use `a[i]` like any other pointer. When done, free up the array with [`cf_array_free`](../array/cf_array_free.md). You may store any kind of element you wish, including structs. However, since the array will grow as necessary elements cannot store pointers to themselves or any other element. Pointers into the array are _not stable_. A good workaround is to instead store indices into the array, and fetch pointers to elements only temporarily. @@ -44,65 +47,63 @@ for (int i = 0; i < a.count(); ++i) { Since the C++ wrapper has a constructor and destructor there's no need to manually call any free function (unlike the C api) -- the destructor cleans up the array's memory resources whenever it gets called. -## Hash Table/Map +## Map -A hash table is used to map a unique key to a specific value. The value can be fetched later very efficiently (in constant time). This makes the hash table a very popular data structure for general purpose problem solving. Often times hash tables are used to store unique identifiers for game objects, assets, and provide an easy way to create associations between different sets of data. +A map is used to map a unique key to a specific value. The value can be fetched later very efficiently (in constant time). This makes the map a very popular data structure for general purpose problem solving. Often times maps are used to store unique identifiers for game objects, assets, and provide an easy way to create associations between different sets of data. -In C we use the [Hash API](../api_reference.md#hash), while in C++ there's a `Map` class that wraps the C functionality. It contains a very similar API including get/find, insert, remove, etc. We will cover both the C and C++ APIs in this page. +In C we use the [`CF_MAP`](../map/cf_map.md) type, while in C++ there's a `Map` class that wraps the C functionality. It contains a very similar API including get/find, insert, remove, etc. We will cover both the C and C++ APIs in this page. > [!IMPORTANT] > Since the table itself grows dynamically, values _may not_ store pointers to themselves or other values. All values are stored as [plain old data (POD)](https://stackoverflow.com/questions/146452/what-are-pod-types-in-c), as their location in memory will get shuffled around internally as the map grows. -## htbl in C +## CF_MAP in C -The [`htbl`](../hash/htbl.md) (stands for hashtable) works on a typed pointer, and automatically grows to fit new key/value pairs as necessary. Internally it's implemented with a [stretchy buffer](https://github.com/creikey/stretchy-buff), just like the [Array API in C](../topics/data_structures.md?id=array). It can store values with unique 64-bit keys. +[`CF_MAP(T)`](../map/cf_map.md) is a markup macro for a map type. All keys are `uint64_t`. You can store any POD value type. The map grows automatically as entries are added. ```cpp -htbl CF_V2* pts = NULL; -hset(pts, 0, cf_v2(3, 5)); -hset(pts, 10, cf_v2(-1, -1); -hset(pts, -2, cf_v2(0, 0)); +CF_MAP(CF_V2) pts = NULL; +map_set(pts, 0, cf_v2(3, 5)); +map_set(pts, 10, cf_v2(-1, -1)); +map_set(pts, -2, cf_v2(0, 0)); -// Use `hget` to fetch values. -CF_V2 a = hget(pts, 0); -CF_V2 b = hget(pts, 10); -CF_V2 c = hget(pts, -2); +// Use `map_get` to fetch values. +CF_V2 a = map_get(pts, 0); +CF_V2 b = map_get(pts, 10); +CF_V2 c = map_get(pts, -2); // Loop over {key, item} pairs like so: -const uint64_t* keys = hkeys(pts); -for (int i = 0; i < hcount(pts); ++i) { +uint64_t* keys = map_keys(pts); +for (int i = 0; i < map_size(pts); ++i) { uint64_t key = keys[i]; CF_V2 v = pts[i]; // ... } -hfree(pts); +map_free(pts); ``` -All keys for [`htbl`](../hash/htbl.md) are typecasted to a `uint64_t`. You can use pointers, integers, chars, etc. as keys. Arbitrary values can be passed to the table, including return results from function calls or hard-coded literals (like `10` or `"Strings!"`). +All keys for `CF_MAP` are `uint64_t`. You can use pointers, integers, chars, etc. as keys. Use `map_set` to insert, `map_get` to fetch, `map_del` to remove, and `map_free` to clean up. ### Strings as Keys -Since the [`htbl`](../hash/htbl.md) typecasts all keys to `uint64_t` internally we cannot use strings as keys, right? Good question! Actually there's a _highly recommended_ technique to deal with strings as keys. The [Strings](../topics/strings.md) page has all the string related details. We can make use of the _string interning_ functions to create stable, unique string references. Here is the list of intern functions: +Since `CK_MAP` uses `uint64_t` keys internally we cannot use strings as keys directly. However there's a _highly recommended_ technique using _string interning_ to create stable, unique string references. The [Strings](../topics/strings.md) page has all the string related details. Here is the list of intern functions: -* [`sintern`](../string/sintern.md) -* [`sintern_range`](../string/sintern_range.md) -* [`silen`](../string/silen.md) -* [`sivalid`](../string/sivalid.md) -* [`sinuke`](../string/sinuke.md) +* [`cf_sintern`](../string/cf_sintern.md) +* [`cf_sintern_range`](../string/cf_sintern_range.md) +* [`cf_sinuke`](../string/cf_sinuke.md) -[`sintern`](../string/sintern.md) is the important one. Given a string it will return you a pointer to an identical string, but with a stable and unique pointer. The pointer is unique based on the contents of the string, ensuring only one copy of any string exists. The pointer will be completely immutable, and valid until [`sinuke`](../string/sinuke.md) is called, which cleans up all memory used by the string interning API up to that point. +[`cf_sintern`](../string/cf_sintern.md) is the important one. Given a string it will return you a pointer to an identical string, but with a stable and unique pointer. The pointer is unique based on the contents of the string, ensuring only one copy of any string exists. The pointer will be completely immutable, and valid until [`cf_sinuke`](../string/cf_sinuke.md) is called, which cleans up all memory used by the string interning API up to that point. -By interning a string the stable + unique pointer can be used as a globally unique identifier for the string contents itself. We can then insert the pointer to the string into hashtables and use it as a valid lookup key. The pattern is to take any dynamic string, pass it to [`sintern`](../string/sintern.md), then pass the stable pointer around from there on (but remember, it's contents are immutable!). +By interning a string the stable + unique pointer can be used as a globally unique identifier for the string contents itself. We can then cast the pointer to `uint64_t` and use it as a valid map key. The pattern is to take any dynamic string, pass it to [`cf_sintern`](../string/cf_sintern.md), then pass the stable pointer around from there on (but remember, its contents are immutable!). ```cpp -const char* special_name = sintern("Something Special"); +const char* special_name = cf_sintern("Something Special"); const char* name = GetName(); -name = sintern(name); +name = cf_sintern(name); if (name == special_name) { // Valid to compare pointers directly! - Data* data = hget(table, name); // Valid to use as key lookup! Internally compares pointers directly. - DoStuff(data); + CF_V2 data = map_get(table, (uint64_t)name, CF_V2); // Cast interned pointer to key. + DoStuff(&data); } ``` @@ -115,16 +116,16 @@ There are of course some downsides. - Intern'd strings are 100% immutable -- you cannot change their contents after creation. - It's important not to pollute the intern table with many different little strings inside of a loop. For example, when iterating over numbers or counters. -- Since an intern'd string looks just like a normal string, it can be difficult to remember which is which. You can still use [`sivalid`](../string/sivalid.md), but this is just a sanity check meant only for debugging. +- Since an intern'd string looks just like a normal string, it can be difficult to remember which is which. ## Map in C++ -In C++ we have access to `Map`, a wrapper class for the [C htbl API](../topics/data_structures.md?id=hash-tablemap). It contains many similar functions as the C api, including add/insert, get/try_get, keys/vals. +In C++ we have access to `Map`, a wrapper class for the C map API. It contains many similar functions, including insert, get/try_get, keys/items, remove, and count. ```cpp Map vecs; -vecs.add(0, V2(0,0)); -vecs.add(1, V2(10,0)); +vecs.insert(0, V2(0,0)); +vecs.insert(1, V2(10,0)); v2 a = vecs.get(0); v2 b = vecs.get(1); diff --git a/docs/topics/drawing.md b/docs/topics/drawing.md index 80c8f7eed..57c2fceb0 100644 --- a/docs/topics/drawing.md +++ b/docs/topics/drawing.md @@ -38,7 +38,7 @@ int main(int argc, char* argv[]) } draw_push_color(make_color(0xeba48bff)); - draw_push_antialias(true); + draw_push_shape_aa(1.5f); float t = 0; while (app_is_running()) { @@ -72,8 +72,7 @@ int main(int argc, char* argv[]) The draw API has some settings that can be pushed and popped. Pushing and popping settings is a great way to customize how to draw without affecting the settings of the rest of your code. Here are some of the customizeable settings: - color -- antialias on/off -- antialias scale +- shape antialias (0 = off, non-zero = on at that scale, default 1.5) - layer - chubbiness - shader @@ -87,13 +86,14 @@ The layer controls the order things are drawn. You can set what layer to draw up ## Drawing Sprites -Sprites can be loaded with either .ase/.aseprite files or .png files. The recommended method is .ase files called [Aseprite](https://www.aseprite.org/) files. An aseprite file contains all the animation and image data necessary for a 2D frame based animations. If instead you want to support your own custom animation format, or any other format, you can load up your animation data and feed it into the [.png API](https://github.com/RandyGaul/cute_framework/blob/master/include/cute_png_cache.h). +Sprites can be loaded with either .ase/.aseprite files or .png files. The recommended method is .ase files called [Aseprite](https://www.aseprite.org/) files. An aseprite file contains all the animation and image data necessary for a 2D frame based animations. If instead you want to support your own custom animation format, or any other format, you can build sprites from individual .png files using the [Custom Sprites](custom_sprites.md) API. Some particular pages of interest are: - [Sprite API Reference](../api_reference.md#sprite) - [CF_Sprite](../sprite/cf_sprite.md) - [cf_sprite_play](../sprite/cf_sprite_play.md) +- [Custom Sprites](custom_sprites.md) CF comes with a convenience function called [`cf_make_demo_sprite`](../sprite/cf_make_demo_sprite.md). This sprite contains a small pixel art girl with a couple built-in animations. Here's a program to load her up and draw her on screen: @@ -179,27 +179,36 @@ You can apply customizable shaders that work with the draw API by using function The draw API passes *all* geometry into an optional shader function, within the fragment shader, called `shader`. This function is the final step in the entire fragment shader, granting the opportunity to alter the final output pixel color. Let us look at the custom shader skeleton/stub (does no-op). ```glsl -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { - return vec4(mix(color.rgb, params.rgb, params.a), color.a); + return vec4(mix(color.rgb, params.attributes.rgb, params.attributes.a), color.a); } ``` The `color` param is the color that would be rendered if you don't make any modifications to it within the `shader` function. If drawing a sprite this would be the color of a particular pixel from the sprite's texture, or, if drawing a shape the color of the shape itself. -`pos` was the rendering position used when calling an associated draw function like `cf_draw_sprite`. +The `params` argument is a `ShaderParams` struct with the following fields: -For pixel art games it's important to sample using the function `smooth_uv`, something like so: `smooth_uv(v_uv, u_texture_size)` to generate a uv coordinate that will scale pixel art correctly. +- `view_pos` (vec2) - world position from the draw call +- `uv` (vec2) - texture UV coordinate +- `uv_min` (vec2) - UV bounds minimum of the sprite/glyph in the atlas (zero for shapes) +- `uv_max` (vec2) - UV bounds maximum of the sprite/glyph in the atlas (zero for shapes) +- `screen_uv` (vec2) - screen-space UV where (0,0) is top-left, (1,1) is bottom-right +- `attributes` (vec4) - four custom floats from [cf_draw_push_vertex_attributes](../draw/cf_draw_push_vertex_attributes.md) -`screen_uv` is a position relative to the screen, where (0,0) is the top-left, and (1,1) is the bottom-right of the screen. +The `params.uv_min` and `params.uv_max` fields provide atlas UV bounds for sprite sub-regions, which can be useful for effects that need to know the boundaries of the current sprite within the texture atlas. CF internally pads every sprite with a 1-pixel transparent border in the atlas, so clamping modified UVs to these bounds works well as a clamp-to-edge strategy without bleeding from neighboring sprites: -`params` are four optional floats. They come from vertex attributes set by [cf_draw_push_vertex_attributes](../draw/cf_draw_push_vertex_attributes.md). Each different item drawn through CF's draw API will attach the previously pushed attributes onto their vertices. These four floats are general-purpose, and only used to pass into the `shader` function. Use them to pack information to implement your own custom visual FX, such as color tinting, or anything else on a per-object basis. +```glsl +uv = clamp(uv, params.uv_min, params.uv_max); +``` + +For pixel art games it's important to sample using the function `smooth_uv`, something like so: `smooth_uv(v_uv, u_texture_size)` to generate a uv coordinate that will scale pixel art correctly. You may also add in uniforms and textures as-needed. The draw API has some functions for setting uniforms and textures via [cf_draw_set_uniform](../draw/cf_draw_set_uniform.md) and [cf_draw_set_texture](../draw/cf_draw_set_texture.md). These will get auto-magically hooked up and send values to your shader. When you add in your own uniforms just be sure to place them inside of a uniform block like in the below sample (see `shd_uniforms`, and don't change this name either! It must be called `shd_uniforms`). Shaders have access to some "hidden" environment variables. In particular you have access to: -- `v_uv` if you're drawing a canvas with [cf_draw_canvas](../draw/cf_draw_canvas.md) it's often very helpful to sample from the canvas. You may do this via: `texture(u_image, v_uv)`. +- `v_uv` a legacy alias for `params.uv`, containing the texture UV coordinate. Prefer using `params.uv` in new code. If you're drawing a canvas with [cf_draw_canvas](../draw/cf_draw_canvas.md) it's often very helpful to sample from the canvas. You may do this via: `texture(u_image, params.uv)`. - `u_image` if you're drawing a canvas with [cf_draw_canvas](../draw/cf_draw_canvas.md) can be quite useful if you need to, for any reason, sample the canvas. See the above point on `v_uv`. - `u_texture_size` if you're drawing a canvas with [cf_draw_canvas](../draw/cf_draw_canvas.md) is sometimes useful for certain algorithms that need to calcualte texel sizes, or know the size of the texture they are sampling from. @@ -229,9 +238,9 @@ vec4 normal_to_color(vec2 n) return vec4(n * 0.5 + 0.5, 1.0, 1.0); } -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { - vec2 uv = screen_uv; + vec2 uv = params.screen_uv; vec2 dim = vec2(1.0/160.0,1.0/120.0); vec2 n = normal_from_heightmap(noise_tex, uv); vec2 w = normal_from_heightmap(wavelets_tex, uv+n*dim*10.0); @@ -259,7 +268,7 @@ draw_set_texture("noise_tex", noise_tex); draw_set_texture("scene_tex", canvas_get_target(scene_canvas)); draw_set_uniform("show_noise", show_noise ? 1.0f : 0.0f); draw_set_uniform("show_normals", show_normals ? 1.0f : 0.0f); -draw_push_antialias(false); +draw_push_shape_aa(0); draw_box(V2(0,0), (float)W, (float)H); ``` diff --git a/docs/topics/index.md b/docs/topics/index.md index e23b06a30..1ce52a301 100644 --- a/docs/topics/index.md +++ b/docs/topics/index.md @@ -12,14 +12,17 @@ To learn about a specific area in Cute Framework visit one of these topic articl After completing the above fundamental reading it's recommended to then select topics of interest from the list below. The list is sorted alphabetically. * [Allocators](./allocator.md) +* [Android Builds](./android.md) * [Atomics](./atomics.md) * [Audio](./audio.md) * [Camera](./camera.md) * [Collision](./collision.md) * [Coroutines](./coroutines.md) +* [Custom Sprites](./custom_sprites.md) * [Data Structures](./data_structures.md) * [Web Builds with Emscripten](./emscripten.md) * [Input](./input.md) +* [Input Bindings](./input_bindings.md) * [MacOS + iOS Builds](./ios.md) * [Low Level Graphics](./low_level_graphics.md) * [Multithreading](./multithreading.md) diff --git a/docs/topics/input_bindings.md b/docs/topics/input_bindings.md new file mode 100644 index 000000000..d27e9ed1a --- /dev/null +++ b/docs/topics/input_bindings.md @@ -0,0 +1,108 @@ +# Input Bindings + +Input bindings provide an abstraction layer over raw keyboard, mouse, and joypad inputs. Instead of polling individual keys or buttons, you create a binding that maps multiple physical inputs to a single logical action. Bindings support input buffering for forgiving press/release detection. + +There are three binding types, each building on the previous: + +- **Button Binding** -- Maps any combination of keys, mouse buttons, joypad buttons, and analog triggers to a single on/off button. +- **Axis Binding** -- Combines two button bindings (negative/positive) into a -1..1 axis with conflict resolution. +- **Stick Binding** -- Combines two axis bindings (X/Y) into a 2D stick. + +## Button Bindings + +A [`CF_ButtonBinding`](../binding/cf_buttonbinding.md) aggregates multiple physical inputs into a single logical button. Any of the bound inputs being active will activate the binding. + +```c +// Create a "jump" button bound to spacebar, joypad A, and left trigger. +CF_ButtonBinding jump = cf_make_button_binding(0, 0.1f); +cf_button_binding_add_key(jump, CF_KEY_SPACE); +cf_button_binding_add_joypad_button(jump, CF_JOYPAD_BUTTON_A); +cf_button_binding_add_trigger(jump, CF_JOYPAD_AXIS_TRIGGERLEFT, 0.5f, true); + +// In your game loop: +if (cf_binding_pressed(jump)) { + // Jump was just pressed (with 0.1s buffer window). +} +if (cf_button_binding_down(jump)) { + // Jump is currently held down. +} + +// When done: +cf_destroy_binding(jump); +``` + +The `press_buffer` parameter to [`cf_make_button_binding`](../binding/cf_make_button_binding.md) controls how long a press/release event stays active, in seconds. This means that even if your gameplay code checks the binding a few frames late, the press still registers. Use [`cf_button_binding_consume_press`](../binding/cf_button_binding_consume_press.md) to consume a buffered event so it only triggers once. + +## Axis Bindings + +A [`CF_AxisBinding`](../binding/cf_axisbinding.md) combines a negative and positive button binding into a single axis that outputs values from -1 to 1. + +```c +// Create a horizontal movement axis. +CF_AxisBinding move_x = cf_make_axis_binding(0); +cf_axis_binding_add_keys(move_x, CF_KEY_A, CF_KEY_D); +cf_axis_binding_add_left_stick_x(move_x, 0.15f); +cf_axis_binding_add_dpad_x(move_x); + +float x = cf_binding_value(move_x); // -1 to 1 +float sign = cf_binding_sign(move_x); // -1, 0, or 1 +``` + +When both directions are pressed simultaneously, the conflict resolution mode determines the result. Set it with [`cf_axis_binding_set_conflict`](../binding/cf_axis_binding_set_conflict.md): + +- `CF_AXIS_CONFLICT_NEWEST` (default) -- Use the most recently pressed direction. +- `CF_AXIS_CONFLICT_OLDEST` -- Use the first pressed direction. +- `CF_AXIS_CONFLICT_CANCEL` -- Return 0. + +## Stick Bindings + +A [`CF_StickBinding`](../binding/cf_stickbinding.md) combines two axis bindings (X and Y) into a 2D stick that returns a `CF_V2`. + +```c +// Create a movement stick with WASD + left analog stick + d-pad. +CF_StickBinding move = cf_make_stick_binding(0); +cf_stick_binding_add_wasd(move); +cf_stick_binding_add_left_stick(move, 0.15f); +cf_stick_binding_add_dpad(move); + +CF_V2 dir = cf_binding_value(move); // Each component -1 to 1. +CF_V2 sign = cf_binding_sign(move); // Each component -1, 0, or 1. +``` + +Convenience functions [`cf_stick_binding_add_wasd`](../binding/cf_stick_binding_add_wasd.md), [`cf_stick_binding_add_arrow_keys`](../binding/cf_stick_binding_add_arrow_keys.md), and [`cf_stick_binding_add_dpad`](../binding/cf_stick_binding_add_dpad.md) bind common key/button groups in a single call. + +## Input Buffering + +Input buffering is a technique for forgiving input timing. When creating a button binding, you specify a `press_buffer` duration in seconds. During this window, [`cf_binding_pressed`](../binding/cf_binding_pressed.md) and [`cf_binding_released`](../binding/cf_binding_released.md) will continue to return true, even if the physical input has already changed. + +This is useful for platformers where the player presses jump slightly before landing. By buffering the press for 0.1 seconds, the jump will still register when the character touches the ground. + +To prevent a buffered press from firing multiple times, use [`cf_binding_consume_press`](../binding/cf_binding_consume_press.md). Once consumed, the press will not report again until a new physical press occurs. + +## Deadzone + +Analog stick inputs use a global deadzone threshold to filter out stick drift. By default this is 0.15. Values below the deadzone are zeroed out, and the remaining range is remapped to 0..1. + +Use [`cf_binding_get_deadzone`](../binding/cf_binding_get_deadzone.md) and [`cf_binding_set_deadzone`](../binding/cf_binding_set_deadzone.md) to read or adjust the threshold. + +## Joypad Connection Tracking + +Register a callback with [`cf_register_joypad_connect_callback`](../binding/cf_register_joypad_connect_callback.md) to be notified when joypads connect or disconnect. This is useful for showing controller UI prompts or adjusting player assignments. + +```c +void on_joypad(int player_index, bool connected, void* udata) { + if (connected) { + printf("Joypad %d connected\n", player_index); + } else { + printf("Joypad %d disconnected\n", player_index); + } +} + +cf_register_joypad_connect_callback(on_joypad, NULL); +``` + +## Generic Dispatch + +In C, the `cf_binding_pressed`, `cf_binding_released`, `cf_binding_value`, `cf_binding_sign`, `cf_binding_consume_press`, `cf_binding_consume_release`, and `cf_destroy_binding` macros use C11 `_Generic` to dispatch based on handle type. This lets you write uniform code regardless of whether you are working with a button, axis, or stick binding. + +In C++, equivalent overloaded functions are available in `namespace Cute`. diff --git a/docs/topics/low_level_graphics.md b/docs/topics/low_level_graphics.md index 4f62297af..27665e4ff 100644 --- a/docs/topics/low_level_graphics.md +++ b/docs/topics/low_level_graphics.md @@ -138,33 +138,7 @@ To compile shaders from bytecode (SPIRV) you must first compile them to a byteco Shader compilation is explained in more details [here](shader_compilation.md). -Be aware that shaders must adhere to strict rules for resource sets. Here's the notes from CF's source: - -```cpp -/** - * For _VERTEX_ shaders: - * 0: Sampled textures, followed by storage textures, followed by storage buffers - * 1: Uniform buffers - * For _FRAGMENT_ shaders: - * 2: Sampled textures, followed by storage textures, followed by storage buffers - * 3: Uniform buffers - * - * Example _VERTEX shader: - * layout (set = 0, binding = 0) uniform sampler2D u_image; - * - * layout (set = 1, binding = 0) uniform uniform_block { - * vec2 u_texture_size; - * }; - * - * Example _FRAGMENT_ shader: - * - * layout (set = 2, binding = 0) uniform sampler2D u_image; - * - * layout (set = 3, binding = 0) uniform uniform_block { - * vec2 u_texture_size; - * }; - */ -``` +Be aware that shaders must adhere to strict rules for resource sets. See [Resource Set Layout](shader_compilation.md#resource-set-layout) for the full reference. In short: vertex shaders use sets 0-1, fragment shaders use sets 2-3, and compute shaders use sets 0-2. For uniforms you only have one uniform block available, and it *must* be named `uniform_block`. However, if your shader is make from the draw api (`cf_make_draw_shader`) uniform blocks must be named `shd_uniforms`. @@ -249,6 +223,29 @@ In C++ we can set values for these uniforms by name using [`cf_material_set_unif You can set textures on a material as well via [`cf_material_set_texture_vs`](../graphics/cf_material_set_texture_vs.md) for the vertex shader, or [`cf_material_set_texture_fs`](../graphics/cf_material_set_texture_fs.md) for the fragment shader. More on textures in the next section. +## Compute Shaders + +Compute shaders run on the GPU outside the normal graphics pipeline. They are available on SDL_GPU backends (Vulkan, D3D12, Metal) but not on GLES3. For the GLSL resource set layout rules and barrier safety, see [Resource Set Layout](shader_compilation.md#resource-set-layout). + +### Binding Resources from C + +Resources are bound through two mechanisms: + +- **Material** (name-matched): Sampled textures via [`cf_material_set_texture_cs`](../graphics/cf_material_set_texture_cs.md), uniforms via [`cf_material_set_uniform_cs`](../graphics/cf_material_set_uniform_cs.md). +- **Dispatch struct** (positional): Read-write storage textures via `CF_ComputeDispatch::rw_textures`, readonly storage textures via `CF_ComputeDispatch::ro_textures`. + +```c +// Bind sampled texture and uniform via material. +cf_material_set_texture_cs(material, "u_input", my_texture); +cf_material_set_uniform_cs(material, "u_time", &time, CF_UNIFORM_TYPE_FLOAT, 1); + +// Bind RW storage texture via dispatch and run. +CF_ComputeDispatch dispatch = cf_compute_dispatch_defaults(width / 16, height / 16, 1); +dispatch.rw_textures = &output_texture; +dispatch.rw_texture_count = 1; +cf_dispatch_compute(compute_shader, material, dispatch); +``` + ## Textures Textures hold image data, as in pixels. Though in graphics we call them texels, not pixels. Actually, a texel can hold arbitrary data, but usually we just store one `vec4` (in glsl) or `CF_Color` (in C++) per pixel. Texture data is fetched from a shader using what's called uv-coordinates. diff --git a/docs/topics/multithreading.md b/docs/topics/multithreading.md index e1da4c385..c085bce31 100644 --- a/docs/topics/multithreading.md +++ b/docs/topics/multithreading.md @@ -56,13 +56,13 @@ Usually condition variables are not used on their own, and higher level primitiv ## Mutex -The mutex, aka lock ([`CF_Mutex`](http://localhost:3000/#/multithreading/cf_mutex), stands for "mutual exclusion". It's used as a basic _synchronization primitive_ for two threads to communicate with one another. The idea is the mutex can be acquired by only a single thread at any given time. If any other thread attempts to aquire the mutex (lock it), the thread will sleep and wait until the lock can be acquired. +The mutex, aka lock ([`CF_Mutex`](../multithreading/cf_mutex.md)), stands for "mutual exclusion". It's used as a basic _synchronization primitive_ for two threads to communicate with one another. The idea is the mutex can be acquired by only a single thread at any given time. If any other thread attempts to aquire the mutex (lock it), the thread will sleep and wait until the lock can be acquired. Sometimes locks are used on their own, but generally locks are considered difficult to use, tedious, and a bit error-prone. It's recommended to instead use a thread pool if possible. But, if you really know what you're doing sometimes a simple mutex is preferable. For further reading Beej has an excellent article on [Threads and Mutexes](https://beej.us/guide/bgc/html/split/multithreading.html). ## Read/Write Lock -Similar to mutex, a read write lock is a bit higher level ([`CF_ReadWriteLock`](http://localhost:3000/#/multithreading/cf_readwritelock)). It supports many simultaneous readers, but only a single writer at a time (the write excludes other readers as well). Usually the read write lock is used to implement other tools or data structures. +Similar to mutex, a read write lock is a bit higher level ([`CF_ReadWriteLock`](../multithreading/cf_readwritelock.md)). It supports many simultaneous readers, but only a single writer at a time (the write excludes other readers as well). Usually the read write lock is used to implement other tools or data structures. For example, a multi-threaded hash table can be constructed with a read write lock. Most of the time the hash table can operate in read-only mode. We can use a read write lock for the entire table. Many simultaneous readers can fetch or lookup keys freely from the table. If the table needs to insert an element we can then write lock the table and perform the insertion. This will wait for all readers to exitb before doing any writing. To take this a step further, if the hash table uses chained collision resolution another read write lock can be used for each collision chain. As chains are updated, only an individual chain needs to be write-locked at a given time. If the table becomes saturated it can expand it's memory size with a single global write-lock, of which would write-lock all of the chains before expansion. This would completely wait for all readers to exit before expansion. diff --git a/docs/topics/renderer.md b/docs/topics/renderer.md index 92f59ac48..3501e50d4 100644 --- a/docs/topics/renderer.md +++ b/docs/topics/renderer.md @@ -118,7 +118,7 @@ Conclusion? If you're still packing sprites into atlases offline then you're get Upgrading sprites to deal with flipbook style animations is a broad topic with many solutions. For CF I've chosen to natively support [Aseprite](https://www.aseprite.org). In the indie dev scene (and generally the 2D gamedev scene) Aseprite has more or less become the de facto tool of choice (besides perhaps some Photoshop lovers). Aseprite has its own binary format. CF just [parses these files directly](https://github.com/RandyGaul/cute_framework/blob/master/libraries/cute/cute_aseprite.h) and fills in animation tables that sprites can point to. Credits to Noel Berry from Celeste for the original idea and implementation of the .ase file loader. -Of course, other formats can be supported, as CF provides some lower level APIs to manually construct sprites by setting up [arrays of .png files](https://github.com/RandyGaul/cute_framework/blob/master/include/cute_png_cache.h). This lets users who already have their own custom art pipeline or flow integrate with CF in their own way. +Of course, other formats can be supported, as CF provides some lower level APIs to manually construct sprites from individual .png files. See the [Custom Sprites](custom_sprites.md) topic for details. This lets users who already have their own custom art pipeline or flow integrate with CF in their own way. # Shapes @@ -196,13 +196,13 @@ The fragment shader needs to look at `shape_type` and do an *actual branch* depe ```glsl void main() { - bool is_sprite = v_type >= (0.0/255.0) && v_type < (0.5/255.0); - bool is_text = v_type > (0.5/255.0) && v_type < (1.5/255.0); - bool is_box = v_type > (1.5/255.0) && v_type < (2.5/255.0); - bool is_seg = v_type > (2.5/255.0) && v_type < (3.5/255.0); - bool is_tri = v_type > (3.5/255.0) && v_type < (4.5/255.0); - bool is_tri_sdf = v_type > (4.5/255.0) && v_type < (5.5/255.0); - bool is_poly = v_type > (5.5/255.0) && v_type < (6.5/255.0); + bool is_sprite = v_type >= 0.0 && v_type < 0.5; + bool is_text = v_type > 0.5 && v_type < 1.5; + bool is_box = v_type > 1.5 && v_type < 2.5; + bool is_seg = v_type > 2.5 && v_type < 3.5; + bool is_tri = v_type > 3.5 && v_type < 4.5; + bool is_tri_sdf = v_type > 4.5 && v_type < 5.5; + bool is_poly = v_type > 5.5 && v_type < 6.5; // Traditional sprite/text/tri cases. vec4 c = vec4(0); @@ -280,10 +280,9 @@ To deal with camera zoom CF chose to pass in an `antialias_factor`, or dubiously // This factor remains constant-size despite zooming in/out with the camera. void CF_Draw::set_aaf() { - float on_or_off = draw->antialias.last() ? 1.0f : 0.0f; float inv_cam_scale = 1.0f / len(draw->cam_stack.last().m.y); - float scale = draw->antialias_scale.last(); - aaf = scale * inv_cam_scale * on_or_off; + float scale = draw->antialias.last(); + aaf = scale * inv_cam_scale; } ``` @@ -463,9 +462,9 @@ To create a shader you can check out the [Shader Compilation](https://randygaul. When using CF's draw API you can upload [Draw Shaders](https://randygaul.github.io/cute_framework/topics/drawing/#shaders) to the GPU. These are little fragment shaders to operate on shapes or canvases. It looks like this to start with as a pass-through shader (does no-op): ```glsl -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { - return vec4(mix(color.rgb, params.rgb, params.a), color.a); + return vec4(mix(color.rgb, params.attributes.rgb, params.attributes.a), color.a); } ``` @@ -495,9 +494,9 @@ vec4 normal_to_color(vec2 n) return vec4(n * 0.5 + 0.5, 1.0, 1.0); } -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { - vec2 uv = screen_uv; + vec2 uv = params.screen_uv; vec2 dim = vec2(1.0/160.0,1.0/120.0); vec2 n = normal_from_heightmap(noise_tex, uv); vec2 w = normal_from_heightmap(wavelets_tex, uv+n*dim*10.0); @@ -537,8 +536,8 @@ vec2 pts[8]; void main() { - bool is_sprite = v_type >= (0.0/255.0) && v_type < (0.5/255.0); - bool is_text = v_type > (0.5/255.0) && v_type < (1.5/255.0); + bool is_sprite = v_type >= 0.0 && v_type < 0.5; + bool is_text = v_type > 0.5 && v_type < 1.5; // snip ... ``` diff --git a/docs/topics/shader_compilation.md b/docs/topics/shader_compilation.md index 1e6a20a45..f1e58485a 100644 --- a/docs/topics/shader_compilation.md +++ b/docs/topics/shader_compilation.md @@ -146,6 +146,61 @@ When using CMake, prefix the path with `${CMAKE_CURRENT_SOURCE_DIR}` to make it CF also provides several builtin utility modules: `gamma.shd`, `distance.shd`, `smooth_uv.shd`, `blend.shd`. These can always be `#include`-d by your shader without setting the include path. You can view these files by looking at CF's source code to see what sort of extra helper functions are available for use in your shaders. +## Resource Set Layout + +All shaders compiled by CF (whether at runtime or precompiled) must assign resources to specific descriptor sets. These sets are dictated by SDL_GPU's SPIR-V binding convention and vary by shader stage. + +### Graphics Shaders (Vertex + Fragment) + +| Resource type | Vertex shader | Fragment shader | +|---|---|---| +| Sampled textures, storage textures, storage buffers | `set = 0` | `set = 2` | +| Uniform buffers | `set = 1` | `set = 3` | + +Example vertex shader: +```glsl +layout (set = 0, binding = 0) uniform sampler2D u_image; +layout (set = 1, binding = 0) uniform uniform_block { + vec2 u_texture_size; +}; +``` + +Example fragment shader: +```glsl +layout (set = 2, binding = 0) uniform sampler2D u_image; +layout (set = 3, binding = 0) uniform uniform_block { + vec2 u_texture_size; +}; +``` + +### Compute Shaders + +| Resource type | Set | +|---|---| +| Sampled textures, then readonly storage textures, then readonly storage buffers | `set = 0` | +| Read-write storage textures, then read-write storage buffers | `set = 1` | +| Uniform buffers | `set = 2` | + +Example compute shader: +```glsl +layout (set = 0, binding = 0) uniform sampler2D u_input; +layout (set = 1, binding = 0, rgba8) uniform writeonly image2D u_output; +layout (set = 2, binding = 0) uniform uniform_block { + float u_time; +}; +layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in; +``` + +When multiple resources share the same set, they are ordered by binding index. For example, if a compute shader has both a sampled texture and a readonly storage texture on set 0: +```glsl +layout (set = 0, binding = 0) uniform sampler2D u_sampled; +layout (set = 0, binding = 1) readonly uniform image2D u_storage; +``` + +### Barrier Safety + +When using `shared` memory with `barrier()` in compute shaders, all threads in a workgroup must reach each `barrier()` call. Do not use `return` to exit threads before a barrier. Instead, let all threads participate in the loop/barrier and guard side-effects (like `imageStore`) behind a bounds check at the end. + ## Draw Shader Quirks Due to the way [custom draw shaders](../topics/drawing.md?id=shaders) are compiled, any errors in your shader will be reported as being from `shader_stub.shd`. diff --git a/docs/topics/strings.md b/docs/topics/strings.md index 028922673..8b35528a7 100644 --- a/docs/topics/strings.md +++ b/docs/topics/strings.md @@ -2,21 +2,15 @@ CF has a [dynamic string API](../api_reference.md#string) where strings are 100% compatible with normal C-strings. Dynamic strings can be modified and grow onto the heap as necessary. They come with a wide variety of manipulation functions such as removing certain characters, trimming whitespace, find + replace, and a whole lot more. In C++ we also have the `String` class which wraps the dynamic C string API. -## Dynamic Strings - -In CF's C API we can create a new string with [`sset`](../string/sset.md). +> [!NOTE] +> CF provides shortform convenience macros for common string operations (e.g. `smake`, `sfree`, `sappend`, `sfmt`). These are simple wrappers around the longform `cf_string_*` functions. The shortform macros are not individually documented but work identically to their longform counterparts. -```cpp -char* s = NULL; -sset(s, "Hello world!"); -printf("%s", s); -sfree(s); -``` +## Dynamic Strings -Or alternatively: +In CF's C API we can create a new string with [`cf_string_make`](../string/cf_string_make.md) or [`cf_string_dup`](../string/cf_string_dup.md). ```cpp -char* s = sset(NULL, "Hello world!"); +char* s = smake("Hello world!"); printf("%s", s); sfree(s); ``` @@ -27,13 +21,21 @@ Which outputs: Hello world! ``` -All dynamic strings must be free'd up with [`sfree`](../string/sfree.md) when done. +To overwrite an existing string use [`cf_string_set`](../string/cf_string_set.md). The first argument must be an l-value (a variable), not a literal like NULL. Use `cf_string_make` to create a string from scratch. -To push some more characters onto the end of the string use [`spush`](../string/spush.md). +```cpp +char* s = smake("Hello world!"); +sset(s, "Goodbye!"); +printf("%s", s); +sfree(s); +``` + +All dynamic strings must be free'd up with [`cf_string_free`](../string/cf_string_free.md) when done. + +To push some more characters onto the end of the string use [`cf_string_push`](../string/cf_string_push.md). ```cpp -char* s = NULL; -sset(s, "Hello world!"); +char* s = smake("Hello world!"); spush(s, '!'); spush(s, '!'); @@ -48,11 +50,10 @@ Which outputs: Hello world!!! ``` -You can append a string onto the end of a dynamic string with [`sappend`](../string/sappend.md). +You can append a string onto the end of a dynamic string with [`cf_string_append`](../string/cf_string_append.md). ```cpp -char* s = NULL; -sset(s, "Hello world!"); +char* s = smake("Hello world!"); sappend(s, " What a nice string we have."); printf("%s", s); @@ -63,19 +64,19 @@ sfree(s); Which outputs: ``` -Hello world! What a string string we have. +Hello world! What a nice string we have. ``` ## String Conversions -To convert an integer to a string call [`sint`](../string/sint.md). +To convert an integer to a string call [`cf_string_int`](../string/cf_string_int.md). ```cpp char* s = sint(NULL, 10); printf("%s", s); // s is now "10". ``` -To convert from a string to an integer call [stoint](../string/stoint.md). +To convert from a string to an integer call [`cf_string_toint`](../string/cf_string_toint.md). ```cpp int x = stoint("100"); @@ -85,18 +86,19 @@ There are similar functions available for float, double, boolean, and hex number ## String Formatting -String formatting is done with a printf-style function called [`sfmt`](../string/sfmt.md). +String formatting is done with a printf-style function called [`cf_string_fmt`](../string/cf_string_fmt.md). To create a new formatted string from scratch use [`cf_string_fmt_make`](../string/cf_string_fmt_make.md). ```cpp -sfmt(s, "%s said hello to %s.\n", "Bob", "Sally"); -printf("%s, s"); // Prints: "Bob said hello to Sally." +char* s = sfmake("%s said hello to %s.\n", "Bob", "Sally"); +printf("%s", s); // Prints: "Bob said hello to Sally." +sfree(s); ``` -You can append a format (instead of overwriting the previous string contents) with [`sfmt_append`](../string/sfmt_append.md). +To overwrite an existing string with formatted text use `cf_string_fmt`. To append formatted text use [`cf_string_fmt_append`](../string/cf_string_fmt_append.md). ## String Manipulation -There are a variety of manipulation functions available for strings. Be sure to check out the [String API Reference](../api_reference.md#string) for a full list and for more examples. Some really popular ones include [`sreplace`](../string/sreplace.md), [`ssplit`](../string/ssplit.md), and [`strim`](../string/strim.md). +There are a variety of manipulation functions available for strings. Be sure to check out the [String API Reference](../api_reference.md#string) for a full list and for more examples. Some really popular ones include [`cf_string_replace`](../string/cf_string_replace.md), [`cf_string_split`](../string/cf_string_split.md), and [`cf_string_trim`](../string/cf_string_trim.md). ## String in C++ @@ -116,15 +118,15 @@ Well hello there! Today is red day, meaning everything is the color red. ## String Hashing -To get a hash of a string call [`shash`](../string/shash.md). +To get a hash of a string call [`cf_string_hash`](../string/cf_string_hash.md). -Be sure to check out this section on [String Interning](../topics/data_structures.md#strings-as-keys), which covers the [String Intern API](../string/sintern.md). You may use this to construct immutable strings that work super efficiently for comparisons and hash tables. +Be sure to check out this section on [String Interning](../topics/data_structures.md#strings-as-keys), which covers the [String Intern API](../string/cf_sintern.md). You may use `cf_sintern` to construct immutable strings that work super efficiently for comparisons and maps. ## UTF8 -It's highly recommended to store strings for your game in text files and load them up from disk. This makes it easy for a localizer to make different versions of text in different langauges without editing anything other than simple text files. The format of your strings should be in the [UTF8 format](https://en.wikipedia.org/wiki/UTF-8), which is 100% backwards compatible with typical C-strings you're already used to. +It's highly recommended to store strings for your game in text files and load them up from disk. This makes it easy for a localizer to make different versions of text in different languages without editing anything other than simple text files. The format of your strings should be in the [UTF8 format](https://en.wikipedia.org/wiki/UTF-8), which is 100% backwards compatible with typical C-strings you're already used to. -The UTF8 format encodes a large number of characters by making certain characters take up more than a single byte. To encode or decode UTF8 characters you may call [`sappend_UTF8`](../string/sappend_utf8.md) or [`cf_decode_UTF8`](../string/cf_decode_utf8.md). +The UTF8 format encodes a large number of characters by making certain characters take up more than a single byte. To encode or decode UTF8 characters you may call [`cf_string_append_UTF8`](../string/cf_string_append_utf8.md) or [`cf_string_decode_UTF8`](../string/cf_string_decode_utf8.md). In C++ we have access to the `UTF8` helper class. Simply load it up with a string and call `.next()` to get each decoded character with `.codepoint`. diff --git a/docs/topics/virtual_file_system.md b/docs/topics/virtual_file_system.md index d2d4bc92f..322694708 100644 --- a/docs/topics/virtual_file_system.md +++ b/docs/topics/virtual_file_system.md @@ -28,7 +28,7 @@ The above snippet will give the folder `"C:/Users/Randy/Documents/data"`, which - No colons `:` > [!NOTE] -> **Normalizing** a path is the process of removing relative directories, removing redundant or Windows style slashes, and attempting to convert the string to a more platform-independant form. You can still normalize platform-dependent paths too though. Call [`spnorm`](../path/spnorm.md) to normalize a string path. +> **Normalizing** a path is the process of removing relative directories, removing redundant or Windows style slashes, and attempting to convert the string to a more platform-independant form. You can still normalize platform-dependent paths too though. Call [`cf_path_normalize`](../path/cf_path_normalize.md) to normalize a string path. By mounting we achieve great portability by using platform-independent paths within our game. The paths are also more secure by removing relative paths (which reduce the chances of anyone accessing unanticipated directories), and most important of all grants versatility. diff --git a/include/cute.h b/include/cute.h index 66cd648b3..7852c0145 100644 --- a/include/cute.h +++ b/include/cute.h @@ -31,6 +31,7 @@ #include "cute_array.h" #include "cute_audio.h" #include "cute_base64.h" +#include "cute_binding.h" #include "cute_clipboard.h" #include "cute_color.h" #include "cute_multithreading.h" @@ -39,8 +40,10 @@ #include "cute_doubly_list.h" #include "cute_draw.h" #include "cute_file_system.h" +#include "cute_guid.h" #include "cute_graphics.h" -#include "cute_hashtable.h" +#include "cute_map.h" +#include "cute_haptics.h" #include "cute_https.h" #include "cute_image.h" #include "cute_input.h" @@ -49,7 +52,7 @@ #include "cute_math.h" #include "cute_networking.h" #include "cute_noise.h" -#include "cute_png_cache.h" +#include "cute_custom_sprite.h" #include "cute_rnd.h" #include "cute_sprite.h" #include "cute_string.h" diff --git a/include/cute_alloc.h b/include/cute_alloc.h index ed27f8b42..e6bc9d655 100644 --- a/include/cute_alloc.h +++ b/include/cute_alloc.h @@ -10,6 +10,10 @@ #include "cute_defines.h" +#define CK_ALLOC(sz) cf_alloc(sz) +#define CK_REALLOC(p, sz) cf_realloc(p, sz) +#define CK_FREE(p) cf_free(p) + #ifdef __cplusplus extern "C" { #endif // __cplusplus @@ -210,7 +214,7 @@ CF_API void CF_CALL cf_arena_reset(CF_Arena* arena); /** * @function cf_destroy_arena * @category allocator - * @brief Free's up all resources used by the allocator. + * @brief Frees up all resources used by the allocator. * @param arena The arena to free. * @related cf_arena_init cf_arena_alloc cf_arena_reset cf_arena_free */ diff --git a/include/cute_app.h b/include/cute_app.h index 4e31fecfd..80374232c 100644 --- a/include/cute_app.h +++ b/include/cute_app.h @@ -52,8 +52,8 @@ typedef uint32_t CF_DisplayID; /** * @function cf_default_display * @category app - * @brief TODO - * @related TODO + * @brief Returns the default display ID for the primary monitor. + * @related cf_default_display cf_display_count cf_get_display_list cf_free_display_list */ CF_API CF_DisplayID CF_CALL cf_default_display(void); @@ -61,7 +61,7 @@ CF_API CF_DisplayID CF_CALL cf_default_display(void); * @function cf_display_count * @category app * @brief Returns the number of displays on the system. - * @remarks Inidices >= 0 and < the return value are valid display indices. + * @remarks Indices >= 0 and < the return value are valid display indices. * @related cf_make_app cf_display_count cf_display_x cf_display_y cf_display_width cf_display_height cf_display_refresh_rate cf_display_bounds cf_display_name cf_display_orientation */ CF_API int CF_CALL cf_display_count(void); @@ -69,16 +69,16 @@ CF_API int CF_CALL cf_display_count(void); /** * @function cf_get_display_list * @category app - * @brief TODO - * @related TODO + * @brief Returns a dynamic array of all available display IDs. Free with `cf_free_display_list`. + * @related cf_default_display cf_display_count cf_get_display_list cf_free_display_list */ CF_API CF_DisplayID* CF_CALL cf_get_display_list(void); /** * @function cf_free_display_list * @category app - * @brief TODO - * @related TODO + * @brief Frees the display list returned by `cf_get_display_list`. + * @related cf_default_display cf_display_count cf_get_display_list cf_free_display_list */ CF_API void CF_CALL cf_free_display_list(CF_DisplayID* display_list); @@ -170,7 +170,7 @@ CF_API CF_DisplayOrientation CF_CALL cf_display_orientation(CF_DisplayID display * app_destroy(); * return 0; * } - * @remarks The `app_options` parameter of `cf_make_app` is a bitmask flag. Simply take the `APP_OPTIONS_*` flags listed above and OR them together. + * @remarks The `app_options` parameter of `cf_make_app` is a bitmask flag. Simply take the `CF_APP_OPTIONS_*` flags listed above and OR them together. * @related CF_AppOptionFlagBits cf_make_app cf_destroy_app */ #define CF_APP_OPTION_DEFS \ @@ -196,7 +196,7 @@ CF_API CF_DisplayOrientation CF_CALL cf_display_orientation(CF_DisplayID display CF_ENUM(APP_OPTIONS_GFX_METAL_BIT, 1 << 9) \ /* @entry Starts the application with a Vulkan backend. */ \ CF_ENUM(APP_OPTIONS_GFX_VULKAN_BIT, 1 << 10) \ - /* @entry TODO */ \ + /* @entry Starts the application with an OpenGL backend. */ \ CF_ENUM(APP_OPTIONS_GFX_OPENGL_BIT, 1 << 11) \ /* @entry Starts the application with a debug mode graphics context. */ \ CF_ENUM(APP_OPTIONS_GFX_DEBUG_BIT, 1 << 12) \ @@ -245,7 +245,7 @@ typedef enum CF_AppOptionFlagBits * return 0; * } * @remarks The options parameter is an enum from `app_options`. Different options can be OR'd together. - * Parameters `w` and `h` are ignored if the window is initialized to fullscreen mode with `APP_OPTIONS_FULLSCREEN`. + * Parameters `w` and `h` are ignored if the window is initialized to fullscreen mode with `CF_APP_OPTIONS_FULLSCREEN_BIT`. * @related CF_AppOptionFlagBits cf_app_is_running cf_app_signal_shutdown cf_destroy_app */ CF_API CF_Result CF_CALL cf_make_app(const char* window_title, CF_DisplayID display_id, int x, int y, int w, int h, CF_AppOptionFlags options, const char* argv0); @@ -270,7 +270,7 @@ CF_API void CF_CALL cf_destroy_app(void); * int main(int argc, const char** argv) * { * // Create a window with a resolution of 640 x 480, along with a DirectX 11 context. - * app_make("Fancy Window Title", 0, 50, 50, 640, 480, CF_APP_OPTIONS_D3D11_CONTEXT, argv[0]); + * app_make("Fancy Window Title", 0, 50, 50, 640, 480, CF_APP_OPTIONS_GFX_D3D11_BIT, argv[0]); * * while (app_is_running()) * { @@ -301,7 +301,7 @@ CF_API bool CF_CALL cf_app_is_running(void); * int main(int argc, const char** argv) * { * // Create a window with a resolution of 640 x 480, along with a DirectX 11 context. - * app_make("Fancy Window Title", 0, 50, 50, 640, 480, CF_APP_OPTIONS_D3D11_CONTEXT, argv[0]); + * app_make("Fancy Window Title", 0, 50, 50, 640, 480, CF_APP_OPTIONS_GFX_D3D11_BIT, argv[0]); * * while (app_is_running()) * { @@ -343,7 +343,7 @@ CF_API void CF_CALL cf_app_update(CF_OnUpdateFn* on_update); * int main(int argc, const char** argv) * { * // Create a window with a resolution of 640 x 480, along with a DirectX 11 context. - * app_make("Fancy Window Title", 0, 50, 50, 640, 480, CF_APP_OPTIONS_D3D11_CONTEXT, argv[0]); + * app_make("Fancy Window Title", 0, 50, 50, 640, 480, CF_APP_OPTIONS_GFX_D3D11_BIT, argv[0]); * * while (app_is_running()) * { @@ -509,7 +509,7 @@ CF_API void CF_CALL cf_app_request_attention(void); /** * @function cf_app_request_attention_continuously * @category app - * @brief Requests attention for the window for continuously. + * @brief Requests attention for the window continuously. * @remarks On Windows this flashes the tab icon, and bounces the dock icon on OSX. * @related cf_app_request_attention cf_app_request_attention_continuously cf_app_request_attention_cancel */ @@ -605,7 +605,7 @@ CF_API void* CF_CALL cf_app_init_imgui(void); * @enum CF_MSAA * @category app * @brief Multisample count used for MSAA for the app's offscreen canvas. - * @remarks This function turns on . + * @remarks This function turns on MSAA. * @related cf_app_set_msaa cf_msaa_string */ #define CF_MSAA_DEFS \ @@ -749,7 +749,7 @@ CF_API void CF_CALL cf_app_set_fullscreen_mode(void); /** * @function cf_app_set_title * @category app - * @brief Sets the application' true fullscreen mode's title. + * @brief Sets the window title. * @related cf_app_set_windowed_mode cf_app_set_borderless_fullscreen_mode cf_app_set_fullscreen_mode cf_app_set_title cf_app_set_icon */ CF_API void CF_CALL cf_app_set_title(const char* title); @@ -775,7 +775,7 @@ CF_API float CF_CALL cf_app_get_framerate(void); /** * @function cf_app_get_smoothed_framerate * @category app - * @brief Returns the smoothed framerate of the application. Last 60 frames are averaged. This values is controlled by `CF_FRAMERATE_SMOOTHING`. + * @brief Returns the smoothed framerate of the application. Last 60 frames are averaged. This value is controlled by `CF_FRAMERATE_SMOOTHING`. * @related cf_app_get_framerate cf_app_get_smoothed_framerate */ CF_API float CF_CALL cf_app_get_smoothed_framerate(void); @@ -891,13 +891,16 @@ CF_INLINE void app_show_window() { return cf_app_show_window(); } CF_INLINE int app_get_width() { return cf_app_get_width(); } CF_INLINE int app_get_height() { return cf_app_get_height(); } CF_INLINE float app_get_dpi_scale() { return cf_app_get_dpi_scale(); } -CF_INLINE bool app_dpi_scaled_was_changed() { return cf_app_dpi_scale_was_changed(); } +CF_INLINE bool app_dpi_scale_was_changed() { return cf_app_dpi_scale_was_changed(); } CF_INLINE void app_center_window() { cf_app_center_window(); } CF_INLINE bool app_was_resized() { return cf_app_was_resized(); } CF_INLINE bool app_was_moved() { return cf_app_was_moved(); } CF_INLINE bool app_lost_focus() { return cf_app_lost_focus(); } CF_INLINE bool app_gained_focus() { return cf_app_gained_focus(); } CF_INLINE bool app_has_focus() { return cf_app_has_focus(); } +CF_INLINE void app_request_attention() { cf_app_request_attention(); } +CF_INLINE void app_request_attention_continuously() { cf_app_request_attention_continuously(); } +CF_INLINE void app_request_attention_cancel() { cf_app_request_attention_cancel(); } CF_INLINE bool app_was_minimized() { return cf_app_was_minimized(); } CF_INLINE bool app_was_maximized() { return cf_app_was_maximized(); } CF_INLINE bool app_minimized() { return cf_app_minimized(); } diff --git a/include/cute_array.h b/include/cute_array.h index 2ff36b66f..80c407ea5 100644 --- a/include/cute_array.h +++ b/include/cute_array.h @@ -11,6 +11,10 @@ #include "cute_defines.h" #include "cute_c_runtime.h" #include "cute_alloc.h" +#include "cute/ckit.h" + +// Shortform array macros (apush, adel, etc.) are provided by ckit.h. +// You may use them directly if you wish, or use the cf_ longform prefixes below. //-------------------------------------------------------------------------------------------------- // C API @@ -20,277 +24,202 @@ * @category array * @brief An empty macro used in the C API to markup dynamic arrays. * @example > Creating a dynamic array, pushing some elements into the array, and freeing it up afterwards. - * dyna int* a = NULL; - * apush(a, 5); - * CF_ASSERT(alen(a) == 1); - * alen(a)--; - * CF_ASSERT(alen(a) == 0); - * afree(a); + * dyna int* a = NULL; + * apush(a, 5); + * CF_ASSERT(asize(a) == 1); + * asetlen(a, 0); + * CF_ASSERT(asize(a) == 0); + * afree(a); * @remarks This is an optional and _completely_ empty macro. It's only purpose is to provide a bit of visual indication a type is a * dynamic array. One downside of the C-macro API is the opaque nature of the pointer type. Since the macros use polymorphism * on typed pointers, there's no actual array struct type. It can get really annoying to sometimes forget if a pointer is an * array, a hashtable, or just a pointer. This macro can be used to markup the type to make it much more clear for function * parameters or struct member definitions. It's saying "Hey, I'm a dynamic array!" to mitigate this downside. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @related dyna cf_array_size cf_array_push cf_array_pop cf_array_free cf_array_hash */ -#define dyna +#define dyna CK_DYNA + +// Shortform macros (asize, acount, acap, afit, apush, apop, aend, alast, aclear, +// asetlen, aset, arev, ahash, adel, astatic, afree) are provided by ckit.h. + +//-------------------------------------------------------------------------------------------------- +// Longform C API. +// These map cf_array_* names to the shortform ckit macros. /** - * @function alen + * @function cf_array_size * @category array - * @brief Returns the number of elements in the array. + * @brief Returns the number of elements in the array, or 0 if NULL. * @param a The array. - * @example > Creating an array, adding an element, then decrementing the count to zero before freeing the array. - * dyna int* a = NULL; - * apush(a, 5); - * CF_ASSERT(alen(a) == 1); - * alen(a)--; - * CF_ASSERT(alen(a) == 0); - * afree(a); - * @remarks `a` must not by `NULL`. This function returns a proper l-value, so you can assign to it, i.e. increment/decrement can be quite useful. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @return Returns the number of elements in `a`. + * @remarks Shortform: `asize(a)`. + * @related cf_array_size cf_array_count cf_array_capacity cf_array_fit cf_array_push cf_array_pop cf_array_free */ -#define alen(a) cf_array_len(a) +#define cf_array_size(a) asize(a) /** - * @function asize + * @function cf_array_count * @category array - * @brief Returns the number of elements in the array. + * @brief Returns the number of elements in the array, or 0 if NULL. Same as `cf_array_size`. * @param a The array. - * @example > Creating an array, getting the size of the array, then freeing it up afterwards. - * dyna int* a = NULL; - * apush(a, 5); - * CF_ASSERT(asize(a) == 1); - * afree(a); - * @remarks `a` can be `NULL`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @return Returns the number of elements in `a`. + * @remarks Shortform: `acount(a)`. + * @related cf_array_size cf_array_size cf_array_capacity cf_array_fit cf_array_push cf_array_pop cf_array_free */ -#define asize(a) cf_array_size(a) +#define cf_array_count(a) acount(a) /** - * @function acount + * @function cf_array_capacity * @category array - * @brief Returns the number of elements in the array. + * @brief Returns the capacity of the array (number of elements allocated), or 0 if NULL. * @param a The array. - * @example > Creating an array, getting the size of the array, then freeing it up afterwards. - * dyna int* a = NULL; - * apush(a, 5); - * CF_ASSERT(acount(a) == 1); - * afree(a); - * @remarks `a` can be `NULL`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @return Returns the number of elements `a` can hold before the next reallocation. + * @remarks Shortform: `acap(a)`. + * @related cf_array_size cf_array_fit cf_array_push cf_array_free */ -#define acount(a) cf_array_count(a) +#define cf_array_capacity(a) acap(a) /** - * @function acap + * @function cf_array_fit * @category array - * @brief Returns the capacity of the array. - * @param a The array. - * @remarks `a` can be `NULL`. The capacity automatically grows if the size of the array grows over the capacity. - * You can use `afit` to ensure a minimum capacity as an optimization. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @brief Ensures the array can hold at least `n` elements, growing if necessary. + * @param a The array. Modified in-place. + * @param n The minimum number of elements to reserve capacity for. + * @remarks Shortform: `afit(a, n)`. + * @related cf_array_size cf_array_capacity cf_array_push cf_array_free */ -#define acap(a) cf_array_capacity(a) +#define cf_array_fit(a, n) afit(a, n) /** - * @function afit + * @function cf_array_push * @category array - * @brief Ensures the capacity of the array is at least `n` elements large. - * @param a The array. - * @param n The number of elements to resize the array's capacity to. - * @return Returns a pointer to the array. - * @remarks This function does not change the number of live elements, the count/size, of the array. Only the capacity. - * This function is only useful as an optimization to avoid extra/unnecessary internal allocations. `a` is - * automatically re-assigned to a new pointer if the array was internally regrown. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @brief Appends an element to the end of the array, growing if necessary. + * @param a The array. Modified in-place. + * @param ... The element to append. + * @remarks Shortform: `apush(a, ...)`. + * @related cf_array_pop cf_array_size cf_array_fit cf_array_free */ -#define afit(a, n) cf_array_fit(a, n) +#define cf_array_push(a, ...) apush(a, __VA_ARGS__) /** - * @function apush + * @function cf_array_pop * @category array - * @brief Pushes an element onto the back of the array. - * @param a The array. Can be `NULL`. - * @param ... The element to push. - * @return Returns the pushed element. - * @example > Pushing some elements onto an array and asserting their values. - * dyna int* a = NULL; - * apush(a, 5); - * apush(a, 13); - * CF_ASSERT(a[0] == 5); - * CF_ASSERT(a[1] == 13); - * CF_ASSERT(asize(a) == 2); - * afree(a); - * @remarks `a` is automatically re-assigned to a new pointer if the array was internally regrown. If `a` is `NULL` a new - * dynamic array is allocated on-the-spot for you, and assigned back to `a`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @brief Removes and returns the last element of the array. + * @param a The array. Must be non-NULL and non-empty. + * @return Returns the removed element. + * @remarks Shortform: `apop(a)`. + * @related cf_array_push cf_array_last cf_array_size */ -#define apush(a, ...) cf_array_push(a, (__VA_ARGS__)) +#define cf_array_pop(a) apop(a) /** - * @function apop + * @function cf_array_end * @category array - * @brief Pops and returns an element off the back of the array. - * @param a The array. Can not be `NULL`. - * @return Returns the popped element. - * @remarks The last element of the array is fetched and will be returned. The size of the array is decremented by one. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @brief Returns a pointer one past the last element. + * @param a The array. + * @return Returns `a + asize(a)`. + * @remarks Shortform: `aend(a)`. + * @related cf_array_last cf_array_size */ -#define apop(a) cf_array_pop(a) +#define cf_array_end(a) aend(a) /** - * @function aend + * @function cf_array_last * @category array - * @brief Returns a pointer one element beyond the end of the array. - * @param a The array. Can be `NULL`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @brief Returns the last element of the array. + * @param a The array. Must be non-NULL and non-empty. + * @return Returns `a[asize(a) - 1]`. + * @remarks Shortform: `alast(a)`. + * @related cf_array_pop cf_array_end cf_array_size */ -#define aend(a) cf_array_end(a) +#define cf_array_last(a) alast(a) /** - * @function alast + * @function cf_array_clear * @category array - * @brief Returns the last element in the array. - * @param a The array. Can not be `NULL`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @brief Sets the element count to zero without freeing memory. + * @param a The array. + * @remarks Shortform: `aclear(a)`. + * @related cf_array_free cf_array_size cf_array_setlen */ -#define alast(a) cf_array_last(a) +#define cf_array_clear(a) aclear(a) /** - * @function aclear + * @function cf_array_setlen * @category array - * @brief Sets the array's count to zero. Does not free any resources. - * @param a The array. Can be `NULL`. - * @return Returns zero. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @brief Directly sets the element count. The array must be non-NULL and have enough capacity. + * @param a The array. Must be non-NULL. + * @param n The new element count. + * @remarks Shortform: `asetlen(a, n)`. Use `cf_array_fit` first to ensure sufficient capacity. + * @related cf_array_clear cf_array_size cf_array_fit */ -#define aclear(a) cf_array_clear(a) +#define cf_array_setlen(a, n) asetlen(a, n) /** - * @function aset + * @function cf_array_set * @category array - * @brief Copies the array b into array a. Will automatically fit a if needed with `afit`. - * @param a The array to copy into (destination). - * @param b The array to copy from (source). - * @return Returns a pointer to `a`. `a` will automatically be reassigned to any new pointer. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @brief Copies the contents of array `b` into array `a`, resizing `a` if needed. + * @param a The destination array. Modified in-place. + * @param b The source array. + * @remarks Shortform: `aset(a, b)`. + * @related cf_array_size cf_array_fit cf_array_free */ -#define aset(a, b) cf_array_set(a, b) +#define cf_array_set(a, b) aset(a, b) /** - * @function arev + * @function cf_array_reverse * @category array - * @brief Reverses the elements in an array. - * @param a The array. Can be `NULL`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @brief Reverses the order of elements in the array in-place. + * @param a The array. + * @remarks Shortform: `arev(a)`. + * @related cf_array_size */ -#define arev(a) cf_array_reverse(a) +#define cf_array_reverse(a) arev(a) /** - * @function ahash + * @function cf_array_hash * @category array - * @brief Returns the hash of all the bytes in the array. + * @brief Returns a hash of all the bytes in the array using FNV1a. * @param a The array. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @return Returns a `uint64_t` hash. + * @remarks Shortform: `ahash(a)`. + * @related cf_array_size */ -#define ahash(a) cf_array_hash(a) +#define cf_array_hash(a) ahash(a) /** - * @function adel + * @function cf_array_del * @category array - * @brief Performs an *unordered* removal of the element at index i. + * @brief Removes an element by swapping it with the last element (unordered). * @param a The array. * @param i The index of the element to remove. - * @remarks The last element of the array is swapped into the index `i`. This is a constant time - * operation, but does *not preserve order* of the array. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @remarks Shortform: `adel(a, i)`. Does not preserve order. + * @related cf_array_pop cf_array_size */ -#define adel(a, i) cf_array_del(a, i) +#define cf_array_del(a, i) adel(a, i) /** - * @function astatic + * @function cf_array_static * @category array - * @brief Creates an array with an initial static storage backing. Will grow onto the heap if the size becomes too large. - * @param a The array. Can be `NULL`. - * @param buffer An initial buffer of memory to store the array within. - * @param buffer_size The size of `buffer` in bytes. - * @return Returns a pointer to `a`. `a` will automatically be reassigned to any new pointer. - * @remarks This macro is useful as an optimization to avoid any dynamic memory allocation in the common case for implementing - * certain data structures (such as strings or stack vectors). As the array grows too large for the `buffer` it will - * dynamically grow into the heap. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @brief Initializes a dynamic array backed by a static (stack) buffer. + * @param a The array pointer. Modified in-place. + * @param buffer A stack buffer to use as initial storage. + * @param buffer_size Size of `buffer` in bytes. + * @remarks Shortform: `astatic(a, buffer, buffer_size)`. The array will copy out of the static + * buffer and switch to heap allocation if it outgrows the buffer. + * @related cf_array_fit cf_array_free */ -#define astatic(a, buffer, buffer_size) cf_array_static(a, buffer, buffer_size) +#define cf_array_static(a, buffer, buffer_size) astatic(a, buffer, buffer_size) /** - * @function afree + * @function cf_array_free * @category array - * @brief Frees up all resources used by the array. - * @param a The array. - * @remarks Sets `a` to `NULL`. - * @related dyna asize acount acap afit apush apop aend alast aclear aset arev ahash adel astatic afree + * @brief Frees the array and sets the pointer to NULL. + * @param a The array. Modified in-place. Safe to call on NULL. + * @remarks Shortform: `afree(a)`. + * @related cf_array_push cf_array_clear cf_array_size */ -#define afree(a) cf_array_free(a) - -//-------------------------------------------------------------------------------------------------- -// Longform C API. - -#ifdef __cplusplus -#define cf_array_len(a) (CF_ACANARY(a), CF_AHDR(a)->size) -#else -#define cf_array_len(a) (CF_AHDR(a)->size) -#endif -#define cf_array_size(a) (a ? cf_array_len(a) : 0) -#define cf_array_count(a) cf_array_size(a) -#define cf_array_capacity(a) ((a) ? CF_AHDR(a)->capacity : 0) -#define cf_array_fit(a, n) ((n) <= cf_array_capacity(a) ? 0 : (*(void**)&(a) = cf_agrow((a), (n), sizeof(*a)))) -#define cf_array_push(a, ...) (CF_ACANARY(a), cf_array_fit((a), 1 + ((a) ? cf_array_len(a) : 0)), (a)[cf_array_len(a)++] = (__VA_ARGS__)) -#define cf_array_pop(a) (a[--cf_array_len(a)]) -#define cf_array_end(a) (a + cf_array_size(a)) -#define cf_array_last(a) (a[cf_array_len(a) - 1]) -#define cf_array_clear(a) (CF_ACANARY(a), (a) ? cf_array_len(a) = 0 : 0) -#define cf_array_set(a, b) (*(void**)&(a) = cf_aset((void*)(a), (void*)(b), sizeof(*a))) -#define cf_array_reverse(a) (a ? cf_arev(a, sizeof(*a)) : NULL) -#define cf_array_hash(a) cf_fnv1a(a, sizeof(*a) * cf_array_size(a)) -#define cf_array_del(a, i) (a[i] = a[--alen(a)]) -#define cf_array_static(a, buffer, buffer_size) (*(void**)&(a) = cf_astatic(buffer, buffer_size, sizeof(*a))) -#define cf_array_free(a) do { CF_ACANARY(a); if (a && !CF_AHDR(a)->is_static) cf_free(CF_AHDR(a)); a = NULL; } while (0) - -//-------------------------------------------------------------------------------------------------- -// Hidden API - Not intended for direct use. - -#define CF_AHDR(a) ((CF_Ahdr*)a - 1) -#define CF_ACOOKIE 0xE6F7E359 - -// If you see this macro asserting it likely means you didn't pass a proper dynamic string or array -// to one of the array/string macros. You must not pass string literals to these functions for certain input -// arguments, and instead, build a dynamic array/string using the C apis (e.g. `apush` or `sset`). This macro -// also helps detect heap/stack corruption such as buffer underruns. -#define CF_ACANARY(a) ((a) ? CF_ASSERT(CF_AHDR(a)->cookie == CF_ACOOKIE) : (void)0) - -// *Hidden* array header. -typedef struct CF_Ahdr -{ - int size; - int capacity; - bool is_static; - char* data; - uint32_t cookie; -} CF_Ahdr; - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -CF_API void* CF_CALL cf_agrow(const void* a, int new_size, size_t element_size); -CF_API void* CF_CALL cf_astatic(const void* a, int capacity, size_t element_size); -CF_API void* CF_CALL cf_aset(const void* a, const void* b, size_t element_size); -CF_API void* CF_CALL cf_arev(const void* a, size_t element_size); - -#ifdef __cplusplus -} -#endif // __cplusplus +#define cf_array_free(a) afree(a) //-------------------------------------------------------------------------------------------------- // C++ API diff --git a/include/cute_audio.h b/include/cute_audio.h index 16868d4f3..2a220f7b1 100644 --- a/include/cute_audio.h +++ b/include/cute_audio.h @@ -23,7 +23,7 @@ extern "C" { * @struct CF_Sound * @category audio * @brief An opaque pointer representing a sound created by `cf_play_sound`. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch */ typedef struct CF_Sound { uint64_t id; } CF_Sound; // @end @@ -88,22 +88,6 @@ CF_API CF_Audio CF_CALL cf_audio_load_wav_from_memory(void* memory, int byte_cou */ CF_API void CF_CALL cf_audio_destroy(CF_Audio audio); -/** - * @function cf_audio_cull_duplicates - * @category audio - * @brief Turns on/off duplicate culling. - * @remarks This is turned on by default. If the exact same sound is played more than once on a given audio update it will simply - * amplify the volume of the sound. This can create an audio bug where the sound plays way louder than intended. This - * setting simply culls away any extra calls to `play_sound` for a particular sound. - * - * The audio update rate is determined by how quickly the audio mixing thread runs, meaning exactly how many sounds can - * get culled within a particular timespan is variable -- but usually you still want this option turned on. - * - * When on this applies to both sound FX and music. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume - */ -CF_API void CF_CALL cf_audio_cull_duplicates(bool true_to_cull_duplicates); - /** * @function cf_audio_sample_rate * @category audio @@ -177,7 +161,7 @@ CF_API void CF_CALL cf_audio_set_pause(bool true_for_paused); * @brief Plays audio as music. * @param audio_source A `CF_Audio` containing your music. * @param fade_in_time A number of seconds to fade the music in. Can be 0.0f to instantly play your music. - * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_sample_index cf_music_set_sample_index cf_music_set_pitch + * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_time cf_music_set_time cf_music_set_pitch */ CF_API void CF_CALL cf_music_play(CF_Audio audio_source, float fade_in_time); @@ -186,7 +170,7 @@ CF_API void CF_CALL cf_music_play(CF_Audio audio_source, float fade_in_time); * @category audio * @brief Stop the music currently playing. * @param fade_out_time A number of seconds to fade the music out. Can be 0.0f to instantly stop your music. - * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_sample_index cf_music_set_sample_index cf_music_set_pitch + * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_time cf_music_set_time cf_music_set_pitch */ CF_API void CF_CALL cf_music_stop(float fade_out_time); @@ -195,7 +179,7 @@ CF_API void CF_CALL cf_music_stop(float fade_out_time); * @category audio * @brief Set the volume for music. * @param volume A value from 0.0f to 1.0f, where 0.0f means no volume, and 1.0f means full volume. - * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_sample_index cf_music_set_sample_index cf_music_set_pitch + * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_time cf_music_set_time cf_music_set_pitch */ CF_API void CF_CALL cf_music_set_volume(float volume); @@ -204,7 +188,7 @@ CF_API void CF_CALL cf_music_set_volume(float volume); * @category audio * @brief Turns on or off music looping. * @param true_to_loop True to loop the music. - * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_sample_index cf_music_set_sample_index cf_music_set_pitch + * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_time cf_music_set_time cf_music_set_pitch */ CF_API void CF_CALL cf_music_set_loop(bool true_to_loop); @@ -215,7 +199,7 @@ CF_API void CF_CALL cf_music_set_loop(bool true_to_loop); * @param pitch The pitch to set, default to 1.0f. * @remarks This is a playback speed multiplier, meaning negative numbers will play the music backwards, * while positive numbers play forwards. - * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_sample_index cf_music_set_sample_index cf_music_set_pitch + * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_time cf_music_set_time cf_music_set_pitch */ CF_API void CF_CALL cf_music_set_pitch(float pitch); @@ -223,7 +207,7 @@ CF_API void CF_CALL cf_music_set_pitch(float pitch); * @function cf_music_pause * @category audio * @brief Pauses the music. - * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_sample_index cf_music_set_sample_index cf_music_set_pitch + * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_time cf_music_set_time cf_music_set_pitch */ CF_API void CF_CALL cf_music_pause(void); @@ -231,7 +215,7 @@ CF_API void CF_CALL cf_music_pause(void); * @function cf_music_resume * @category audio * @brief Resumes the music if the music was paused. - * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_sample_index cf_music_set_sample_index cf_music_set_pitch + * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_time cf_music_set_time cf_music_set_pitch */ CF_API void CF_CALL cf_music_resume(void); @@ -242,7 +226,7 @@ CF_API void CF_CALL cf_music_resume(void); * @param fade_out_time A number of seconds to fade the currently playing track out. Can be 0.0f to instantly stop your music. * @param fade_in_time A number of seconds to fade the next track in. Can be 0.0f to instantly play your music. * @remarks The currently playing track is faded out, then the second track is faded in. - * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_sample_index cf_music_set_sample_index cf_music_set_pitch + * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_time cf_music_set_time cf_music_set_pitch */ CF_API void CF_CALL cf_music_switch_to(CF_Audio audio_source, float fade_out_time, float fade_in_time); @@ -251,28 +235,28 @@ CF_API void CF_CALL cf_music_switch_to(CF_Audio audio_source, float fade_out_tim * @category audio * @brief Crossfades the currently playing track out with the next track in. * @param cross_fade_time A number of seconds to crossfade to the next track. Can be 0.0f to instantly switch to the next track. - * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_sample_index cf_music_set_sample_index cf_music_set_pitch + * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_time cf_music_set_time cf_music_set_pitch */ CF_API void CF_CALL cf_music_crossfade(CF_Audio audio_source, float cross_fade_time); /** - * @function cf_music_get_sample_index + * @function cf_music_get_time * @category audio - * @brief Returns the current sample index the music is playing at. + * @brief Returns the current playback time of the music in seconds. * @remarks This can be useful to sync a dynamic audio system that can turn on/off different instruments or sounds. - * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_sample_index cf_music_set_sample_index cf_music_set_pitch + * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_time cf_music_set_time cf_music_set_pitch */ -CF_API int CF_CALL cf_music_get_sample_index(void); +CF_API double CF_CALL cf_music_get_time(void); /** - * @function cf_music_set_sample_index + * @function cf_music_set_time * @category audio - * @brief Sets the sample index to play at for the music. - * @param sample_index Tells where to play music from within the `CF_Audio` for the currently playing music track. + * @brief Seeks to a specific time in the currently playing music. + * @param time_in_seconds The playback position to seek to, in seconds. * @remarks This can be useful to sync a dynamic audio system that can turn on/off different instruments or sounds. - * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_sample_index cf_music_set_sample_index cf_music_set_pitch + * @related cf_music_play cf_music_stop cf_music_set_volume cf_music_set_loop cf_music_pause cf_music_resume cf_music_switch_to cf_music_crossfade cf_music_get_time cf_music_set_time cf_music_set_pitch */ -CF_API CF_Result CF_CALL cf_music_set_sample_index(int sample_index); +CF_API CF_Result CF_CALL cf_music_set_time(double time_in_seconds); /** * @function cf_music_set_on_finish_callback @@ -293,7 +277,7 @@ CF_API void CF_CALL cf_music_set_on_finish_callback(void (*on_finished)(void* ud * @category audio * @brief Parameters for the function `cf_play_sound`. * @remarks You can use default settings from the `cf_sound_params_defaults` function. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch */ typedef struct CF_SoundParams { @@ -312,8 +296,8 @@ typedef struct CF_SoundParams /* @member Default: 1.0f. Lower numbers lower the pitch and increase playback speed. Higher numbers increase the pitch and reduce playback speed. */ float pitch; - /* @member Default: 0. Specify the sample to start playing at. In term of seconds this would be the `cf_audio_sample_rate` * seconds. */ - int sample_index; + /* @member Default: 0.0. The starting time in seconds to begin playback from. */ + double start_time; } CF_SoundParams; // @end @@ -321,7 +305,7 @@ typedef struct CF_SoundParams * @function cf_sound_params_defaults * @category audio * @brief Returns a `CF_SoundParams` filled with default state, to use with `cf_play_sound`. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch */ CF_INLINE CF_SoundParams CF_CALL cf_sound_params_defaults(void) { @@ -331,7 +315,7 @@ CF_INLINE CF_SoundParams CF_CALL cf_sound_params_defaults(void) params.volume = 1.0f; params.pan = 0.5f; params.pitch = 1.0f; - params.sample_index = 0; + params.start_time = 0.0; return params; } @@ -342,7 +326,7 @@ CF_INLINE CF_SoundParams CF_CALL cf_sound_params_defaults(void) * @param audio_source The `CF_Audio` samples for the sound to play. * @param params `CF_SoundParams` on how to play the sound. You can use default values by calling `cf_sound_params_defaults`. * @return Returns a playing sound `CF_Sound`. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch */ CF_API CF_Sound CF_CALL cf_play_sound(CF_Audio audio_source, CF_SoundParams params); @@ -362,8 +346,8 @@ CF_API void CF_CALL cf_sound_set_on_finish_callback(void (*on_finished)(CF_Sound * @category audio * @brief Returns whether or not a sound is active. * @param sound The sound. - * @return Rreturns true if the sound is active, or false if it finished playing (and was not looped). - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch + * @return Returns true if the sound is active, or false if it finished playing (and was not looped). + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch */ CF_API bool CF_CALL cf_sound_is_active(CF_Sound sound); @@ -373,7 +357,7 @@ CF_API bool CF_CALL cf_sound_is_active(CF_Sound sound); * @brief Returns whether or not a sound is paused. * @param sound The sound. * @remarks You can set a sound to paused with `cf_sound_set_is_paused`, or upon creation with `cf_play_sound`. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch */ CF_API bool CF_CALL cf_sound_get_is_paused(CF_Sound sound); @@ -383,7 +367,7 @@ CF_API bool CF_CALL cf_sound_get_is_paused(CF_Sound sound); * @brief Returns whether or not a sound is looped. * @param sound The sound. * @remarks You can set a sound to looped with `cf_sound_set_is_looped`, or upon creation with `cf_play_sound`. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch */ CF_API bool CF_CALL cf_sound_get_is_looped(CF_Sound sound); @@ -393,7 +377,7 @@ CF_API bool CF_CALL cf_sound_get_is_looped(CF_Sound sound); * @brief Returns the volume of the sound. * @param sound The sound. * @remarks You can set a sound volume with `cf_sound_set_volume`, or upon creation with `cf_play_sound`. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch */ CF_API float CF_CALL cf_sound_get_volume(CF_Sound sound); @@ -402,21 +386,21 @@ CF_API float CF_CALL cf_sound_get_volume(CF_Sound sound); * @category audio * @brief Returns the pitch of the sound. * @param sound The sound. - * @remarks You can set a sound volume with `cf_sound_set_volume`, or upon creation with `cf_play_sound`. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch + * @remarks You can set a sound pitch with `cf_sound_set_pitch`, or upon creation with `cf_play_sound`. + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch cf_sound_get_pitch */ CF_API float CF_CALL cf_sound_get_pitch(CF_Sound sound); /** - * @function cf_sound_get_sample_index + * @function cf_sound_get_time * @category audio - * @brief Returns the index of the currently playing sample for the sound. + * @brief Returns the current playback time of the sound in seconds. * @param sound The sound. - * @remarks You can set a sound's playing index with `cf_sound_set_sample_index`. This can be useful to sync a dynamic audio system that + * @remarks You can set a sound's playback time with `cf_sound_set_time`. This can be useful to sync a dynamic audio system that * can turn on/off different instruments or sounds. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch */ -CF_API int CF_CALL cf_sound_get_sample_index(CF_Sound sound); +CF_API double CF_CALL cf_sound_get_time(CF_Sound sound); /** * @function cf_sound_set_is_paused @@ -425,7 +409,7 @@ CF_API int CF_CALL cf_sound_get_sample_index(CF_Sound sound); * @param sound The sound. * @param true_for_paused The pause state to set. * @remarks You can get a sound's paused state with `cf_sound_get_is_paused`. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch */ CF_API void CF_CALL cf_sound_set_is_paused(CF_Sound sound, bool true_for_paused); @@ -436,7 +420,7 @@ CF_API void CF_CALL cf_sound_set_is_paused(CF_Sound sound, bool true_for_paused) * @param sound The sound. * @param true_for_looped The loop state to set. * @remarks You can get a sound's looped state with `cf_sound_get_is_looped`. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch */ CF_API void CF_CALL cf_sound_set_is_looped(CF_Sound sound, bool true_for_looped); @@ -447,7 +431,7 @@ CF_API void CF_CALL cf_sound_set_is_looped(CF_Sound sound, bool true_for_looped) * @param sound The sound. * @param volume A value from 0.0f to 1.0f. 0.0f meaning silent, 1.0f meaning max volume. * @remarks You can get a sound's volume with `cf_sound_get_volume`. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch */ CF_API void CF_CALL cf_sound_set_volume(CF_Sound sound, float volume); @@ -456,27 +440,27 @@ CF_API void CF_CALL cf_sound_set_volume(CF_Sound sound, float volume); * @category audio * @brief Sets pitch for the sound. * @remarks Defaults to 1.0f. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch */ CF_API void CF_CALL cf_sound_set_pitch(CF_Sound sound, float pitch); /** - * @function cf_sound_set_sample_index + * @function cf_sound_set_time * @category audio - * @brief Sets the sample index for the sound to control which sample to play next. - * @param sound The sound. - * @param sample_index The index of the sample to play the sound from. - * @remarks You can get a sound's playing index with `cf_sound_get_sample_index`. This can be useful to sync a dynamic audio system that + * @brief Seeks to a specific time in the sound. + * @param sound The sound. + * @param time_in_seconds The playback position to seek to, in seconds. + * @remarks You can get a sound's playback time with `cf_sound_get_time`. This can be useful to sync a dynamic audio system that * can turn on/off different instruments or sounds. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch */ -CF_API void CF_CALL cf_sound_set_sample_index(CF_Sound sound, int sample_index); +CF_API CF_Result CF_CALL cf_sound_set_time(CF_Sound sound, double time_in_seconds); /** * @function cf_sound_stop * @category audio * @brief Stops the sound instance so it no longer plays. - * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_sample_index cf_sound_set_sample_index cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch + * @related CF_SoundParams CF_Sound cf_sound_params_defaults cf_play_sound cf_sound_is_active cf_sound_get_is_paused cf_sound_get_is_looped cf_sound_get_volume cf_sound_get_time cf_sound_set_time cf_sound_set_is_paused cf_sound_set_is_looped cf_sound_set_volume cf_sound_stop cf_sound_set_pitch */ CF_API void CF_CALL cf_sound_stop(CF_Sound sound); @@ -497,7 +481,6 @@ CF_INLINE CF_Audio audio_load_wav(const char* path) { return cf_audio_load_wav(p CF_INLINE CF_Audio audio_load_ogg_from_memory(void* memory, int byte_count) { return cf_audio_load_ogg_from_memory(memory, byte_count); } CF_INLINE CF_Audio audio_load_wav_from_memory(void* memory, int byte_count) { return cf_audio_load_wav_from_memory(memory, byte_count); } CF_INLINE void audio_destroy(CF_Audio audio) { cf_audio_destroy(audio); } -CF_INLINE void audio_cull_duplicates(bool true_to_cull_duplicates = false) { cf_audio_cull_duplicates(true_to_cull_duplicates); } CF_INLINE int audio_sample_rate(CF_Audio audio) { return cf_audio_sample_rate(audio); } CF_INLINE int audio_sample_count(CF_Audio audio) { return cf_audio_sample_count(audio); } CF_INLINE int audio_channel_count(CF_Audio audio) { return cf_audio_channel_count(audio); } @@ -522,8 +505,8 @@ CF_INLINE void music_pause() { cf_music_pause(); } CF_INLINE void music_resume() { cf_music_resume(); } CF_INLINE void music_switch_to(CF_Audio audio_source, float fade_out_time = 0, float fade_in_time = 0) { cf_music_switch_to(audio_source, fade_out_time, fade_in_time); } CF_INLINE void music_crossfade(CF_Audio audio_source, float cross_fade_time = 0) { cf_music_crossfade(audio_source, cross_fade_time); } -CF_INLINE void music_set_sample_index(int sample_index) { cf_music_set_sample_index(sample_index); } -CF_INLINE int music_get_sample_index() { return cf_music_get_sample_index(); } +CF_INLINE CF_Result music_set_time(double time_in_seconds) { return cf_music_set_time(time_in_seconds); } +CF_INLINE double music_get_time() { return cf_music_get_time(); } // ------------------------------------------------------------------------------------------------- @@ -535,14 +518,17 @@ CF_INLINE bool sound_get_is_paused(CF_Sound sound) { return cf_sound_get_is_paus CF_INLINE bool sound_get_is_looped(CF_Sound sound) { return cf_sound_get_is_looped(sound); } CF_INLINE float sound_get_volume(CF_Sound sound) { return cf_sound_get_volume(sound); } CF_INLINE float sound_get_pitch(CF_Sound sound) { return cf_sound_get_pitch(sound); } -CF_INLINE int sound_get_sample_index(CF_Sound sound) { return cf_sound_get_sample_index(sound); } +CF_INLINE double sound_get_time(CF_Sound sound) { return cf_sound_get_time(sound); } CF_INLINE void sound_set_is_paused(CF_Sound sound, bool true_for_paused) { cf_sound_set_is_paused(sound, true_for_paused); } CF_INLINE void sound_set_is_looped(CF_Sound sound, bool true_for_looped) { cf_sound_set_is_looped(sound, true_for_looped); } CF_INLINE void sound_set_volume(CF_Sound sound, float volume) { cf_sound_set_volume(sound, volume); } CF_INLINE void sound_set_pitch(CF_Sound sound, float pitch = 1.0f) { cf_sound_set_pitch(sound, pitch); } -CF_INLINE void sound_set_sample_index(CF_Sound sound, int sample_index) { cf_sound_set_sample_index(sound, sample_index); } +CF_INLINE CF_Result sound_set_time(CF_Sound sound, double time_in_seconds) { return cf_sound_set_time(sound, time_in_seconds); } CF_INLINE void sound_stop(CF_Sound sound) { cf_sound_stop(sound); } +CF_INLINE void music_set_on_finish_callback(void (*on_finished)(void* udata), void* udata = NULL, bool single_threaded = true) { cf_music_set_on_finish_callback(on_finished, udata, single_threaded); } +CF_INLINE void sound_set_on_finish_callback(void (*on_finished)(CF_Sound snd, void* udata), void* udata = NULL, bool single_threaded = true) { cf_sound_set_on_finish_callback(on_finished, udata, single_threaded); } + } #endif // CF_CPP diff --git a/include/cute_base64.h b/include/cute_base64.h index c887ff295..11718bb7a 100644 --- a/include/cute_base64.h +++ b/include/cute_base64.h @@ -38,7 +38,7 @@ extern "C" { * @category base64 * @brief Calculates the size of data after base64 decoding it. * @param size The size of base64 encoded data. - * @return Returns the number of bytes the raw dencoded data will take up. This will deflate the size ~33%. + * @return Returns the number of bytes the raw decoded data will take up. This will deflate the size ~33%. * @remarks Use this for the `dst_size` in `cf_base64_decode`. * Base64 encoding is useful for storing data as text in a copy-paste-safe manner. For more information about * base64 encoding see this link: [RFC-4648](https://tools.ietf.org/html/rfc4648) or [Wikipedia Base64](https://en.wikipedia.org/wiki/Base64). diff --git a/include/cute_binding.h b/include/cute_binding.h new file mode 100644 index 000000000..2a0dcaae2 --- /dev/null +++ b/include/cute_binding.h @@ -0,0 +1,897 @@ +/* + Cute Framework + Copyright (C) 2026 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +#ifndef CF_BINDING_H +#define CF_BINDING_H + +#include "cute_defines.h" +#include "cute_math.h" +#include "cute_input.h" +#include "cute_joypad.h" +#include "cute_time.h" + +//-------------------------------------------------------------------------------------------------- +// C API + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** + * @struct CF_ButtonBinding + * @category binding + * @brief A handle to a button binding, which maps multiple physical inputs to a single abstract button. + * @remarks Supports input buffering, where presses/releases persist over a small time window for forgiving input. + * Create with `cf_make_button_binding`, destroy with `cf_destroy_button_binding`. + * @related CF_ButtonBinding CF_AxisBinding CF_StickBinding cf_make_button_binding cf_destroy_button_binding + */ +typedef struct CF_ButtonBinding { uint64_t id; } CF_ButtonBinding; +// @end + +/** + * @struct CF_AxisBinding + * @category binding + * @brief A handle to an axis binding, which maps a negative/positive button pair to a -1..1 axis. + * @remarks Supports conflict resolution when both directions are pressed simultaneously. + * Create with `cf_make_axis_binding`, destroy with `cf_destroy_axis_binding`. + * @related CF_ButtonBinding CF_AxisBinding CF_StickBinding cf_make_axis_binding cf_destroy_axis_binding + */ +typedef struct CF_AxisBinding { uint64_t id; } CF_AxisBinding; +// @end + +/** + * @struct CF_StickBinding + * @category binding + * @brief A handle to a stick binding, combining two axis bindings (x and y) into a 2D stick. + * @remarks Create with `cf_make_stick_binding`, destroy with `cf_destroy_stick_binding`. + * @related CF_ButtonBinding CF_AxisBinding CF_StickBinding cf_make_stick_binding cf_destroy_stick_binding + */ +typedef struct CF_StickBinding { uint64_t id; } CF_StickBinding; +// @end + +/** + * @enum CF_AxisConflict + * @category binding + * @brief How to resolve simultaneous opposing directions on an axis. + * @related CF_AxisConflict cf_axis_binding_set_conflict + */ +#define CF_AXIS_CONFLICT_DEFS \ + /* @entry If both directions pressed, use the newest. */ \ + CF_ENUM(AXIS_CONFLICT_NEWEST, 0) \ + /* @entry If both directions pressed, use the oldest. */ \ + CF_ENUM(AXIS_CONFLICT_OLDEST, 1) \ + /* @entry If both directions pressed, return 0. */ \ + CF_ENUM(AXIS_CONFLICT_CANCEL, 2) \ + /* @end */ + +typedef enum CF_AxisConflict +{ + #define CF_ENUM(K, V) CF_##K = V, + CF_AXIS_CONFLICT_DEFS + #undef CF_ENUM +} CF_AxisConflict; + +/** + * @function cf_axis_conflict_to_string + * @category binding + * @brief Converts a `CF_AxisConflict` to a c-string. + * @param conflict The conflict mode. + * @related CF_AxisConflict + */ +CF_INLINE const char* cf_axis_conflict_to_string(CF_AxisConflict conflict) +{ + switch (conflict) { + #define CF_ENUM(K, V) case CF_##K: return CF_STRINGIZE(CF_##K); + CF_AXIS_CONFLICT_DEFS + #undef CF_ENUM + default: return NULL; + } +} + +//-------------------------------------------------------------------------------------------------- +// ButtonBinding API. + +/** + * @function cf_make_button_binding + * @category binding + * @brief Creates a button binding for the given player. + * @param player_index The joypad player index (0 for single player). + * @param press_buffer Seconds to buffer press/release events for forgiving input. + * @return Returns a `CF_ButtonBinding` handle. + * @related CF_ButtonBinding cf_make_button_binding cf_destroy_button_binding + */ +CF_API CF_ButtonBinding CF_CALL cf_make_button_binding(int player_index, float press_buffer); + +/** + * @function cf_destroy_button_binding + * @category binding + * @brief Destroys a button binding. + * @param b The button binding handle. + * @related CF_ButtonBinding cf_make_button_binding cf_destroy_button_binding + */ +CF_API void CF_CALL cf_destroy_button_binding(CF_ButtonBinding b); + +/** + * @function cf_button_binding_add_key + * @category binding + * @brief Binds a keyboard key to this button binding. + * @param b The button binding handle. + * @param key The key to bind. + * @related CF_ButtonBinding cf_button_binding_add_key cf_button_binding_add_mouse_button cf_button_binding_add_joypad_button cf_button_binding_add_trigger + */ +CF_API void CF_CALL cf_button_binding_add_key(CF_ButtonBinding b, CF_KeyButton key); + +/** + * @function cf_button_binding_add_mouse_button + * @category binding + * @brief Binds a mouse button to this button binding. + * @param b The button binding handle. + * @param button The mouse button to bind. + * @related CF_ButtonBinding cf_button_binding_add_key cf_button_binding_add_mouse_button cf_button_binding_add_joypad_button cf_button_binding_add_trigger + */ +CF_API void CF_CALL cf_button_binding_add_mouse_button(CF_ButtonBinding b, CF_MouseButton button); + +/** + * @function cf_button_binding_add_joypad_button + * @category binding + * @brief Binds a joypad button to this button binding. + * @param b The button binding handle. + * @param button The joypad button to bind. + * @related CF_ButtonBinding cf_button_binding_add_key cf_button_binding_add_mouse_button cf_button_binding_add_joypad_button cf_button_binding_add_trigger + */ +CF_API void CF_CALL cf_button_binding_add_joypad_button(CF_ButtonBinding b, CF_JoypadButton button); + +/** + * @function cf_button_binding_add_trigger + * @category binding + * @brief Binds a joypad axis (as a trigger/threshold) to this button binding. + * @param b The button binding handle. + * @param axis The joypad axis to use. + * @param threshold The threshold value (0..1) at which the axis counts as pressed. + * @param positive True to trigger on positive axis values, false for negative. + * @related CF_ButtonBinding cf_button_binding_add_key cf_button_binding_add_mouse_button cf_button_binding_add_joypad_button cf_button_binding_add_trigger + */ +CF_API void CF_CALL cf_button_binding_add_trigger(CF_ButtonBinding b, CF_JoypadAxis axis, float threshold, bool positive); + +/** + * @function cf_button_binding_pressed + * @category binding + * @brief Returns true if the button was pressed (with input buffering). + * @param b The button binding handle. + * @related CF_ButtonBinding cf_button_binding_pressed cf_button_binding_released cf_button_binding_down cf_button_binding_value + */ +CF_API bool CF_CALL cf_button_binding_pressed(CF_ButtonBinding b); + +/** + * @function cf_button_binding_released + * @category binding + * @brief Returns true if the button was released (with input buffering). + * @param b The button binding handle. + * @related CF_ButtonBinding cf_button_binding_pressed cf_button_binding_released cf_button_binding_down cf_button_binding_value + */ +CF_API bool CF_CALL cf_button_binding_released(CF_ButtonBinding b); + +/** + * @function cf_button_binding_down + * @category binding + * @brief Returns true if the button is currently held down. + * @param b The button binding handle. + * @related CF_ButtonBinding cf_button_binding_pressed cf_button_binding_released cf_button_binding_down cf_button_binding_value + */ +CF_API bool CF_CALL cf_button_binding_down(CF_ButtonBinding b); + +/** + * @function cf_button_binding_value + * @category binding + * @brief Returns the analog value of the button (0..1). + * @param b The button binding handle. + * @related CF_ButtonBinding cf_button_binding_value cf_button_binding_sign + */ +CF_API float CF_CALL cf_button_binding_value(CF_ButtonBinding b); + +/** + * @function cf_button_binding_sign + * @category binding + * @brief Returns -1, 0, or 1 based on the button value. + * @param b The button binding handle. + * @related CF_ButtonBinding cf_button_binding_value cf_button_binding_sign + */ +CF_API float CF_CALL cf_button_binding_sign(CF_ButtonBinding b); + +/** + * @function cf_button_binding_consume_press + * @category binding + * @brief Consumes the press event so subsequent queries return false until a new press occurs. + * @param b The button binding handle. + * @return Returns true if there was a press to consume. + * @related CF_ButtonBinding cf_button_binding_consume_press cf_button_binding_consume_release + */ +CF_API bool CF_CALL cf_button_binding_consume_press(CF_ButtonBinding b); + +/** + * @function cf_button_binding_consume_release + * @category binding + * @brief Consumes the release event so subsequent queries return false until a new release occurs. + * @param b The button binding handle. + * @return Returns true if there was a release to consume. + * @related CF_ButtonBinding cf_button_binding_consume_press cf_button_binding_consume_release + */ +CF_API bool CF_CALL cf_button_binding_consume_release(CF_ButtonBinding b); + +/** + * @function cf_button_binding_set_deadzone + * @category binding + * @brief Sets a per-binding deadzone, overriding the global deadzone for this binding. + * @param b The button binding handle. + * @param deadzone The deadzone value. Set to -1 to use the global deadzone. + * @related CF_ButtonBinding cf_binding_set_deadzone cf_axis_binding_set_deadzone + */ +CF_API void CF_CALL cf_button_binding_set_deadzone(CF_ButtonBinding b, float deadzone); + +/** + * @function cf_button_binding_value_raw + * @category binding + * @brief Returns the raw analog value (0..1) with no deadzone applied. + * @param b The button binding handle. + * @related CF_ButtonBinding cf_button_binding_value cf_axis_binding_value_raw cf_stick_binding_value_raw + */ +CF_API float CF_CALL cf_button_binding_value_raw(CF_ButtonBinding b); + +/** + * @function cf_button_binding_set_repeat + * @category binding + * @brief Enables key repeat for this button binding. + * @param b The button binding handle. + * @param delay Seconds before the first repeat fires. Set to -1 to disable repeat. + * @param interval Seconds between subsequent repeats. + * @related CF_ButtonBinding cf_button_binding_repeated + */ +CF_API void CF_CALL cf_button_binding_set_repeat(CF_ButtonBinding b, float delay, float interval); + +/** + * @function cf_button_binding_repeated + * @category binding + * @brief Returns true on frames where the key repeat fires. + * @param b The button binding handle. + * @related CF_ButtonBinding cf_button_binding_set_repeat + */ +CF_API bool CF_CALL cf_button_binding_repeated(CF_ButtonBinding b); + +//-------------------------------------------------------------------------------------------------- +// AxisBinding API. + +/** + * @function cf_make_axis_binding + * @category binding + * @brief Creates an axis binding for the given player. + * @param player_index The joypad player index (0 for single player). + * @return Returns a `CF_AxisBinding` handle. + * @related CF_AxisBinding cf_make_axis_binding cf_destroy_axis_binding + */ +CF_API CF_AxisBinding CF_CALL cf_make_axis_binding(int player_index); + +/** + * @function cf_destroy_axis_binding + * @category binding + * @brief Destroys an axis binding and its internal button bindings. + * @param a The axis binding handle. + * @related CF_AxisBinding cf_make_axis_binding cf_destroy_axis_binding + */ +CF_API void CF_CALL cf_destroy_axis_binding(CF_AxisBinding a); + +/** + * @function cf_axis_binding_add_keys + * @category binding + * @brief Binds a negative/positive key pair to this axis. + * @param a The axis binding handle. + * @param negative The key for negative direction. + * @param positive The key for positive direction. + * @related CF_AxisBinding cf_axis_binding_add_keys cf_axis_binding_add_mouse_buttons cf_axis_binding_add_joypad_buttons + */ +CF_API void CF_CALL cf_axis_binding_add_keys(CF_AxisBinding a, CF_KeyButton negative, CF_KeyButton positive); + +/** + * @function cf_axis_binding_add_mouse_buttons + * @category binding + * @brief Binds a negative/positive mouse button pair to this axis. + * @param a The axis binding handle. + * @param negative The mouse button for negative direction. + * @param positive The mouse button for positive direction. + * @related CF_AxisBinding cf_axis_binding_add_keys cf_axis_binding_add_mouse_buttons cf_axis_binding_add_joypad_buttons + */ +CF_API void CF_CALL cf_axis_binding_add_mouse_buttons(CF_AxisBinding a, CF_MouseButton negative, CF_MouseButton positive); + +/** + * @function cf_axis_binding_add_joypad_buttons + * @category binding + * @brief Binds a negative/positive joypad button pair to this axis. + * @param a The axis binding handle. + * @param negative The joypad button for negative direction. + * @param positive The joypad button for positive direction. + * @related CF_AxisBinding cf_axis_binding_add_keys cf_axis_binding_add_mouse_buttons cf_axis_binding_add_joypad_buttons + */ +CF_API void CF_CALL cf_axis_binding_add_joypad_buttons(CF_AxisBinding a, CF_JoypadButton negative, CF_JoypadButton positive); + +/** + * @function cf_axis_binding_add_triggers + * @category binding + * @brief Binds a negative/positive joypad axis pair (as triggers) to this axis. + * @param a The axis binding handle. + * @param negative The joypad axis for negative direction. + * @param positive The joypad axis for positive direction. + * @param threshold The threshold value (0..1) at which the axis counts as pressed. + * @related CF_AxisBinding cf_axis_binding_add_triggers + */ +CF_API void CF_CALL cf_axis_binding_add_triggers(CF_AxisBinding a, CF_JoypadAxis negative, CF_JoypadAxis positive, float threshold); + +/** + * @function cf_axis_binding_add_left_stick_x + * @category binding + * @brief Binds the left stick X axis. + * @param a The axis binding handle. + * @param threshold Deadzone threshold. + * @related CF_AxisBinding cf_axis_binding_add_left_stick_x cf_axis_binding_add_left_stick_y cf_axis_binding_add_right_stick_x cf_axis_binding_add_right_stick_y + */ +CF_API void CF_CALL cf_axis_binding_add_left_stick_x(CF_AxisBinding a, float threshold); + +/** + * @function cf_axis_binding_add_left_stick_y + * @category binding + * @brief Binds the left stick Y axis. + * @param a The axis binding handle. + * @param threshold Deadzone threshold. + * @related CF_AxisBinding cf_axis_binding_add_left_stick_x cf_axis_binding_add_left_stick_y cf_axis_binding_add_right_stick_x cf_axis_binding_add_right_stick_y + */ +CF_API void CF_CALL cf_axis_binding_add_left_stick_y(CF_AxisBinding a, float threshold); + +/** + * @function cf_axis_binding_add_right_stick_x + * @category binding + * @brief Binds the right stick X axis. + * @param a The axis binding handle. + * @param threshold Deadzone threshold. + * @related CF_AxisBinding cf_axis_binding_add_left_stick_x cf_axis_binding_add_left_stick_y cf_axis_binding_add_right_stick_x cf_axis_binding_add_right_stick_y + */ +CF_API void CF_CALL cf_axis_binding_add_right_stick_x(CF_AxisBinding a, float threshold); + +/** + * @function cf_axis_binding_add_right_stick_y + * @category binding + * @brief Binds the right stick Y axis. + * @param a The axis binding handle. + * @param threshold Deadzone threshold. + * @related CF_AxisBinding cf_axis_binding_add_left_stick_x cf_axis_binding_add_left_stick_y cf_axis_binding_add_right_stick_x cf_axis_binding_add_right_stick_y + */ +CF_API void CF_CALL cf_axis_binding_add_right_stick_y(CF_AxisBinding a, float threshold); + +/** + * @function cf_axis_binding_set_conflict + * @category binding + * @brief Sets the conflict resolution mode for when both directions are pressed. + * @param a The axis binding handle. + * @param mode The conflict resolution mode. + * @related CF_AxisBinding CF_AxisConflict cf_axis_binding_set_conflict + */ +CF_API void CF_CALL cf_axis_binding_set_conflict(CF_AxisBinding a, CF_AxisConflict mode); + +/** + * @function cf_axis_binding_pressed + * @category binding + * @brief Returns true if either direction of the axis was pressed. + * @param a The axis binding handle. + * @related CF_AxisBinding cf_axis_binding_pressed cf_axis_binding_released cf_axis_binding_value cf_axis_binding_sign + */ +CF_API bool CF_CALL cf_axis_binding_pressed(CF_AxisBinding a); + +/** + * @function cf_axis_binding_released + * @category binding + * @brief Returns true if either direction of the axis was released. + * @param a The axis binding handle. + * @related CF_AxisBinding cf_axis_binding_pressed cf_axis_binding_released cf_axis_binding_value cf_axis_binding_sign + */ +CF_API bool CF_CALL cf_axis_binding_released(CF_AxisBinding a); + +/** + * @function cf_axis_binding_value + * @category binding + * @brief Returns the axis value from -1 to 1, with conflict resolution applied. + * @param a The axis binding handle. + * @related CF_AxisBinding cf_axis_binding_pressed cf_axis_binding_released cf_axis_binding_value cf_axis_binding_sign + */ +CF_API float CF_CALL cf_axis_binding_value(CF_AxisBinding a); + +/** + * @function cf_axis_binding_sign + * @category binding + * @brief Returns -1, 0, or 1 based on the axis value. + * @param a The axis binding handle. + * @related CF_AxisBinding cf_axis_binding_value cf_axis_binding_sign + */ +CF_API float CF_CALL cf_axis_binding_sign(CF_AxisBinding a); + +/** + * @function cf_axis_binding_consume_press + * @category binding + * @brief Consumes press events on both directions of the axis. + * @param a The axis binding handle. + * @return Returns true if there was a press to consume. + * @related CF_AxisBinding cf_axis_binding_consume_press cf_axis_binding_consume_release + */ +CF_API bool CF_CALL cf_axis_binding_consume_press(CF_AxisBinding a); + +/** + * @function cf_axis_binding_consume_release + * @category binding + * @brief Consumes release events on both directions of the axis. + * @param a The axis binding handle. + * @return Returns true if there was a release to consume. + * @related CF_AxisBinding cf_axis_binding_consume_press cf_axis_binding_consume_release + */ +CF_API bool CF_CALL cf_axis_binding_consume_release(CF_AxisBinding a); + +/** + * @function cf_axis_binding_set_deadzone + * @category binding + * @brief Sets a per-binding deadzone on both directions of the axis. + * @param a The axis binding handle. + * @param deadzone The deadzone value. Set to -1 to use the global deadzone. + * @related CF_AxisBinding cf_button_binding_set_deadzone cf_binding_set_deadzone + */ +CF_API void CF_CALL cf_axis_binding_set_deadzone(CF_AxisBinding a, float deadzone); + +/** + * @function cf_axis_binding_value_raw + * @category binding + * @brief Returns the raw axis value (-1..1) with no deadzone applied. + * @param a The axis binding handle. + * @related CF_AxisBinding cf_axis_binding_value cf_button_binding_value_raw cf_stick_binding_value_raw + */ +CF_API float CF_CALL cf_axis_binding_value_raw(CF_AxisBinding a); + +//-------------------------------------------------------------------------------------------------- +// StickBinding API. + +/** + * @function cf_make_stick_binding + * @category binding + * @brief Creates a stick binding for the given player, combining X and Y axes. + * @param player_index The joypad player index (0 for single player). + * @return Returns a `CF_StickBinding` handle. + * @related CF_StickBinding cf_make_stick_binding cf_destroy_stick_binding + */ +CF_API CF_StickBinding CF_CALL cf_make_stick_binding(int player_index); + +/** + * @function cf_destroy_stick_binding + * @category binding + * @brief Destroys a stick binding and its internal axis bindings. + * @param s The stick binding handle. + * @related CF_StickBinding cf_make_stick_binding cf_destroy_stick_binding + */ +CF_API void CF_CALL cf_destroy_stick_binding(CF_StickBinding s); + +/** + * @function cf_stick_binding_add_keys + * @category binding + * @brief Binds four keys (up/down/left/right) to this stick. + * @param s The stick binding handle. + * @param up Key for up. + * @param down Key for down. + * @param left Key for left. + * @param right Key for right. + * @related CF_StickBinding cf_stick_binding_add_keys cf_stick_binding_add_wasd cf_stick_binding_add_arrow_keys cf_stick_binding_add_dpad + */ +CF_API void CF_CALL cf_stick_binding_add_keys(CF_StickBinding s, CF_KeyButton up, CF_KeyButton down, CF_KeyButton left, CF_KeyButton right); + +/** + * @function cf_stick_binding_add_wasd + * @category binding + * @brief Binds WASD keys to this stick. + * @param s The stick binding handle. + * @related CF_StickBinding cf_stick_binding_add_keys cf_stick_binding_add_wasd cf_stick_binding_add_arrow_keys cf_stick_binding_add_dpad + */ +CF_API void CF_CALL cf_stick_binding_add_wasd(CF_StickBinding s); + +/** + * @function cf_stick_binding_add_arrow_keys + * @category binding + * @brief Binds arrow keys to this stick. + * @param s The stick binding handle. + * @related CF_StickBinding cf_stick_binding_add_keys cf_stick_binding_add_wasd cf_stick_binding_add_arrow_keys cf_stick_binding_add_dpad + */ +CF_API void CF_CALL cf_stick_binding_add_arrow_keys(CF_StickBinding s); + +/** + * @function cf_stick_binding_add_dpad + * @category binding + * @brief Binds the joypad d-pad to this stick. + * @param s The stick binding handle. + * @related CF_StickBinding cf_stick_binding_add_keys cf_stick_binding_add_wasd cf_stick_binding_add_arrow_keys cf_stick_binding_add_dpad + */ +CF_API void CF_CALL cf_stick_binding_add_dpad(CF_StickBinding s); + +/** + * @function cf_stick_binding_add_left_stick + * @category binding + * @brief Binds the left analog stick to this stick. + * @param s The stick binding handle. + * @param threshold Deadzone threshold. + * @related CF_StickBinding cf_stick_binding_add_left_stick cf_stick_binding_add_right_stick + */ +CF_API void CF_CALL cf_stick_binding_add_left_stick(CF_StickBinding s, float threshold); + +/** + * @function cf_stick_binding_add_right_stick + * @category binding + * @brief Binds the right analog stick to this stick. + * @param s The stick binding handle. + * @param threshold Deadzone threshold. + * @related CF_StickBinding cf_stick_binding_add_left_stick cf_stick_binding_add_right_stick + */ +CF_API void CF_CALL cf_stick_binding_add_right_stick(CF_StickBinding s, float threshold); + +/** + * @function cf_stick_binding_pressed + * @category binding + * @brief Returns true if any direction of the stick was pressed. + * @param s The stick binding handle. + * @related CF_StickBinding cf_stick_binding_pressed cf_stick_binding_released cf_stick_binding_value cf_stick_binding_sign + */ +CF_API bool CF_CALL cf_stick_binding_pressed(CF_StickBinding s); + +/** + * @function cf_stick_binding_released + * @category binding + * @brief Returns true if any direction of the stick was released. + * @param s The stick binding handle. + * @related CF_StickBinding cf_stick_binding_pressed cf_stick_binding_released cf_stick_binding_value cf_stick_binding_sign + */ +CF_API bool CF_CALL cf_stick_binding_released(CF_StickBinding s); + +/** + * @function cf_stick_binding_value + * @category binding + * @brief Returns a 2D vector with x/y axis values, each from -1 to 1. + * @param s The stick binding handle. + * @related CF_StickBinding cf_stick_binding_pressed cf_stick_binding_released cf_stick_binding_value cf_stick_binding_sign + */ +CF_API CF_V2 CF_CALL cf_stick_binding_value(CF_StickBinding s); + +/** + * @function cf_stick_binding_sign + * @category binding + * @brief Returns a 2D vector where each component is -1, 0, or 1. + * @param s The stick binding handle. + * @related CF_StickBinding cf_stick_binding_value cf_stick_binding_sign + */ +CF_API CF_V2 CF_CALL cf_stick_binding_sign(CF_StickBinding s); + +/** + * @function cf_stick_binding_consume_press + * @category binding + * @brief Consumes press events on both axes of the stick. + * @param s The stick binding handle. + * @return Returns true if there was a press to consume. + * @related CF_StickBinding cf_stick_binding_consume_press cf_stick_binding_consume_release + */ +CF_API bool CF_CALL cf_stick_binding_consume_press(CF_StickBinding s); + +/** + * @function cf_stick_binding_consume_release + * @category binding + * @brief Consumes release events on both axes of the stick. + * @param s The stick binding handle. + * @return Returns true if there was a release to consume. + * @related CF_StickBinding cf_stick_binding_consume_press cf_stick_binding_consume_release + */ +CF_API bool CF_CALL cf_stick_binding_consume_release(CF_StickBinding s); + +/** + * @function cf_stick_binding_set_circular_deadzone + * @category binding + * @brief Sets a circular deadzone on the stick, applied to the combined x/y magnitude. + * @param s The stick binding handle. + * @param deadzone The circular deadzone radius (0..1). Set to -1 to disable. + * @remarks When active, both underlying axis deadzones are set to 0 so analog values pass through for the circular calculation. + * Digital inputs (keys, dpad) override the analog value when active. + * @related CF_StickBinding cf_stick_binding_value cf_binding_set_deadzone + */ +CF_API void CF_CALL cf_stick_binding_set_circular_deadzone(CF_StickBinding s, float deadzone); + +/** + * @function cf_stick_binding_value_raw + * @category binding + * @brief Returns the raw 2D stick value with no deadzone applied. + * @param s The stick binding handle. + * @related CF_StickBinding cf_stick_binding_value cf_button_binding_value_raw cf_axis_binding_value_raw + */ +CF_API CF_V2 CF_CALL cf_stick_binding_value_raw(CF_StickBinding s); + +//-------------------------------------------------------------------------------------------------- +// Joypad connection tracking. + +/** + * @function cf_register_joypad_connect_callback + * @category binding + * @brief Registers a callback invoked when a joypad is connected or disconnected. + * @param fn Callback function: player_index, connected (true=connect, false=disconnect), udata. + * @param udata User data passed to the callback. + * @related cf_register_joypad_connect_callback + */ +CF_API void CF_CALL cf_register_joypad_connect_callback(void (*fn)(int player_index, bool connected, void* udata), void* udata); + +//-------------------------------------------------------------------------------------------------- +// Deadzone. + +/** + * @function cf_binding_get_deadzone + * @category binding + * @brief Returns the global deadzone threshold for analog stick inputs. + * @remarks Defaults to 0.15f. Values below this threshold are zeroed out. Valid range is roughly 0.1 to 0.2. + * Individual bindings can override this with `cf_button_binding_set_deadzone` or `cf_axis_binding_set_deadzone`. + * @related cf_binding_get_deadzone cf_binding_set_deadzone cf_button_binding_set_deadzone cf_axis_binding_set_deadzone + */ +CF_API float CF_CALL cf_binding_get_deadzone(); + +/** + * @function cf_binding_set_deadzone + * @category binding + * @brief Sets the global deadzone threshold for analog stick inputs. + * @param deadzone The deadzone value (roughly 0.1 to 0.2). Default is 0.15f. + * @remarks Inputs from controllers below this threshold are zeroed out. Setting too low causes stick drift, too high reduces sensitivity. + * Individual bindings can override this with `cf_button_binding_set_deadzone` or `cf_axis_binding_set_deadzone`. + * @related cf_binding_get_deadzone cf_binding_set_deadzone cf_button_binding_set_deadzone cf_axis_binding_set_deadzone + */ +CF_API void CF_CALL cf_binding_set_deadzone(float deadzone); + +//-------------------------------------------------------------------------------------------------- +// _Generic dispatch macros (C11). + +#ifndef __cplusplus + +/** + * @function cf_binding_pressed + * @category binding + * @brief Returns true if the binding was pressed (with buffering), dispatching by handle type. + * @param x A `CF_ButtonBinding`, `CF_AxisBinding`, or `CF_StickBinding` handle. + * @remarks This is a C11 `_Generic` macro. In C++ use the overloaded `binding_pressed()` functions in `namespace Cute`. + * @related cf_button_binding_pressed cf_axis_binding_pressed cf_stick_binding_pressed cf_binding_released cf_binding_value cf_binding_sign + */ +#define cf_binding_pressed(x) _Generic((x), \ + CF_ButtonBinding: cf_button_binding_pressed, \ + CF_AxisBinding: cf_axis_binding_pressed, \ + CF_StickBinding: cf_stick_binding_pressed \ +)(x) + +/** + * @function cf_binding_released + * @category binding + * @brief Returns true if the binding was released (with buffering), dispatching by handle type. + * @param x A `CF_ButtonBinding`, `CF_AxisBinding`, or `CF_StickBinding` handle. + * @remarks This is a C11 `_Generic` macro. In C++ use the overloaded `binding_released()` functions in `namespace Cute`. + * @related cf_button_binding_released cf_axis_binding_released cf_stick_binding_released cf_binding_pressed cf_binding_value cf_binding_sign + */ +#define cf_binding_released(x) _Generic((x), \ + CF_ButtonBinding: cf_button_binding_released, \ + CF_AxisBinding: cf_axis_binding_released, \ + CF_StickBinding: cf_stick_binding_released \ +)(x) + +/** + * @function cf_binding_value + * @category binding + * @brief Returns the analog value of the binding, dispatching by handle type. + * @param x A `CF_ButtonBinding`, `CF_AxisBinding`, or `CF_StickBinding` handle. + * @remarks This is a C11 `_Generic` macro. Returns `float` for button/axis, `CF_V2` for stick. In C++ use the overloaded `binding_value()` functions in `namespace Cute`. + * @related cf_button_binding_value cf_axis_binding_value cf_stick_binding_value cf_binding_pressed cf_binding_released cf_binding_sign + */ +#define cf_binding_value(x) _Generic((x), \ + CF_ButtonBinding: cf_button_binding_value, \ + CF_AxisBinding: cf_axis_binding_value, \ + CF_StickBinding: cf_stick_binding_value \ +)(x) + +/** + * @function cf_binding_sign + * @category binding + * @brief Returns the sign of the binding value, dispatching by handle type. + * @param x A `CF_ButtonBinding`, `CF_AxisBinding`, or `CF_StickBinding` handle. + * @remarks This is a C11 `_Generic` macro. Returns `float` (-1/0/1) for button/axis, `CF_V2` for stick. In C++ use the overloaded `binding_sign()` functions in `namespace Cute`. + * @related cf_button_binding_sign cf_axis_binding_sign cf_stick_binding_sign cf_binding_pressed cf_binding_released cf_binding_value + */ +#define cf_binding_sign(x) _Generic((x), \ + CF_ButtonBinding: cf_button_binding_sign, \ + CF_AxisBinding: cf_axis_binding_sign, \ + CF_StickBinding: cf_stick_binding_sign \ +)(x) + +/** + * @function cf_binding_consume_press + * @category binding + * @brief Consumes the press event on the binding, dispatching by handle type. + * @param x A `CF_ButtonBinding`, `CF_AxisBinding`, or `CF_StickBinding` handle. + * @remarks This is a C11 `_Generic` macro. In C++ use the overloaded `binding_consume_press()` functions in `namespace Cute`. + * @related cf_button_binding_consume_press cf_axis_binding_consume_press cf_stick_binding_consume_press cf_binding_consume_release + */ +#define cf_binding_consume_press(x) _Generic((x), \ + CF_ButtonBinding: cf_button_binding_consume_press, \ + CF_AxisBinding: cf_axis_binding_consume_press, \ + CF_StickBinding: cf_stick_binding_consume_press \ +)(x) + +/** + * @function cf_binding_consume_release + * @category binding + * @brief Consumes the release event on the binding, dispatching by handle type. + * @param x A `CF_ButtonBinding`, `CF_AxisBinding`, or `CF_StickBinding` handle. + * @remarks This is a C11 `_Generic` macro. In C++ use the overloaded `binding_consume_release()` functions in `namespace Cute`. + * @related cf_button_binding_consume_release cf_axis_binding_consume_release cf_stick_binding_consume_release cf_binding_consume_press + */ +#define cf_binding_consume_release(x) _Generic((x), \ + CF_ButtonBinding: cf_button_binding_consume_release, \ + CF_AxisBinding: cf_axis_binding_consume_release, \ + CF_StickBinding: cf_stick_binding_consume_release \ +)(x) + +/** + * @function cf_binding_value_raw + * @category binding + * @brief Returns the raw value of the binding (no deadzone), dispatching by handle type. + * @param x A `CF_ButtonBinding`, `CF_AxisBinding`, or `CF_StickBinding` handle. + * @remarks This is a C11 `_Generic` macro. Returns `float` for button/axis, `CF_V2` for stick. + * @related cf_button_binding_value_raw cf_axis_binding_value_raw cf_stick_binding_value_raw cf_binding_value + */ +#define cf_binding_value_raw(x) _Generic((x), \ + CF_ButtonBinding: cf_button_binding_value_raw, \ + CF_AxisBinding: cf_axis_binding_value_raw, \ + CF_StickBinding: cf_stick_binding_value_raw \ +)(x) + +/** + * @function cf_binding_set_deadzone_per + * @category binding + * @brief Sets a per-binding deadzone, dispatching by handle type (button or axis). + * @param x A `CF_ButtonBinding` or `CF_AxisBinding` handle. + * @param dz The deadzone value. Set to -1 to use the global deadzone. + * @remarks This is a C11 `_Generic` macro. For stick bindings use `cf_stick_binding_set_circular_deadzone` instead. + * @related cf_button_binding_set_deadzone cf_axis_binding_set_deadzone cf_stick_binding_set_circular_deadzone + */ +#define cf_binding_set_deadzone_per(x, dz) _Generic((x), \ + CF_ButtonBinding: cf_button_binding_set_deadzone, \ + CF_AxisBinding: cf_axis_binding_set_deadzone \ +)(x, dz) + +/** + * @function cf_destroy_binding + * @category binding + * @brief Destroys a binding and frees its resources, dispatching by handle type. + * @param x A `CF_ButtonBinding`, `CF_AxisBinding`, or `CF_StickBinding` handle. + * @remarks This is a C11 `_Generic` macro. In C++ use the overloaded `destroy_binding()` functions in `namespace Cute`. + * @related cf_destroy_button_binding cf_destroy_axis_binding cf_destroy_stick_binding cf_make_button_binding cf_make_axis_binding cf_make_stick_binding + */ +#define cf_destroy_binding(x) _Generic((x), \ + CF_ButtonBinding: cf_destroy_button_binding, \ + CF_AxisBinding: cf_destroy_axis_binding, \ + CF_StickBinding: cf_destroy_stick_binding \ +)(x) + +#endif // !__cplusplus + +#ifdef __cplusplus +} +#endif // __cplusplus + +//-------------------------------------------------------------------------------------------------- +// C++ API + +#ifdef CF_CPP + +namespace Cute +{ + +CF_INLINE CF_ButtonBinding make_button_binding(int player_index, float press_buffer) { return cf_make_button_binding(player_index, press_buffer); } +CF_INLINE void destroy_button_binding(CF_ButtonBinding b) { cf_destroy_button_binding(b); } +CF_INLINE void button_binding_add_key(CF_ButtonBinding b, CF_KeyButton key) { cf_button_binding_add_key(b, key); } +CF_INLINE void button_binding_add_mouse_button(CF_ButtonBinding b, CF_MouseButton button) { cf_button_binding_add_mouse_button(b, button); } +CF_INLINE void button_binding_add_joypad_button(CF_ButtonBinding b, CF_JoypadButton button) { cf_button_binding_add_joypad_button(b, button); } +CF_INLINE void button_binding_add_trigger(CF_ButtonBinding b, CF_JoypadAxis axis, float threshold, bool positive) { cf_button_binding_add_trigger(b, axis, threshold, positive); } +CF_INLINE bool button_binding_pressed(CF_ButtonBinding b) { return cf_button_binding_pressed(b); } +CF_INLINE bool button_binding_released(CF_ButtonBinding b) { return cf_button_binding_released(b); } +CF_INLINE bool button_binding_down(CF_ButtonBinding b) { return cf_button_binding_down(b); } +CF_INLINE float button_binding_value(CF_ButtonBinding b) { return cf_button_binding_value(b); } +CF_INLINE float button_binding_sign(CF_ButtonBinding b) { return cf_button_binding_sign(b); } +CF_INLINE bool button_binding_consume_press(CF_ButtonBinding b) { return cf_button_binding_consume_press(b); } +CF_INLINE bool button_binding_consume_release(CF_ButtonBinding b) { return cf_button_binding_consume_release(b); } +CF_INLINE void button_binding_set_deadzone(CF_ButtonBinding b, float deadzone) { cf_button_binding_set_deadzone(b, deadzone); } +CF_INLINE float button_binding_value_raw(CF_ButtonBinding b) { return cf_button_binding_value_raw(b); } +CF_INLINE void button_binding_set_repeat(CF_ButtonBinding b, float delay, float interval) { cf_button_binding_set_repeat(b, delay, interval); } +CF_INLINE bool button_binding_repeated(CF_ButtonBinding b) { return cf_button_binding_repeated(b); } + +CF_INLINE CF_AxisBinding make_axis_binding(int player_index) { return cf_make_axis_binding(player_index); } +CF_INLINE void destroy_axis_binding(CF_AxisBinding a) { cf_destroy_axis_binding(a); } +CF_INLINE void axis_binding_add_keys(CF_AxisBinding a, CF_KeyButton negative, CF_KeyButton positive) { cf_axis_binding_add_keys(a, negative, positive); } +CF_INLINE void axis_binding_add_mouse_buttons(CF_AxisBinding a, CF_MouseButton negative, CF_MouseButton positive) { cf_axis_binding_add_mouse_buttons(a, negative, positive); } +CF_INLINE void axis_binding_add_joypad_buttons(CF_AxisBinding a, CF_JoypadButton negative, CF_JoypadButton positive) { cf_axis_binding_add_joypad_buttons(a, negative, positive); } +CF_INLINE void axis_binding_add_triggers(CF_AxisBinding a, CF_JoypadAxis negative, CF_JoypadAxis positive, float threshold) { cf_axis_binding_add_triggers(a, negative, positive, threshold); } +CF_INLINE void axis_binding_add_left_stick_x(CF_AxisBinding a, float threshold) { cf_axis_binding_add_left_stick_x(a, threshold); } +CF_INLINE void axis_binding_add_left_stick_y(CF_AxisBinding a, float threshold) { cf_axis_binding_add_left_stick_y(a, threshold); } +CF_INLINE void axis_binding_add_right_stick_x(CF_AxisBinding a, float threshold) { cf_axis_binding_add_right_stick_x(a, threshold); } +CF_INLINE void axis_binding_add_right_stick_y(CF_AxisBinding a, float threshold) { cf_axis_binding_add_right_stick_y(a, threshold); } +CF_INLINE void axis_binding_set_conflict(CF_AxisBinding a, CF_AxisConflict mode) { cf_axis_binding_set_conflict(a, mode); } +CF_INLINE bool axis_binding_pressed(CF_AxisBinding a) { return cf_axis_binding_pressed(a); } +CF_INLINE bool axis_binding_released(CF_AxisBinding a) { return cf_axis_binding_released(a); } +CF_INLINE float axis_binding_value(CF_AxisBinding a) { return cf_axis_binding_value(a); } +CF_INLINE float axis_binding_sign(CF_AxisBinding a) { return cf_axis_binding_sign(a); } +CF_INLINE bool axis_binding_consume_press(CF_AxisBinding a) { return cf_axis_binding_consume_press(a); } +CF_INLINE bool axis_binding_consume_release(CF_AxisBinding a) { return cf_axis_binding_consume_release(a); } +CF_INLINE void axis_binding_set_deadzone(CF_AxisBinding a, float deadzone) { cf_axis_binding_set_deadzone(a, deadzone); } +CF_INLINE float axis_binding_value_raw(CF_AxisBinding a) { return cf_axis_binding_value_raw(a); } + +CF_INLINE CF_StickBinding make_stick_binding(int player_index) { return cf_make_stick_binding(player_index); } +CF_INLINE void destroy_stick_binding(CF_StickBinding s) { cf_destroy_stick_binding(s); } +CF_INLINE void stick_binding_add_keys(CF_StickBinding s, CF_KeyButton up, CF_KeyButton down, CF_KeyButton left, CF_KeyButton right) { cf_stick_binding_add_keys(s, up, down, left, right); } +CF_INLINE void stick_binding_add_wasd(CF_StickBinding s) { cf_stick_binding_add_wasd(s); } +CF_INLINE void stick_binding_add_arrow_keys(CF_StickBinding s) { cf_stick_binding_add_arrow_keys(s); } +CF_INLINE void stick_binding_add_dpad(CF_StickBinding s) { cf_stick_binding_add_dpad(s); } +CF_INLINE void stick_binding_add_left_stick(CF_StickBinding s, float threshold) { cf_stick_binding_add_left_stick(s, threshold); } +CF_INLINE void stick_binding_add_right_stick(CF_StickBinding s, float threshold) { cf_stick_binding_add_right_stick(s, threshold); } +CF_INLINE bool stick_binding_pressed(CF_StickBinding s) { return cf_stick_binding_pressed(s); } +CF_INLINE bool stick_binding_released(CF_StickBinding s) { return cf_stick_binding_released(s); } +CF_INLINE CF_V2 stick_binding_value(CF_StickBinding s) { return cf_stick_binding_value(s); } +CF_INLINE CF_V2 stick_binding_sign(CF_StickBinding s) { return cf_stick_binding_sign(s); } +CF_INLINE bool stick_binding_consume_press(CF_StickBinding s) { return cf_stick_binding_consume_press(s); } +CF_INLINE bool stick_binding_consume_release(CF_StickBinding s) { return cf_stick_binding_consume_release(s); } +CF_INLINE void stick_binding_set_circular_deadzone(CF_StickBinding s, float deadzone) { cf_stick_binding_set_circular_deadzone(s, deadzone); } +CF_INLINE CF_V2 stick_binding_value_raw(CF_StickBinding s) { return cf_stick_binding_value_raw(s); } + +CF_INLINE void register_joypad_connect_callback(void (*fn)(int player_index, bool connected, void* udata), void* udata) { cf_register_joypad_connect_callback(fn, udata); } + +CF_INLINE float binding_get_deadzone() { return cf_binding_get_deadzone(); } +CF_INLINE void binding_set_deadzone(float deadzone) { cf_binding_set_deadzone(deadzone); } + +// Overloaded dispatch functions. +CF_INLINE bool binding_pressed(CF_ButtonBinding b) { return cf_button_binding_pressed(b); } +CF_INLINE bool binding_pressed(CF_AxisBinding a) { return cf_axis_binding_pressed(a); } +CF_INLINE bool binding_pressed(CF_StickBinding s) { return cf_stick_binding_pressed(s); } + +CF_INLINE bool binding_released(CF_ButtonBinding b) { return cf_button_binding_released(b); } +CF_INLINE bool binding_released(CF_AxisBinding a) { return cf_axis_binding_released(a); } +CF_INLINE bool binding_released(CF_StickBinding s) { return cf_stick_binding_released(s); } + +CF_INLINE float binding_value(CF_ButtonBinding b) { return cf_button_binding_value(b); } +CF_INLINE float binding_value(CF_AxisBinding a) { return cf_axis_binding_value(a); } +CF_INLINE CF_V2 binding_value(CF_StickBinding s) { return cf_stick_binding_value(s); } + +CF_INLINE float binding_sign(CF_ButtonBinding b) { return cf_button_binding_sign(b); } +CF_INLINE float binding_sign(CF_AxisBinding a) { return cf_axis_binding_sign(a); } +CF_INLINE CF_V2 binding_sign(CF_StickBinding s) { return cf_stick_binding_sign(s); } + +CF_INLINE bool binding_consume_press(CF_ButtonBinding b) { return cf_button_binding_consume_press(b); } +CF_INLINE bool binding_consume_press(CF_AxisBinding a) { return cf_axis_binding_consume_press(a); } +CF_INLINE bool binding_consume_press(CF_StickBinding s) { return cf_stick_binding_consume_press(s); } + +CF_INLINE bool binding_consume_release(CF_ButtonBinding b) { return cf_button_binding_consume_release(b); } +CF_INLINE bool binding_consume_release(CF_AxisBinding a) { return cf_axis_binding_consume_release(a); } +CF_INLINE bool binding_consume_release(CF_StickBinding s) { return cf_stick_binding_consume_release(s); } + +CF_INLINE float binding_value_raw(CF_ButtonBinding b) { return cf_button_binding_value_raw(b); } +CF_INLINE float binding_value_raw(CF_AxisBinding a) { return cf_axis_binding_value_raw(a); } +CF_INLINE CF_V2 binding_value_raw(CF_StickBinding s) { return cf_stick_binding_value_raw(s); } + +CF_INLINE void binding_set_deadzone(CF_ButtonBinding b, float deadzone) { cf_button_binding_set_deadzone(b, deadzone); } +CF_INLINE void binding_set_deadzone(CF_AxisBinding a, float deadzone) { cf_axis_binding_set_deadzone(a, deadzone); } + +CF_INLINE void destroy_binding(CF_ButtonBinding b) { cf_destroy_button_binding(b); } +CF_INLINE void destroy_binding(CF_AxisBinding a) { cf_destroy_axis_binding(a); } +CF_INLINE void destroy_binding(CF_StickBinding s) { cf_destroy_stick_binding(s); } + +} + +#endif // CF_CPP + +#endif // CF_BINDING_H diff --git a/include/cute_clipboard.h b/include/cute_clipboard.h index fe5041cff..36b70a772 100644 --- a/include/cute_clipboard.h +++ b/include/cute_clipboard.h @@ -34,6 +34,41 @@ CF_API char* CF_CALL cf_clipboard_get(void); */ CF_API CF_Result CF_CALL cf_clipboard_set(const char* string); +/** + * @function cf_clipboard_set_data + * @category input + * @brief Sets arbitrary data on the clipboard for the given MIME types. + * @param data Pointer to the data to copy onto the clipboard. + * @param size Size of the data in bytes. + * @param mime_types Array of MIME type strings (e.g. "image/png"). + * @param num_mime_types Number of MIME type strings. + * @return Returns success or an error result. + * @remarks The data is copied internally. The same data is returned for any of the listed MIME types. + * @related cf_clipboard_set_data cf_clipboard_get_data cf_clipboard_has_data + */ +CF_API CF_Result CF_CALL cf_clipboard_set_data(const void* data, int size, const char** mime_types, int num_mime_types); + +/** + * @function cf_clipboard_get_data + * @category input + * @brief Gets clipboard data for the given MIME type. + * @param mime_type The MIME type to request (e.g. "image/png"). + * @param size Output parameter for the size of the returned data in bytes. + * @return Returns a malloc'd buffer with the clipboard data. Caller must free with `cf_free`. Returns NULL on failure. + * @related cf_clipboard_set_data cf_clipboard_get_data cf_clipboard_has_data + */ +CF_API void* CF_CALL cf_clipboard_get_data(const char* mime_type, int* size); + +/** + * @function cf_clipboard_has_data + * @category input + * @brief Checks if the clipboard contains data for the given MIME type. + * @param mime_type The MIME type to check (e.g. "image/png"). + * @return Returns true if the clipboard has data for the MIME type. + * @related cf_clipboard_set_data cf_clipboard_get_data cf_clipboard_has_data + */ +CF_API bool CF_CALL cf_clipboard_has_data(const char* mime_type); + #ifdef __cplusplus } #endif // __cplusplus @@ -48,6 +83,9 @@ namespace Cute CF_INLINE char* clipboard_get() { return cf_clipboard_get(); } CF_INLINE CF_Result clipboard_set(const char* string) { return cf_clipboard_set(string); } +CF_INLINE CF_Result clipboard_set_data(const void* data, int size, const char** mime_types, int num_mime_types) { return cf_clipboard_set_data(data, size, mime_types, num_mime_types); } +CF_INLINE void* clipboard_get_data(const char* mime_type, int* size) { return cf_clipboard_get_data(mime_type, size); } +CF_INLINE bool clipboard_has_data(const char* mime_type) { return cf_clipboard_has_data(mime_type); } } diff --git a/include/cute_color.h b/include/cute_color.h index ce2bf81ae..2fa270340 100644 --- a/include/cute_color.h +++ b/include/cute_color.h @@ -49,20 +49,31 @@ typedef struct CF_Color */ typedef union CF_Pixel { - struct - { - /* @member The red component, from 0 to 255. */ - uint8_t r; - - /* @member The green component, from 0 to 255. */ - uint8_t g; - - /* @member The blue component, from 0 to 255. */ - uint8_t b; - - /* @member The alpha component (transparency/opaqueness), from 0 to 255. */ - uint8_t a; - } colors; + union { + struct + { + /* @member The red component, from 0 to 255. */ + uint8_t r; + + /* @member The green component, from 0 to 255. */ + uint8_t g; + + /* @member The blue component, from 0 to 255. */ + uint8_t b; + + /* @member The alpha component (transparency/opaqueness), from 0 to 255. */ + uint8_t a; + } colors; + + // For convenience. + struct + { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; + }; + }; /* @member A 32-bit unsigned integer containing the packed-bytes for all four RGBA components. */ uint32_t val; @@ -108,7 +119,7 @@ CF_INLINE CF_Color cf_make_color_rgb(uint8_t r, uint8_t g, uint8_t b) { CF_Color /** * @function cf_make_color_rgba * @category graphics - * @brief Returns a `CF_Color` made from RGB char inputs. + * @brief Returns a `CF_Color` made from RGBA char inputs. * @param r The red component from 0 to 255. * @param g The green component from 0 to 255. * @param b The blue component from 0 to 255. @@ -167,7 +178,7 @@ CF_INLINE CF_Pixel cf_make_pixel_rgb_f(float r, float g, float b) { CF_Pixel p; * @param r The red component from 0.0f to 1.0f. * @param g The green component from 0.0f to 1.0f. * @param b The blue component from 0.0f to 1.0f. - * @param b The alpha component from 0.0f to 1.0f. + * @param a The alpha component from 0.0f to 1.0f. * @related CF_Pixel cf_make_pixel_rgb_f cf_make_pixel_rgba_f cf_make_pixel_rgb cf_make_pixel_rgba cf_make_pixel_hex cf_make_pixel_hex_string */ CF_INLINE CF_Pixel cf_make_pixel_rgba_f(float r, float g, float b, float a) { CF_Pixel p; p.colors.r = (uint8_t)(r * 255.0f); p.colors.g = (uint8_t)(g * 255.0f); p.colors.b = (uint8_t)(b * 255.0f); p.colors.a = (uint8_t)(a * 255.0f); return p; } @@ -187,7 +198,7 @@ CF_INLINE CF_Pixel cf_make_pixel_rgb(uint8_t r, uint8_t g, uint8_t b) { CF_Pixel /** * @function cf_make_pixel_rgba * @category graphics - * @brief Returns a `CF_Pixel` made from RGB char inputs. + * @brief Returns a `CF_Pixel` made from RGBA char inputs. * @param r The red component from 0 to 255. * @param g The green component from 0 to 255. * @param b The blue component from 0 to 255. @@ -200,19 +211,32 @@ CF_INLINE CF_Pixel cf_make_pixel_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a * @function cf_make_pixel_hex * @category graphics * @brief Returns a `CF_Pixel` made from integer hex input. - * @param hex An integer value, e.g. 0xFFAACC11. - * @related CF_Pixel cf_make_pixel_rgb_f cf_make_pixel_rgba_f cf_make_pixel_rgb cf_make_pixel_rgba cf_make_pixel_hex cf_make_pixel_hex_string + * @param hex An integer value, e.g. 0xFFAACC. + * @remarks The opacity of the output pixel is set to 0xFF (fully opaque). + * @related CF_Pixel cf_make_pixel_rgb_f cf_make_pixel_rgba_f cf_make_pixel_rgb cf_make_pixel_rgba cf_make_pixel_hex cf_make_pixel_hex2 cf_make_pixel_hex_string */ -CF_INLINE CF_Pixel cf_make_pixel_hex(int hex) { return cf_make_pixel_rgba((uint8_t)((hex & 0xFF000000) >> 24), (uint8_t)((hex & 0x00FF0000) >> 16), (uint8_t)((hex & 0x0000FF00) >> 8), (uint8_t)(hex & 0x000000FF)); } +CF_INLINE CF_Pixel cf_make_pixel_hex(int hex) { return cf_make_pixel_rgba((uint8_t)((hex & 0xFF0000) >> 16), (uint8_t)((hex & 0x00FF00) >> 8), (uint8_t)(hex & 0x0000FF), 0xFF); } + +/** + * @function cf_make_pixel_hex2 + * @category graphics + * @brief Returns a `CF_Pixel` made from integer hex input. + * @param hex An integer value, e.g. 0xFFAACC, and alpha e.g. 0xFF. + * @related CF_Pixel cf_make_pixel_rgb_f cf_make_pixel_rgba_f cf_make_pixel_rgb cf_make_pixel_rgba cf_make_pixel_hex cf_make_pixel_hex2 cf_make_pixel_hex_string + */ +CF_INLINE CF_Pixel cf_make_pixel_hex2(int hex, int alpha) { return cf_make_pixel_rgba((uint8_t)((hex & 0xFF0000) >> 16), (uint8_t)((hex & 0x00FF00) >> 8), (uint8_t)(hex & 0x0000FF), (uint8_t)(alpha & 0xFF)); } /** * @function cf_make_pixel_hex_string * @category graphics * @brief Returns a `CF_Pixel` made from hex-value string, such as "#42f563" or "0x42f563FF". - * @param hex A hex-value string, such as "#42f563" or "0x42f563FF". - * @related CF_Pixel cf_make_pixel_rgb_f cf_make_pixel_rgba_f cf_make_pixel_rgb cf_make_pixel_rgba cf_make_pixel_hex cf_make_pixel_hex_string + * @param hex A hex-value string formatted as "#RRGGBB[AA]" or "0xRRGGBB[AA]", such as "#42f563" or "0x42f563FF". The last opacity segment is optional. + * @related CF_Pixel cf_make_pixel_rgb_f cf_make_pixel_rgba_f cf_make_pixel_rgb cf_make_pixel_rgba cf_make_pixel_hex cf_make_pixel_hex2 cf_make_pixel_hex_string */ -CF_INLINE CF_Pixel cf_make_pixel_hex_string(const char* hex) { return cf_make_pixel_hex((int)stohex(hex)); } +CF_INLINE CF_Pixel cf_make_pixel_hex_string(const char* hex) { + uint32_t rgba = (uint32_t)stohex(hex); + return cf_make_pixel_rgba((uint8_t)((rgba >> 24) & 0xFF), (uint8_t)((rgba >> 16) & 0xFF), (uint8_t)((rgba >> 8) & 0xFF), (uint8_t)(rgba & 0xFF)); +} /** * @function cf_mul_color @@ -231,7 +255,7 @@ CF_INLINE CF_Color cf_mul_color(CF_Color a, float s) { return cf_make_color_rgba * @brief Performs component-wise multiplication between two colors. * @param a The first color. * @param b The second color. - * @return For colors `a` and `a` the color `{ a.r*b.r, a.g*b.g, a.b*b.b, a.a*b.a }` is returned. + * @return For colors `a` and `b` the color `{ a.r*b.r, a.g*b.g, a.b*b.b, a.a*b.a }` is returned. * @related CF_Color cf_mul_color cf_mul_color2 cf_div_color cf_add_color cf_sub_color cf_abs_color cf_fract_color cf_splat_color cf_mod_color cf_clamp_color cf_clamp_color01 cf_color_lerp cf_color_premultiply */ CF_INLINE CF_Color cf_mul_color2(CF_Color a, CF_Color b) { return cf_make_color_rgba_f(a.r*b.r, a.g*b.g, a.b*b.b, a.a*b.a); } @@ -582,24 +606,22 @@ CF_INLINE CF_Color cf_pixel_to_color(CF_Pixel p) { return cf_make_color_rgba(p.c /** * @function cf_pixel_to_int_rgb * @category graphics - * @brief Converts an RGB `CF_Pixel` to an integer. + * @brief Converts a `CF_Pixel` to an integer in `0x00RRGGBB` format. * @param p The pixel. - * @return Returns an unsigned 32-bit integer of the packed pixel components. The first byte is the red component, the second byte is - * the green component, the third byte is the blue component, the fourth byte is 0xFF or full-alpha. + * @return Returns an unsigned 32-bit integer in `0x00RRGGBB` format. The alpha component is discarded. * @related cf_pixel_to_color cf_pixel_to_int_rgba cf_pixel_to_int_rgb cf_pixel_to_string */ -CF_INLINE uint32_t cf_pixel_to_int_rgb(CF_Pixel p) { return p.val | 0xFF000000; } +CF_INLINE uint32_t cf_pixel_to_int_rgb(CF_Pixel p) { return ((uint32_t)p.colors.r << 16) | ((uint32_t)p.colors.g << 8) | p.colors.b; } /** * @function cf_pixel_to_int_rgba * @category graphics - * @brief Converts an RGBA `CF_Pixel` to an integer. + * @brief Converts a `CF_Pixel` to an integer in `0xRRGGBBAA` format. * @param p The pixel. - * @return Returns an unsigned 32-bit integer of the packed pixel components. The first byte is the red component, the second byte is - * the green component, the third byte is the blue component, the fourth byte is the alpha component. + * @return Returns an unsigned 32-bit integer in `0xRRGGBBAA` format. * @related cf_pixel_to_color cf_pixel_to_int_rgba cf_pixel_to_int_rgb cf_pixel_to_string */ -CF_INLINE uint32_t cf_pixel_to_int_rgba(CF_Pixel p) { return p.val; } +CF_INLINE uint32_t cf_pixel_to_int_rgba(CF_Pixel p) { return ((uint32_t)p.colors.r << 24) | ((uint32_t)p.colors.g << 16) | ((uint32_t)p.colors.b << 8) | p.colors.a; } /** * @function cf_pixel_to_string @@ -609,7 +631,7 @@ CF_INLINE uint32_t cf_pixel_to_int_rgba(CF_Pixel p) { return p.val; } * @remarks Since this function dynamically allocates a Cute Framework C-string, it must be free'd up with `sfree` when you're done with it. * @related cf_pixel_to_color cf_pixel_to_int_rgba cf_pixel_to_int_rgb cf_pixel_to_string */ -CF_INLINE char* cf_pixel_to_string(CF_Pixel p) { char* s = NULL; return shex(s, p.val); } // Call `sfree` when done. +CF_INLINE char* cf_pixel_to_string(CF_Pixel p) { char* s = NULL; return shex(s, cf_pixel_to_int_rgba(p)); } // Call `sfree` when done. /** * @function cf_color_to_pixel @@ -618,39 +640,37 @@ CF_INLINE char* cf_pixel_to_string(CF_Pixel p) { char* s = NULL; return shex(s, * @param c The color. * @related cf_color_to_pixel cf_color_to_int_rgb cf_color_to_int_rgba cf_color_to_string */ -CF_INLINE CF_Pixel cf_color_to_pixel(CF_Color c) { CF_Pixel p; p.colors.r = (int)((uint8_t)(c.r * 255.0f)); p.colors.g = (int)((uint8_t)(c.g * 255.0f)); p.colors.b = (int)((uint8_t)(c.b * 255.0f)); p.colors.a = (int)((uint8_t)(c.a * 255.0f)); return p; } +CF_INLINE CF_Pixel cf_color_to_pixel(CF_Color c) { CF_Pixel p; p.colors.r = (uint8_t)(c.r * 255.0f); p.colors.g = (uint8_t)(c.g * 255.0f); p.colors.b = (uint8_t)(c.b * 255.0f); p.colors.a = (uint8_t)(c.a * 255.0f); return p; } /** * @function cf_color_to_int_rgb * @category graphics - * @brief Converts an RGBA `CF_Color` to an integer. + * @brief Converts a `CF_Color` to an integer in `0x00RRGGBB` format. * @param c The color. - * @return Returns an unsigned 32-bit integer of the packed pixel components. The first byte is the red component, the second byte is - * the green component, the third byte is the blue component, the fourth byte is 0xFF or full-alpha. + * @return Returns an unsigned 32-bit integer in `0x00RRGGBB` format. The alpha component is discarded. * @related cf_color_to_pixel cf_color_to_int_rgb cf_color_to_int_rgba cf_color_to_string */ -CF_INLINE uint32_t cf_color_to_int_rgb(CF_Color c) { return cf_color_to_pixel(c).val | 0xFF000000; } +CF_INLINE uint32_t cf_color_to_int_rgb(CF_Color c) { CF_Pixel p = cf_color_to_pixel(c); return ((uint32_t)p.colors.r << 16) | ((uint32_t)p.colors.g << 8) | p.colors.b; } /** * @function cf_color_to_int_rgba * @category graphics - * @brief Converts an RGB `CF_Color` to an integer. + * @brief Converts a `CF_Color` to an integer in `0xRRGGBBAA` format. * @param c The color. - * @return Returns an unsigned 32-bit integer of the packed pixel components. The first byte is the red component, the second byte is - * the green component, the third byte is the blue component, the fourth byte is the alpha component. + * @return Returns an unsigned 32-bit integer in `0xRRGGBBAA` format. * @related cf_color_to_pixel cf_color_to_int_rgb cf_color_to_int_rgba cf_color_to_string */ -CF_INLINE uint32_t cf_color_to_int_rgba(CF_Color c) { return cf_color_to_pixel(c).val; } +CF_INLINE uint32_t cf_color_to_int_rgba(CF_Color c) { CF_Pixel p = cf_color_to_pixel(c); return ((uint32_t)p.colors.r << 24) | ((uint32_t)p.colors.g << 16) | ((uint32_t)p.colors.b << 8) | p.colors.a; } /** * @function cf_color_to_string * @category graphics * @brief Converts a `CF_Color` to a dynamic string. Free it with `sfree` when done. - * @param p The pixel. + * @param c The color. * @remarks Since this function dynamically allocates a Cute Framework C-string, it must be free'd up with `sfree` when you're done with it. - * @related cf_pixel_to_color cf_pixel_to_int_rgba cf_pixel_to_int_rgb cf_pixel_to_string + * @related cf_color_to_pixel cf_color_to_int_rgb cf_color_to_int_rgba cf_color_to_string */ -CF_INLINE char* cf_color_to_string(CF_Color c) { char* s = NULL; return shex(s, cf_color_to_pixel(c).val); } +CF_INLINE char* cf_color_to_string(CF_Color c) { char* s = NULL; return shex(s, cf_color_to_int_rgba(c)); } /** * @function cf_color_invisible @@ -719,7 +739,7 @@ CF_INLINE CF_Color cf_color_yellow(void) { return cf_make_color_rgb_f(1.0f, 1.0f /** * @function cf_color_orange * @category graphics - * @brief Helper function to return a orange `CF_Color`. + * @brief Helper function to return an orange `CF_Color`. * @related cf_color_invisible cf_color_black cf_color_white cf_color_red cf_color_green cf_color_blue cf_color_yellow cf_color_orange cf_color_purple cf_color_grey cf_color_cyan cf_color_magenta */ CF_INLINE CF_Color cf_color_orange(void) { return cf_make_color_rgb_f(1.0f, 0.65f, 0.0f); } @@ -770,7 +790,7 @@ CF_INLINE CF_Color cf_color_brown(void) { return cf_make_color_rgb(150, 105, 25) * @brief Helper function to return a invisible `CF_Pixel`. * @related cf_pixel_invisible cf_pixel_black cf_pixel_white cf_pixel_red cf_pixel_green cf_pixel_blue cf_pixel_yellow cf_pixel_orange cf_pixel_purple cf_pixel_grey cf_pixel_cyan cf_pixel_magenta */ -CF_INLINE CF_Pixel cf_pixel_invisible(void) { return cf_make_pixel_hex(0); } +CF_INLINE CF_Pixel cf_pixel_invisible(void) { return cf_make_pixel_rgba(0, 0, 0, 0); } /** * @function cf_pixel_clear @@ -778,7 +798,7 @@ CF_INLINE CF_Pixel cf_pixel_invisible(void) { return cf_make_pixel_hex(0); } * @brief Helper function to return a invisible `CF_Pixel`. * @related cf_pixel_invisible cf_pixel_black cf_pixel_white cf_pixel_red cf_pixel_green cf_pixel_blue cf_pixel_yellow cf_pixel_orange cf_pixel_purple cf_pixel_grey cf_pixel_cyan cf_pixel_magenta */ -CF_INLINE CF_Pixel cf_pixel_clear(void) { return cf_make_pixel_hex(0); } +CF_INLINE CF_Pixel cf_pixel_clear(void) { return cf_make_pixel_rgba(0, 0, 0, 0); } /** * @function cf_pixel_black @@ -791,7 +811,7 @@ CF_INLINE CF_Pixel cf_pixel_black(void) { return cf_make_pixel_rgb(0, 0, 0); } /** * @function cf_pixel_white * @category graphics - * @brief Helper function to return a invisible `white`. + * @brief Helper function to return a white `CF_Pixel`. * @related cf_pixel_invisible cf_pixel_black cf_pixel_white cf_pixel_red cf_pixel_green cf_pixel_blue cf_pixel_yellow cf_pixel_orange cf_pixel_purple cf_pixel_grey cf_pixel_cyan cf_pixel_magenta */ CF_INLINE CF_Pixel cf_pixel_white(void) { return cf_make_pixel_rgb(255, 255, 255); } @@ -831,7 +851,7 @@ CF_INLINE CF_Pixel cf_pixel_yellow(void) { return cf_make_pixel_rgb(255, 255, 0) /** * @function cf_pixel_orange * @category graphics - * @brief Helper function to return a orange `CF_Pixel`. + * @brief Helper function to return an orange `CF_Pixel`. * @related cf_pixel_invisible cf_pixel_black cf_pixel_white cf_pixel_red cf_pixel_green cf_pixel_blue cf_pixel_yellow cf_pixel_orange cf_pixel_purple cf_pixel_grey cf_pixel_cyan cf_pixel_magenta */ CF_INLINE CF_Pixel cf_pixel_orange(void) { return cf_make_pixel_rgb(255, 165, 0); } @@ -893,14 +913,16 @@ CF_INLINE CF_Color make_color(float r, float g, float b, float a) { return cf_ma CF_INLINE CF_Color make_color(uint8_t r, uint8_t g, uint8_t b) { return cf_make_color_rgb(r, g, b); } CF_INLINE CF_Color make_color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return cf_make_color_rgba(r, g, b, a); } CF_INLINE CF_Color make_color(int hex) { return cf_make_color_hex(hex); } -CF_INLINE CF_Color make_color(const char* s) { return make_color((int)stohex(s)); } +CF_INLINE CF_Color make_color(int hex, int alpha) { return cf_make_color_hex2(hex, alpha); } +CF_INLINE CF_Color make_color(const char* s) { return cf_make_color_hex_string(s); } CF_INLINE CF_Pixel make_pixel(float r, float g, float b) { return cf_make_pixel_rgb_f(r, g, b); } CF_INLINE CF_Pixel make_pixel(float r, float g, float b, float a) { return cf_make_pixel_rgba_f(r, g, b, a); } CF_INLINE CF_Pixel make_pixel(uint8_t r, uint8_t g, uint8_t b) { return cf_make_pixel_rgb(r, g, b); } CF_INLINE CF_Pixel make_pixel(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { return cf_make_pixel_rgba(r, g, b, a); } CF_INLINE CF_Pixel make_pixel(int hex) { return cf_make_pixel_hex(hex); } -CF_INLINE CF_Pixel make_pixel(const char* hex) { return cf_make_pixel_hex((int)stohex(hex)); } +CF_INLINE CF_Pixel make_pixel(int hex, int alpha) { return cf_make_pixel_hex2(hex, alpha); } +CF_INLINE CF_Pixel make_pixel(const char* hex) { return cf_make_pixel_hex_string(hex); } CF_INLINE CF_Color operator*(CF_Color a, float s) { return cf_mul_color(a, s); } CF_INLINE CF_Color operator/(CF_Color a, float s) { return cf_div_color(a, s); } @@ -925,8 +947,13 @@ CF_INLINE float softlight(float base, float blend) { return cf_softlight(base, b CF_INLINE CF_Color overlay(CF_Color base, CF_Color blend) { return cf_overlay_color(base, blend); } CF_INLINE CF_Color softlight(CF_Color base, CF_Color blend) { return cf_softlight_color(base, blend); } -CF_INLINE CF_Pixel operator*(CF_Pixel a, int s) { return cf_mul_pixel(a, s); } -CF_INLINE CF_Pixel operator/(CF_Pixel a, int s) { return cf_div_pixel(a, s); } +CF_INLINE uint8_t mul_un8(int a, int b) { return cf_mul_un8(a, b); } +CF_INLINE uint8_t div_un8(int a, int b) { return cf_div_un8(a, b); } +CF_INLINE uint8_t add_un8(int a, int b) { return cf_add_un8(a, b); } +CF_INLINE uint8_t sub_un8(int a, int b) { return cf_sub_un8(a, b); } + +CF_INLINE CF_Pixel operator*(CF_Pixel a, int s) { return cf_mul_pixel(a, (uint8_t)s); } +CF_INLINE CF_Pixel operator/(CF_Pixel a, int s) { return cf_div_pixel(a, (uint8_t)s); } CF_INLINE CF_Pixel operator+(CF_Pixel a, CF_Pixel b) { return cf_add_pixel(a, b); } CF_INLINE CF_Pixel operator-(CF_Pixel a, CF_Pixel b) { return cf_sub_pixel(a, b); } CF_INLINE bool operator==(CF_Pixel a, CF_Pixel b) { return a.val == b.val; } @@ -935,14 +962,14 @@ CF_INLINE CF_Pixel lerp(CF_Pixel a, CF_Pixel b, uint8_t s) { return cf_pixel_ler CF_INLINE CF_Pixel premultiply(CF_Pixel p) { return cf_pixel_premultiply(p); } CF_INLINE CF_Color to_color(CF_Pixel p) { return cf_make_color_rgba(p.colors.r, p.colors.g, p.colors.b, p.colors.a); } -CF_INLINE uint32_t to_int_rgba(CF_Pixel p) { return p.val; } -CF_INLINE uint32_t to_int_rgb(CF_Pixel p) { return p.val | 0xFF000000; } -CF_INLINE String to_string(CF_Pixel p) { char* s = NULL; return shex(s, p.val); } +CF_INLINE uint32_t to_int_rgba(CF_Pixel p) { return cf_pixel_to_int_rgba(p); } +CF_INLINE uint32_t to_int_rgb(CF_Pixel p) { return cf_pixel_to_int_rgb(p); } +CF_INLINE String to_string(CF_Pixel p) { char* s = NULL; return shex(s, cf_pixel_to_int_rgba(p)); } CF_INLINE CF_Pixel to_pixel(CF_Color c) { return cf_color_to_pixel(c); } -CF_INLINE uint32_t to_int_rgb(CF_Color c) { return cf_color_to_pixel(c).val | 0xFF000000; } -CF_INLINE uint32_t to_int_rgba(CF_Color c) { return cf_color_to_pixel(c).val; } -CF_INLINE String to_string(CF_Color c) { char* s = NULL; return shex(s, cf_color_to_pixel(c).val); } +CF_INLINE uint32_t to_int_rgb(CF_Color c) { return cf_color_to_int_rgb(c); } +CF_INLINE uint32_t to_int_rgba(CF_Color c) { return cf_color_to_int_rgba(c); } +CF_INLINE String to_string(CF_Color c) { char* s = NULL; return shex(s, cf_color_to_int_rgba(c)); } CF_INLINE CF_Color color_invisible() { return cf_color_invisible(); } CF_INLINE CF_Color color_clear() { return cf_color_clear(); } @@ -957,6 +984,7 @@ CF_INLINE CF_Color color_purple() { return cf_color_purple(); } CF_INLINE CF_Color color_grey() { return cf_color_grey(); } CF_INLINE CF_Color color_cyan() { return cf_color_cyan(); } CF_INLINE CF_Color color_magenta() { return cf_color_magenta(); } +CF_INLINE CF_Color color_brown() { return cf_color_brown(); } CF_INLINE CF_Pixel pixel_invisible() { return cf_pixel_invisible(); } CF_INLINE CF_Pixel pixel_clear() { return cf_pixel_clear(); } @@ -971,6 +999,7 @@ CF_INLINE CF_Pixel pixel_purple() { return cf_pixel_purple(); } CF_INLINE CF_Pixel pixel_grey() { return cf_pixel_grey(); } CF_INLINE CF_Pixel pixel_cyan() { return cf_pixel_cyan(); } CF_INLINE CF_Pixel pixel_magenta() { return cf_pixel_magenta(); } +CF_INLINE CF_Pixel pixel_brown() { return cf_pixel_brown(); } } diff --git a/include/cute_coroutine.h b/include/cute_coroutine.h index 8b9a8b52f..c91067095 100644 --- a/include/cute_coroutine.h +++ b/include/cute_coroutine.h @@ -49,7 +49,7 @@ typedef void (CF_CoroutineFn)(CF_Coroutine co); * @param fn The entry point (function) the coroutine runs. * @param stack_size The size of the coroutine's stack to call functions and make local variables, the default is about 57344 bytes. * @param udata Can be `NULL`. Gets handed back to you when `cf_coroutine_get_udata` is called. - * @remarks The coroutine starts in a `COROUTINE_STATE_SUSPENDED`, and won't run until `cf_coroutine_resume` is first called. Free up the + * @remarks The coroutine starts in a `CF_COROUTINE_STATE_SUSPENDED`, and won't run until `cf_coroutine_resume` is first called. Free up the * coroutine with `cf_destroy_coroutine` when done. See `CF_Coroutine` for some more details. **IMPORTANT NOTE**: You should beef * up the stack_size to 1 or 2 MB (you may use e.g. `CF_MB * 2`) if you wish to call into APIs such as DirectX. A variety of APIs * and libraries out there have very deep or complex call stacks -- so the default size may cause stack overflows in such cases. @@ -62,7 +62,7 @@ CF_API CF_Coroutine CF_CALL cf_make_coroutine(CF_CoroutineFn* fn, int stack_size * @category coroutine * @brief Destroys a coroutine created by `cf_make_coroutine`. * @param co The coroutine. - * @remarks All objects on the coroutine's stack will get automically cleaned up. + * @remarks All objects on the coroutine's stack will get automatically cleaned up. * @related CF_Coroutine CF_CoroutineFn CF_CoroutineState cf_make_coroutine cf_destroy_coroutine cf_coroutine_state_to_string cf_coroutine_resume cf_coroutine_yield cf_coroutine_state cf_coroutine_get_udata cf_coroutine_push cf_coroutine_pop cf_coroutine_bytes_pushed cf_coroutine_space_remaining cf_coroutine_currently_running */ CF_API void CF_CALL cf_destroy_coroutine(CF_Coroutine co); @@ -205,7 +205,7 @@ CF_API size_t CF_CALL cf_coroutine_space_remaining(CF_Coroutine co); * @function cf_coroutine_currently_running * @category coroutine * @brief Returns the opaque pointer to the coroutine currently running. - * @remarks Each coroutine has ther `co` pointer handed to them as the only parameter in `CF_CoroutineFn`, so you likely + * @remarks Each coroutine has their `co` pointer handed to them as the only parameter in `CF_CoroutineFn`, so you likely * already have access to the coroutine pointer. However, this function is made available here for convenience. * For example, your coroutines may call into other functions -- instead of passing around a `co` pointer everywhere, * your helper functions can simply fetch the `CF_Coroutine` pointer themselves on an as-needed basis by calling diff --git a/include/cute_custom_sprite.h b/include/cute_custom_sprite.h new file mode 100644 index 000000000..753bf33e0 --- /dev/null +++ b/include/cute_custom_sprite.h @@ -0,0 +1,257 @@ +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +#ifndef CF_CUSTOM_SPRITE_H +#define CF_CUSTOM_SPRITE_H + +#include "cute_defines.h" +#include "cute_array.h" +#include "cute_map.h" +#include "cute_result.h" +#include "cute_color.h" +#include "cute_sprite.h" + +#include "cute/cute_png.h" + +//-------------------------------------------------------------------------------------------------- +// C API + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +/** + * @struct CF_Frame + * @category custom_sprite + * @brief Represents one frame of animation within a sprite. + * @related CF_Frame CF_Animation cf_make_custom_sprite_animation + */ +typedef struct CF_Frame +{ + /* @member Unique identifier for this frame's image. */ + uint64_t id; + + /* @member Number of seconds to display this frame. */ + float delay; +} CF_Frame; +// @end + +/** + * @struct CF_Animation + * @category custom_sprite + * @brief A named set of frames in sequence. + * @related CF_Frame CF_Animation cf_make_custom_sprite_animation + */ +typedef struct CF_Animation +{ + /* The name of the animation. */ + const char* name; + + /* The direction to play the animation. See `CF_PlayDirection`. */ + CF_PlayDirection play_direction; + + /* The frames of the animation. See `dyna`. */ + dyna CF_Frame* frames; + + /* For internal use. */ + int frame_offset; +} CF_Animation; +// @end + +typedef CF_MAP(const CF_Animation*) CF_AnimationTable; + +/** + * @struct CF_Png + * @category custom_sprite + * @brief A single image of raw pixels, loaded from the custom sprite cache. + * @remarks The custom sprite system is used to load png images from disk in order to make sprites. It + * is an advanced option for people who want lower level access to creating their own + * custom sprites, for example by loading sprites from their own custom animation format. If you just + * want a really easy way to load sprites, look at cute_sprite.h for the easy sprite API (search for + * `easy_make_sprite` or `make_sprite` functions). + * + * You will mostly just care about these three functions. + * + * cf_custom_sprite_load_png + * cf_custom_sprite_unload_png + * cf_make_custom_sprite + * + * It's a cache, which means it actually caches images loaded in RAM, so subsequent + * calls to `cf_custom_sprite_load_png` won't have to fetch the image off of disk, as long as + * the image is currently cached in RAM. + * @related CF_Png cf_png_defaults cf_custom_sprite_load_png cf_make_custom_sprite_animation cf_make_custom_sprite + */ +typedef struct CF_Png +{ + /* @member Path to the associated png on-disk. */ + const char* path; + + /* @member Unique identifier assigned by the cache. */ + uint64_t id; + + /* @member Pointer to the pixels of the png image. */ + CF_Pixel* pix; + + /* @member Width of the image in pixels. */ + int w; + + /* @member Height of the image in pixels. */ + int h; +} CF_Png; +// @end + +/** + * @function cf_png_defaults + * @category custom_sprite + * @brief Initialize an empty `CF_Png` struct. + * @return Returns an empty `CF_Png` struct. + * @related CF_Png cf_png_defaults cf_custom_sprite_load_png cf_make_custom_sprite_animation cf_make_custom_sprite + */ +CF_INLINE CF_Png cf_png_defaults(void) +{ + CF_Png result = { 0 }; + result.id = ~0; + return result; +} + +/** + * @function cf_custom_sprite_load_png + * @category custom_sprite + * @brief Returns an image `CF_Png` from the cache. + * @remarks If it does not exist in the cache, it is loaded from disk and placed into the cache. + * @related CF_Png cf_png_defaults cf_custom_sprite_load_png cf_make_custom_sprite_animation cf_make_custom_sprite + */ +CF_API CF_Result CF_CALL cf_custom_sprite_load_png(const char* png_path, CF_Png* png /*= NULL*/); + +/** + * @function cf_custom_sprite_load_png_from_memory + * @category custom_sprite + * @brief Returns an image `CF_Png` from the cache. + * @remarks If it does not exist in the cache, it is loaded from memory and placed into the cache. + * @related CF_Png cf_png_defaults cf_custom_sprite_load_png cf_make_custom_sprite_animation cf_make_custom_sprite + */ +CF_API CF_Result CF_CALL cf_custom_sprite_load_png_from_memory(const char* png_path, const void* memory, size_t size, CF_Png* png /*= NULL*/); + +/** + * @function cf_custom_sprite_unload_png + * @category custom_sprite + * @brief Unloads an image from the cache. + * @related CF_Png cf_png_defaults cf_custom_sprite_load_png cf_make_custom_sprite_animation cf_make_custom_sprite + */ +CF_API void CF_CALL cf_custom_sprite_unload_png(CF_Png png); + +/** + * @function cf_make_custom_sprite_animation + * @category custom_sprite + * @brief Constructs an animation out of an array of frames, along with their delays in milliseconds, or returns one from the cache if it already exists. + * @param name A unique name for the animation. + * @param pngs An array of pngs, see `CF_Png`. + * @param pngs_count The number of images in the `pngs` array. + * @param delays A delay in milliseconds for each frame of the animation. + * @param delays_count The number of floats in the `delays` array. This must match `pngs_count`. + * @return Returns a `CF_Animation`, representing a single animation sequence. + * @remarks Since png files do not contain any kind of animation information (frame delays or sets of frames) + * you must specify all of the animation data yourself in order to make sprites. To flip between different + * animations you can construct an animation table with `cf_make_custom_sprite_animation_table`, which returns + * a table of unique animation names to individual `CF_Animation`'s. + * @related CF_Png cf_custom_sprite_load_png cf_make_custom_sprite_animation cf_make_custom_sprite_animation_table cf_make_custom_sprite + */ +CF_API const CF_Animation* CF_CALL cf_make_custom_sprite_animation(const char* name, const CF_Png* pngs, int pngs_count, const float* delays, int delays_count); + +/** + * @function cf_custom_sprite_get_animation + * @category custom_sprite + * @brief Looks up an animation within the custom sprite cache by name. + * @param name A unique name for the animation. + * @return Returns a `CF_Animation`. + * @remarks You may use this function if you wish to implement your own sprites. However, it's recommended to instead use + * `cf_make_custom_sprite` and `CF_Sprite`. + * @related CF_Png cf_custom_sprite_load_png cf_make_custom_sprite_animation cf_make_custom_sprite_animation_table cf_make_custom_sprite + */ +CF_API const CF_Animation* CF_CALL cf_custom_sprite_get_animation(const char* name); + +/** + * @function cf_make_custom_sprite_animation_table + * @category custom_sprite + * @brief Constructs an animation table given an array of animations, or returns one from the cache if it already exists. + * @param sprite_name A unique name for the animation table. + * @return Returns a `CF_MAP(const CF_Animation*)` map of animation names to animations. + * @remarks The table returned is a map of animation names to individual `CF_Animation`'s. This is represents the guts of a sprite + * implementation. You may use this function if you wish to implement your own sprites. However, it's recommended to instead use + * `cf_make_custom_sprite` and `CF_Sprite`. + * @related CF_Png cf_custom_sprite_load_png cf_make_custom_sprite_animation cf_make_custom_sprite_animation_table cf_make_custom_sprite + */ +CF_API const CF_AnimationTable* CF_CALL cf_make_custom_sprite_animation_table(const char* sprite_name, const CF_Animation* const* animations, int animations_count); + +/** + * @function cf_custom_sprite_get_animation_table + * @category custom_sprite + * @brief Looks up an animation table within the custom sprite cache by name. + * @param sprite_name A unique name for the animation table. + * @return Returns a hashtable of unique sprite names to `CF_Animation`'s, see `cf_make_custom_sprite`. + * @related CF_Png cf_custom_sprite_load_png cf_make_custom_sprite_animation cf_make_custom_sprite_animation_table cf_make_custom_sprite + */ +CF_API const CF_AnimationTable* CF_CALL cf_custom_sprite_get_animation_table(const char* sprite_name); + +/** + * Makes a sprite. Each sprite must refer to an animation table previously constructed by `cf_make_custom_sprite_animation_table`. + * You can supply the pointer to the animation table yourself in `table`, or just leave it NULL. + * If table is `NULL` then `sprite_name` should be the path to your png file instead of the sprite's actual name. + */ +/** + * @function cf_make_custom_sprite + * @category custom_sprite + * @brief Constructs an `CF_Sprite` out of your own custom built animation table. + * @param sprite_name A unique name for the sprite. + * @param table An animation table. + * @return Returns a `CF_Sprite`, ready to use in e.g. `draw_sprite`. + * @related CF_Png cf_custom_sprite_load_png cf_make_custom_sprite_animation cf_make_custom_sprite_animation_table cf_make_custom_sprite + */ +CF_API CF_Sprite CF_CALL cf_make_custom_sprite(const char* sprite_name, const CF_AnimationTable* table /*= NULL*/); + +/** + * @function cf_animation_add_frame + * @category custom_sprite + * @brief Adds a frame to an animation. + * @param animation The animation. + * @param frame The frame. + * @remarks You can use this function to build your own animations in a custom manner. It's recommend to just use `cf_make_sprite`, which + * loads a full sprite out of a .ase file. But, this function provides another low-level option if desired. + * @related CF_Animation CF_Frame cf_make_custom_sprite_animation + */ +CF_INLINE void cf_animation_add_frame(CF_Animation* animation, CF_Frame frame) { CF_ASSERT(animation); apush(animation->frames, frame); } + +#ifdef __cplusplus +} +#endif // __cplusplus + +//-------------------------------------------------------------------------------------------------- +// C++ API + +#ifdef CF_CPP + +#include "cute_array.h" + +namespace Cute +{ + +CF_INLINE CF_Result custom_sprite_load_png(const char* png_path, CF_Png* png = NULL) { return cf_custom_sprite_load_png(png_path, (CF_Png*)png); } +CF_INLINE CF_Result custom_sprite_load_png_from_memory(const char* png_path, const void* memory, size_t size, CF_Png* png = NULL) { return cf_custom_sprite_load_png_from_memory(png_path, memory, size, png); } +CF_INLINE void custom_sprite_unload_png(CF_Png png) { cf_custom_sprite_unload_png(png); } +CF_API const CF_Animation* CF_CALL make_custom_sprite_animation(const char* name, const Array& pngs, const Array& delays); +CF_INLINE const CF_Animation* custom_sprite_get_animation(const char* name) { return cf_custom_sprite_get_animation(name); } +CF_API const CF_AnimationTable* CF_CALL make_custom_sprite_animation_table(const char* sprite_name, const Array& animations); +CF_INLINE const CF_AnimationTable* custom_sprite_get_animation_table(const char* sprite_name) { return cf_custom_sprite_get_animation_table(sprite_name); } +CF_INLINE CF_Sprite make_custom_sprite(const char* sprite_name, const CF_AnimationTable* table = NULL) { return cf_make_custom_sprite(sprite_name, table); } +CF_INLINE void animation_add_frame(CF_Animation* animation, CF_Frame frame) { cf_animation_add_frame(animation, frame); } +CF_INLINE void animation_add_frame(CF_Animation& animation, CF_Frame frame) { cf_animation_add_frame(&animation, frame); } + +} + +#endif // CF_CPP + +#endif // CF_CUSTOM_SPRITE_H diff --git a/include/cute_defer.h b/include/cute_defer.h index 7259a4d65..8a822ac75 100644 --- a/include/cute_defer.h +++ b/include/cute_defer.h @@ -5,6 +5,9 @@ This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info */ +#ifndef CF_DEFER_H +#define CF_DEFER_H + #include "cute_defines.h" #ifdef CF_CPP @@ -61,3 +64,5 @@ static CF_ScopeExit s_create_scope_helper(T func) #define CF_DEFER(L) const auto& CF_TOKEN_PASTE(CF_ScopeExit, __LINE__) = s_create_scope_helper([&]() { L; }) #endif // CF_CPP + +#endif // CF_DEFER_H diff --git a/include/cute_defines.h b/include/cute_defines.h index 0103ac6dc..c25d76d05 100644 --- a/include/cute_defines.h +++ b/include/cute_defines.h @@ -93,6 +93,8 @@ # define CF_API #endif +#define CK_API CF_API + #ifdef CF_WINDOWS # define CF_CALL __cdecl #else @@ -214,4 +216,18 @@ CF_API void CF_CALL cf_set_assert_handler(cf_assert_fn* assert_fn); } #endif // __cplusplus +//-------------------------------------------------------------------------------------------------- +// C++ API + +#ifdef CF_CPP + +namespace Cute +{ + +CF_INLINE void set_assert_handler(cf_assert_fn* assert_fn) { cf_set_assert_handler(assert_fn); } + +} + +#endif // CF_CPP + #endif // CF_DEFINES_H diff --git a/include/cute_doubly_list.h b/include/cute_doubly_list.h index 50145b06c..d0a06bc6d 100644 --- a/include/cute_doubly_list.h +++ b/include/cute_doubly_list.h @@ -102,7 +102,7 @@ typedef struct CF_List /** * @function cf_list_init_node * @category list - * @brief Intializes a node. + * @brief Initializes a node. * @param node The node. * @remarks As this list is circular, each node is initialized to point to itself. * @related CF_ListNode CF_List CF_LIST_NODE CF_LIST_HOST cf_list_init_node cf_list_init cf_list_push_front cf_list_push_back cf_list_remove cf_list_pop_front cf_list_pop_back cf_list_empty cf_list_begin cf_list_end cf_list_front cf_list_back @@ -116,7 +116,7 @@ CF_INLINE void cf_list_init_node(CF_ListNode* node) /** * @function cf_list_init * @category list - * @brief Intializes a list. + * @brief Initializes a list. * @param list The list. * @remarks As an optimization the list contains a dummy node inside of it. To traverse this list, use `cf_list_begin` and * `cf_list_end` in a for loop. See `cf_list_begin` for an example. diff --git a/include/cute_draw.h b/include/cute_draw.h index d8b46db96..6a85705d9 100644 --- a/include/cute_draw.h +++ b/include/cute_draw.h @@ -32,23 +32,23 @@ CF_API void CF_CALL cf_draw_sprite(const CF_Sprite* sprite); /** -* @function cf_draw_sprite_9_slice -* @category draw -* @brief Draws a sprite using 9 slice, the top, left, right and bottom sides will be stretched. -* if no center patch uvs are defined then this defaults back to cf_draw_sprite -* @param sprite The sprite. -* @related cf_draw_sprite cf_draw_sprite_9_slice cf_draw_sprite_9_slice_tiled cf_draw_quad cf_draw_to cf_app_draw_onto_screen -*/ + * @function cf_draw_sprite_9_slice + * @category draw + * @brief Draws a sprite using 9 slice, the top, left, right and bottom sides will be stretched. + * if no center patch uvs are defined then this defaults back to cf_draw_sprite + * @param sprite The sprite. + * @related cf_draw_sprite cf_draw_sprite_9_slice cf_draw_sprite_9_slice_tiled cf_draw_quad cf_draw_to cf_app_draw_onto_screen + */ CF_API void CF_CALL cf_draw_sprite_9_slice(const CF_Sprite* sprite); /** -* @function cf_draw_sprite_9_slice_tiled -* @category draw -* @brief Draws a sprite using 9 slice, the top, left, right and bottom will be tiled. -* if no center patch uvs are defined then this defaults back to cf_draw_sprite -* @param sprite The sprite. -* @related cf_draw_sprite cf_draw_sprite_9_slice cf_draw_sprite_9_slice_tiled cf_draw_quad cf_draw_to cf_app_draw_onto_screen -*/ + * @function cf_draw_sprite_9_slice_tiled + * @category draw + * @brief Draws a sprite using 9 slice, the top, left, right and bottom will be tiled. + * if no center patch uvs are defined then this defaults back to cf_draw_sprite + * @param sprite The sprite. + * @related cf_draw_sprite cf_draw_sprite_9_slice cf_draw_sprite_9_slice_tiled cf_draw_quad cf_draw_to cf_app_draw_onto_screen + */ CF_API void CF_CALL cf_draw_sprite_9_slice_tiled(const CF_Sprite* sprite); /** @@ -113,76 +113,76 @@ CF_API void CF_CALL cf_draw_quad_fill(CF_Aabb bb, float chubbiness); CF_API void CF_CALL cf_draw_quad_fill2(CF_V2 p0, CF_V2 p1, CF_V2 p2, CF_V2 p3, float chubbiness); /** -* @function cf_draw_box -* @category draw -* @brief Draws a quad wireframe. -* @param bb The AABB (Axis-Aligned Bounding Box) to draw a quad over. -* @param thickness The thickness of each line to draw. -* @param chubbiness Inflates the shape, similar to corner-rounding. Makes the shape chubbier. -* @remarks This is an alias for `cf_draw_quad` -* @related cf_draw_quad cf_draw_quad2 cf_draw_quad_fill cf_draw_quad_fill2 -*/ + * @function cf_draw_box + * @category draw + * @brief Draws a quad wireframe. + * @param bb The AABB (Axis-Aligned Bounding Box) to draw a quad over. + * @param thickness The thickness of each line to draw. + * @param chubbiness Inflates the shape, similar to corner-rounding. Makes the shape chubbier. + * @remarks This is an alias for `cf_draw_quad` + * @related cf_draw_quad cf_draw_quad2 cf_draw_quad_fill cf_draw_quad_fill2 + */ CF_INLINE void cf_draw_box(CF_Aabb bb, float thickness, float chubbiness) { cf_draw_quad(bb, thickness, chubbiness); } /** -* @function cf_draw_box_rounded -* @category draw -* @brief Draws a quad wireframe with rounded corners. -* @param bb The AABB (Axis-Aligned Bounding Box) to draw a quad over. -* @param thickness The thickness of each line to draw. -* @param radius The radius to use for rounding. -* @related cf_draw_quad cf_draw_quad2 cf_draw_quad_fill cf_draw_quad_fill2 -*/ + * @function cf_draw_box_rounded + * @category draw + * @brief Draws a quad wireframe with rounded corners. + * @param bb The AABB (Axis-Aligned Bounding Box) to draw a quad over. + * @param thickness The thickness of each line to draw. + * @param radius The radius to use for rounding. + * @related cf_draw_quad cf_draw_quad2 cf_draw_quad_fill cf_draw_quad_fill2 + */ CF_API void CF_CALL cf_draw_box_rounded(CF_Aabb bb, float thickness, float radius); /** -* @function cf_draw_box2 -* @category draw -* @brief Draws a quad wireframe. -* @param p0 A corner of the quad. -* @param p1 A corner of the quad. -* @param p2 A corner of the quad. -* @param p3 A corner of the quad. -* @param thickness The thickness of each line to draw. -* @param chubbiness Inflates the shape, similar to corner-rounding. Makes the shape chubbier. -* @remarks All points `p0` through `p3` are encouraged to be in counter-clockwise order. This is an alias for `cf_draw_quad2` -* @related cf_draw_quad cf_draw_quad2 cf_draw_quad_fill cf_draw_quad_fill2 -*/ + * @function cf_draw_box2 + * @category draw + * @brief Draws a quad wireframe. + * @param p0 A corner of the quad. + * @param p1 A corner of the quad. + * @param p2 A corner of the quad. + * @param p3 A corner of the quad. + * @param thickness The thickness of each line to draw. + * @param chubbiness Inflates the shape, similar to corner-rounding. Makes the shape chubbier. + * @remarks All points `p0` through `p3` are encouraged to be in counter-clockwise order. This is an alias for `cf_draw_quad2` + * @related cf_draw_quad cf_draw_quad2 cf_draw_quad_fill cf_draw_quad_fill2 + */ CF_INLINE void cf_draw_box2(CF_V2 p0, CF_V2 p1, CF_V2 p2, CF_V2 p3, float thickness, float chubbiness) { cf_draw_quad2(p0, p1, p2, p3, thickness, chubbiness); } /** -* @function cf_draw_box_fill -* @category draw -* @brief Draws a quad. -* @param bb The AABB (Axis-Aligned Bounding Box) to draw a quad over. -* @param chubbiness Inflates the shape, similar to corner-rounding. Makes the shape chubbier. -* @remarks This is an alias for `cf_draw_quad_fill` -* @related cf_draw_quad cf_draw_quad2 cf_draw_quad_fill cf_draw_quad_fill2 -*/ + * @function cf_draw_box_fill + * @category draw + * @brief Draws a quad. + * @param bb The AABB (Axis-Aligned Bounding Box) to draw a quad over. + * @param chubbiness Inflates the shape, similar to corner-rounding. Makes the shape chubbier. + * @remarks This is an alias for `cf_draw_quad_fill` + * @related cf_draw_quad cf_draw_quad2 cf_draw_quad_fill cf_draw_quad_fill2 + */ CF_INLINE void cf_draw_box_fill(CF_Aabb bb, float chubbiness) { cf_draw_quad_fill(bb, chubbiness); } /** -* @function cf_draw_box_fill2 -* @category draw -* @brief Draws a quad. -* @param p0 A corner of the quad. -* @param p1 A corner of the quad. -* @param p2 A corner of the quad. -* @param p3 A corner of the quad. -* @param chubbiness Inflates the shape, similar to corner-rounding. Makes the shape chubbier. -* @remarks All points `p0` through `p3` are encouraged to be in counter-clockwise order. This is an alias for `cf_draw_quad_fill2` -* @related cf_draw_quad cf_draw_quad2 cf_draw_quad_fill cf_draw_quad_fill2 -*/ + * @function cf_draw_box_fill2 + * @category draw + * @brief Draws a quad. + * @param p0 A corner of the quad. + * @param p1 A corner of the quad. + * @param p2 A corner of the quad. + * @param p3 A corner of the quad. + * @param chubbiness Inflates the shape, similar to corner-rounding. Makes the shape chubbier. + * @remarks All points `p0` through `p3` are encouraged to be in counter-clockwise order. This is an alias for `cf_draw_quad_fill2` + * @related cf_draw_quad cf_draw_quad2 cf_draw_quad_fill cf_draw_quad_fill2 + */ CF_INLINE void cf_draw_box_fill2(CF_V2 p0, CF_V2 p1, CF_V2 p2, CF_V2 p3, float chubbiness) { cf_draw_quad_fill2(p0, p1, p2, p3, chubbiness); } /** -* @function cf_draw_box_rounded_fill -* @category draw -* @brief Draws a quad with rounded corners. -* @param bb The AABB (Axis-Aligned Bounding Box) to draw a quad over. -* @param radius The radius to use for rounding. -* @related cf_draw_quad cf_draw_quad2 cf_draw_quad_fill cf_draw_quad_fill2 -*/ + * @function cf_draw_box_rounded_fill + * @category draw + * @brief Draws a quad with rounded corners. + * @param bb The AABB (Axis-Aligned Bounding Box) to draw a quad over. + * @param radius The radius to use for rounding. + * @related cf_draw_quad cf_draw_quad2 cf_draw_quad_fill cf_draw_quad_fill2 + */ CF_API void CF_CALL cf_draw_box_rounded_fill(CF_Aabb bb, float radius); /** @@ -311,7 +311,6 @@ CF_API void CF_CALL cf_draw_line(CF_V2 p0, CF_V2 p1, float thickness); * @param count The number of points in the polyline. * @param thickness The thickness of the line to draw. * @param loop True to connect the first and last point to form a loop. False otherwise. - * @param bevel_count The number of edges used to smooth corners. * @related cf_draw_line cf_draw_polyline cf_draw_bezier_line cf_draw_bezier_line2 cf_draw_arrow cf_draw_polygon_fill */ CF_API void CF_CALL cf_draw_polyline(const CF_V2* points, int count, float thickness, bool loop); @@ -377,7 +376,7 @@ CF_API void CF_CALL cf_draw_bezier_line2(CF_V2 a, CF_V2 c0, CF_V2 c1, CF_V2 b, i * @param b The end point. * @param thickness The thickness of the line to draw. * @param arrow_width The width of the arrow to draw. - * @remarks This function is intended only for debug purposes. It's implemented in naive way so the + * @remarks This function is intended only for debug purposes. It's implemented in a naive way so the * arrow shaft will overdraw atop the arrow head. This will become visible if the arrow is * drawn with any transparency. * @related cf_draw_line cf_draw_polyline cf_draw_bezier_line cf_draw_bezier_line2 cf_draw_arrow @@ -444,65 +443,38 @@ CF_API CF_Color CF_CALL cf_draw_pop_color(void); CF_API CF_Color CF_CALL cf_draw_peek_color(void); /** - * @function cf_draw_push_antialias - * @category draw - * @brief Pushes whether or not to apply antialiasing. - * @param antialias True to antialias, false otherwise. - * @remarks Various shape drawing functions can be drawn in antialiased mode, or in plain mode. Antialiasing is slightly slower, - * but looks much smoother. - * @related cf_draw_push_antialias cf_draw_pop_antialias cf_draw_peek_antialias - */ -CF_API void CF_CALL cf_draw_push_antialias(bool antialias); - -/** - * @function cf_draw_pop_antialias - * @category draw - * @brief Pops and returns the last antialias state. - * @remarks Various shape drawing functions can be drawn in antialiased mode, or in plain mode. Antialiasing is slightly slower, - * but looks much smoother. - * @related cf_draw_push_antialias cf_draw_pop_antialias cf_draw_peek_antialias - */ -CF_API bool CF_CALL cf_draw_pop_antialias(void); - -/** - * @function cf_draw_peek_antialias - * @category draw - * @brief Returns the last antialias state. - * @remarks Various shape drawing functions can be drawn in antialiased mode, or in plain mode. Antialiasing is slightly slower, - * but looks much smoother. - * @related cf_draw_push_antialias cf_draw_pop_antialias cf_draw_peek_antialias - */ -CF_API bool CF_CALL cf_draw_peek_antialias(void); - -/** - * @function cf_draw_push_antialias_scale + * @function cf_draw_push_shape_aa * @category draw - * @brief Returns the last antialias scale. - * @remarks Antialias scale controls how much antialiasing will be used. A larger number makes the borders of shapes blurry. - * The number must be greater than 0, but probably not more than 2 or 3 for most cases. The default is 1.5. - * @related cf_draw_push_antialias_scale cf_draw_pop_antialias_scale cf_draw_peek_antialias_scale + * @brief Pushes the shape antialias scale. + * @param aa Antialias scale. 0 turns off antialiasing, non-zero values enable antialiasing at that scale. Default is 1.5. + * @remarks Shape drawing functions can be drawn in antialiased mode, or in plain mode. Antialiasing is slightly slower, + * but looks much smoother. A larger number makes the borders of shapes blurrier. The number probably shouldn't + * be more than 2 or 3 for most cases. This only affects shapes, not text or sprites. + * @related cf_draw_push_shape_aa cf_draw_pop_shape_aa cf_draw_peek_shape_aa */ -CF_API void CF_CALL cf_draw_push_antialias_scale(float scale); +CF_API void CF_CALL cf_draw_push_shape_aa(float aa); /** - * @function cf_draw_pop_antialias_scale + * @function cf_draw_pop_shape_aa * @category draw - * @brief Pops and returns the last antialias scale. - * @remarks Antialias scale controls how much antialiasing will be used. A larger number makes the borders of shapes blurry. - * The number must be greater than 0, but probably not more than 2 or 3 for most cases. The default is 1.5. - * @related cf_draw_push_antialias_scale cf_draw_pop_antialias_scale cf_draw_peek_antialias_scale + * @brief Pops and returns the last shape antialias scale. + * @remarks Shape drawing functions can be drawn in antialiased mode, or in plain mode. Antialiasing is slightly slower, + * but looks much smoother. A larger number makes the borders of shapes blurrier. Default is 1.5, 0 turns off antialiasing. + * This only affects shapes, not text or sprites. + * @related cf_draw_push_shape_aa cf_draw_pop_shape_aa cf_draw_peek_shape_aa */ -CF_API float CF_CALL cf_draw_pop_antialias_scale(void); +CF_API float CF_CALL cf_draw_pop_shape_aa(void); /** - * @function cf_draw_peek_antialias_scale + * @function cf_draw_peek_shape_aa * @category draw - * @brief Returns the last antialias scale. - * @remarks Antialias scale controls how much antialiasing will be used. A larger number makes the borders of shapes blurry. - * The number must be greater than 0, but probably not more than 2 or 3 for most cases. The default is 1.5. - * @related cf_draw_push_antialias_scale cf_draw_pop_antialias_scale cf_draw_peek_antialias_scale + * @brief Returns the last shape antialias scale. + * @remarks Shape drawing functions can be drawn in antialiased mode, or in plain mode. Antialiasing is slightly slower, + * but looks much smoother. A larger number makes the borders of shapes blurrier. Default is 1.5, 0 turns off antialiasing. + * This only affects shapes, not text or sprites. + * @related cf_draw_push_shape_aa cf_draw_pop_shape_aa cf_draw_peek_shape_aa */ -CF_API float CF_CALL cf_draw_peek_antialias_scale(void); +CF_API float CF_CALL cf_draw_peek_shape_aa(void); /** * @function cf_draw_push_vertex_attributes @@ -542,6 +514,73 @@ CF_API CF_Color CF_CALL cf_draw_pop_vertex_attributes(void); */ CF_API CF_Color CF_CALL cf_draw_peek_vertex_attributes(void); +/** + * @function cf_draw_push_tri_colors + * @category draw + * @brief Pushes per-vertex colors for triangle drawing. + * @param c0 Color for the first vertex (p0). + * @param c1 Color for the second vertex (p1). + * @param c2 Color for the third vertex (p2). + * @remarks When active, triangles drawn with `cf_draw_tri_fill` will interpolate colors + * across the triangle surface between c0, c1, and c2 at vertices p0, p1, and p2. + * This only works when chubbiness is 0 and antialiasing is disabled. + * @related cf_draw_push_tri_colors cf_draw_pop_tri_colors cf_draw_peek_tri_colors cf_draw_tri_fill + */ +CF_API void CF_CALL cf_draw_push_tri_colors(CF_Color c0, CF_Color c1, CF_Color c2); + +/** + * @function cf_draw_pop_tri_colors + * @category draw + * @brief Pops the per-vertex triangle color state. + * @related cf_draw_push_tri_colors cf_draw_pop_tri_colors cf_draw_peek_tri_colors cf_draw_tri_fill + */ +CF_API void CF_CALL cf_draw_pop_tri_colors(void); + +/** + * @function cf_draw_peek_tri_colors + * @category draw + * @brief Returns the current per-vertex triangle color state. + * @param c0 Output pointer for color of first vertex (can be NULL). + * @param c1 Output pointer for color of second vertex (can be NULL). + * @param c2 Output pointer for color of third vertex (can be NULL). + * @related cf_draw_push_tri_colors cf_draw_pop_tri_colors cf_draw_peek_tri_colors cf_draw_tri_fill + */ +CF_API void CF_CALL cf_draw_peek_tri_colors(CF_Color* c0, CF_Color* c1, CF_Color* c2); + +/** + * @function cf_draw_push_tri_attributes + * @category draw + * @brief Pushes per-vertex attributes for triangle drawing. + * @param a0 Attributes for the first vertex (p0). + * @param a1 Attributes for the second vertex (p1). + * @param a2 Attributes for the third vertex (p2). + * @remarks When active, triangles drawn with `cf_draw_tri_fill` will interpolate attributes + * across the triangle surface between a0, a1, and a2 at vertices p0, p1, and p2. + * This is useful for custom shaders that need per-vertex data (UVs, blend weights, etc.). + * This only works when chubbiness is 0 and antialiasing is disabled. + * @related cf_draw_push_tri_attributes cf_draw_pop_tri_attributes cf_draw_peek_tri_attributes cf_draw_tri_fill + */ +CF_API void CF_CALL cf_draw_push_tri_attributes(CF_Color a0, CF_Color a1, CF_Color a2); + +/** + * @function cf_draw_pop_tri_attributes + * @category draw + * @brief Pops the per-vertex triangle attribute state. + * @related cf_draw_push_tri_attributes cf_draw_pop_tri_attributes cf_draw_peek_tri_attributes cf_draw_tri_fill + */ +CF_API void CF_CALL cf_draw_pop_tri_attributes(void); + +/** + * @function cf_draw_peek_tri_attributes + * @category draw + * @brief Returns the current per-vertex triangle attribute state. + * @param a0 Output pointer for attributes of first vertex (can be NULL). + * @param a1 Output pointer for attributes of second vertex (can be NULL). + * @param a2 Output pointer for attributes of third vertex (can be NULL). + * @related cf_draw_push_tri_attributes cf_draw_pop_tri_attributes cf_draw_peek_tri_attributes cf_draw_tri_fill + */ +CF_API void CF_CALL cf_draw_peek_tri_attributes(CF_Color* a0, CF_Color* a1, CF_Color* a2); + /** * @struct CF_Vertex * @category draw @@ -554,11 +593,11 @@ CF_API CF_Color CF_CALL cf_draw_peek_vertex_attributes(void); */ typedef struct CF_Vertex { - /* @member World space position. */ + /* @member World space position (in_pos_uv.xy). */ CF_V2 p; - /* @member "Homogenous" position transformed by the camera. */ - CF_V2 posH; + /* @member For internal use -- For sprite rendering (in_pos_uv.zw). */ + CF_V2 uv; /* @member For internal use -- For signed-distance functions for rendering shapes. */ int n; @@ -566,35 +605,35 @@ typedef struct CF_Vertex /* @member For internal use -- For signed-distance functions for rendering shapes. */ CF_V2 shape[8]; - /* @member For internal use -- For sprite rendering. */ - CF_V2 uv; - /* @member Color for rendering shapes (ignored for sprites). */ CF_Pixel color; - /* @member For internal use -- For applying "chubbiness" factor for shapes, or radii on circle/capsule. */ + /* @member For internal use -- For applying "chubbiness" factor for shapes, or radii on circle/capsule (in_shape.x). */ float radius; - /* @member For internal use -- For shape rendering for border style stroke rendering (no fill). */ + /* @member For internal use -- For shape rendering for border style stroke rendering (in_shape.y). */ float stroke; - /* @member For internal use -- Factor for the size of antialiasing. */ + /* @member For internal use -- Factor for the size of antialiasing (in_shape.z). */ float aa; - /* @member For internal use -- The type of shape to be rendered, used by the signed-distance functions within CF's internal fragment shader. */ - uint8_t type; + /* @member For internal use -- Shape type for SDF fragment shader (in_shape.w). Stored as float (0-6). */ + float type; - /* @member Used for the alpha-component (transparency). */ - uint8_t alpha; + /* @member Used for the alpha-component, transparency (in_blend_posH.x). Stored as float 0.0-1.0. */ + float alpha; - /* @member For internal use -- Whether or not to render shapes as filled or stroked. */ - uint8_t fill; + /* @member For internal use -- Whether or not to render shapes as filled or stroked (in_blend_posH.y). 0.0 or 1.0. */ + float fill; - /* @member For internal use -- Currently unused but fills needed padding space. */ - uint8_t unused; + /* @member "Homogenous" position transformed by the camera (in_blend_posH.zw). */ + CF_V2 posH; /* @member Four general purpose floats passed into custom user shaders. */ CF_Color attributes; + + /* @member UV bounds for sprite/text glyphs. Zero for shapes. Packed as (uv_min.x, uv_min.y, uv_max.x, uv_max.y). */ + float uv_bounds[4]; } CF_Vertex; // @end @@ -609,8 +648,8 @@ typedef struct CF_Vertex * * Call `cf_set_vertex_callback` to setup your callback. * - * There is no adjecancy info provided. If you need to know which triangles connect to others you - * should probably redesign your feature to not require adjecancy information, or use your own custom + * There is no adjacency info provided. If you need to know which triangles connect to others you + * should probably redesign your feature to not require adjacency information, or use your own custom * rendering solution. With a custom solution you may use low-level graphics in cute_graphics.h, where * any adjacency info can be controlled 100% by you a-priori. * @related CF_Vertex CF_VertexFn cf_set_vertex_callback @@ -767,7 +806,7 @@ CF_API float CF_CALL cf_peek_text_wrap_width(void); /** * @function cf_push_text_vertical_layout * @category text - * @brief Pushes a whether or not to layout text vertically (as opposed to the default or horizontally). + * @brief Pushes whether or not to layout text vertically (as opposed to the default of horizontally). * @param layout_vertically True to layout vertically, false otherwise. * @related cf_make_font cf_push_font cf_push_text_vertical_layout cf_pop_text_vertical_layout cf_peek_text_vertical_layout cf_draw_text */ @@ -1024,7 +1063,7 @@ typedef bool (CF_TextEffectFn)(CF_TextEffect* fx); * When registering a custom text effect, any parameters in the string will be stored for you * automatically. You only need to fetch them with the appropriate cf_text_effect_get*** function. * Note: You can also setup parameters for markup as strings, not just numbers/colors. Example: `blue text`, - * where the `color` markup contains a parameter called `metadata` and a strinf value of `"Just some string."`. + * where the `color` markup contains a parameter called `metadata` and a string value of `"Just some string."`. * @related CF_TextEffect CF_TextEffectFn cf_text_effect_register cf_text_effect_get_number cf_text_effect_get_color cf_text_effect_get_string */ CF_API void CF_CALL cf_text_effect_register(const char* name, CF_TextEffectFn* fn); @@ -1088,7 +1127,7 @@ typedef struct CF_MarkupInfo /* @member The number of `CF_Aabb`'s in `bounds`. */ int bounds_count; - /* @member An arry of `CF_Aabb`'s, one per line the `text` string provided in the `cf_text_markup_info_fn` callback. */ + /* @member An array of `CF_Aabb`'s, one per line the `text` string provided in the `cf_text_markup_info_fn` callback. */ CF_Aabb* bounds; } CF_MarkupInfo; // @end @@ -1162,23 +1201,23 @@ CF_API bool CF_CALL cf_peek_text_effect_active(void); * @category draw * @brief Pushes a `CF_Rect` for the viewport to render within. * @param viewport The viewport. - * @related TODO + * @related cf_draw_push_viewport cf_draw_pop_viewport cf_draw_peek_viewport */ CF_API void CF_CALL cf_draw_push_viewport(CF_Rect viewport); /** * @function cf_draw_pop_viewport * @category draw - * @brief TODO - * @related TODO + * @brief Pops and returns the last viewport `CF_Rect`. + * @related cf_draw_push_viewport cf_draw_pop_viewport cf_draw_peek_viewport */ CF_API CF_Rect CF_CALL cf_draw_pop_viewport(void); /** * @function cf_draw_peek_viewport * @category draw - * @brief TODO - * @related TODO + * @brief Returns the current viewport `CF_Rect`. + * @related cf_draw_push_viewport cf_draw_pop_viewport cf_draw_peek_viewport */ CF_API CF_Rect CF_CALL cf_draw_peek_viewport(void); @@ -1187,7 +1226,7 @@ CF_API CF_Rect CF_CALL cf_draw_peek_viewport(void); * @category draw * @brief Pushes a `CF_Rect` for the scissor to render within. * @param scissor The scissor box. - * @related TODO + * @related cf_draw_push_scissor cf_draw_pop_scissor cf_draw_peek_scissor */ CF_API void CF_CALL cf_draw_push_scissor(CF_Rect scissor); @@ -1195,7 +1234,7 @@ CF_API void CF_CALL cf_draw_push_scissor(CF_Rect scissor); * @function cf_draw_pop_scissor * @category draw * @brief Pops and returns the last `CF_Rect` for the scissor box. - * @related TODO + * @related cf_draw_push_scissor cf_draw_pop_scissor cf_draw_peek_scissor */ CF_API CF_Rect CF_CALL cf_draw_pop_scissor(void); @@ -1203,7 +1242,7 @@ CF_API CF_Rect CF_CALL cf_draw_pop_scissor(void); * @function cf_draw_peek_scissor * @category draw * @brief Returns the last `CF_Rect` for the scissor box. - * @related TODO + * @related cf_draw_push_scissor cf_draw_pop_scissor cf_draw_peek_scissor */ CF_API CF_Rect CF_CALL cf_draw_peek_scissor(void); @@ -1212,21 +1251,23 @@ CF_API CF_Rect CF_CALL cf_draw_peek_scissor(void); * @category draw * @brief Pushes a `CF_RenderState` for controlling various rendering settings. * @param render_state Various types of rendering states. - * @related TODO + * @related CF_RenderState cf_draw_push_render_state cf_draw_pop_render_state cf_draw_peek_render_state */ CF_API void CF_CALL cf_draw_push_render_state(CF_RenderState render_state); /** * @function cf_draw_pop_render_state * @category draw - * @brief Pops and returns the last `CF_RenderState`.* @related CF_RenderState cf_draw_filter cf_draw_push_viewport cf_draw_push_scissor cf_draw_push_render_state cf_draw_pop_render_state cf_draw_peek_render_state cf_render_to cf_app_draw_onto_screen + * @brief Pops and returns the last `CF_RenderState`. + * @related CF_RenderState cf_draw_push_render_state cf_draw_pop_render_state cf_draw_peek_render_state */ CF_API CF_RenderState CF_CALL cf_draw_pop_render_state(void); /** * @function cf_draw_peek_render_state * @category draw - * @brief Returns the last `CF_RenderState`.* @related CF_RenderState cf_draw_filter cf_draw_push_viewport cf_draw_push_scissor cf_draw_push_render_state cf_draw_pop_render_state cf_draw_peek_render_state cf_render_to cf_app_draw_onto_screen + * @brief Returns the last `CF_RenderState`. + * @related CF_RenderState cf_draw_push_render_state cf_draw_pop_render_state cf_draw_peek_render_state */ CF_API CF_RenderState CF_CALL cf_draw_peek_render_state(void); @@ -1240,7 +1281,7 @@ CF_API CF_RenderState CF_CALL cf_draw_peek_render_state(void); * can likely get away with 4096 on most devices. Larger internal atlases can be useful to decrease the number of * draw calls used, and also enables support for high-res image rendering. * - * Please not you should put in power of 2 atlases sizes to make the hardware happy. Here are the recommended range + * Please note you should put in power of 2 atlases sizes to make the hardware happy. Here are the recommended range * of sizes available: * * - 256 @@ -1248,6 +1289,7 @@ CF_API CF_RenderState CF_CALL cf_draw_peek_render_state(void); * - 1024 * - 2048 * - 4096 + * @related cf_draw_set_atlas_dimensions cf_render_to cf_draw_sprite */ CF_API void CF_CALL cf_draw_set_atlas_dimensions(int width_in_pixels, int height_in_pixels); @@ -1298,6 +1340,18 @@ CF_API CF_Shader CF_CALL cf_make_draw_shader_from_source(const char* src); */ CF_API CF_Shader CF_CALL cf_make_draw_shader_from_bytecode(CF_DrawShaderBytecode bytecode); +/** + * @function cf_shader_reload + * @category draw + * @brief Reloads a draw shader from its original file path. + * @param shader Pointer to the shader handle. Updated in-place on success. + * @return Returns true on success. + * @remarks Only works for shaders originally created with `cf_make_draw_shader`. The shader's path is + * recorded internally at creation time. + * @related CF_Shader cf_make_draw_shader cf_destroy_shader + */ +CF_API bool CF_CALL cf_shader_reload(CF_Shader* shader); + /** * @function cf_draw_push_shader * @category draw @@ -1334,21 +1388,87 @@ CF_API void CF_CALL cf_draw_push_alpha_discard(bool true_enable_alpha_discard); /** * @function cf_draw_pop_alpha_discard * @category draw - * @brief TODO + * @brief Pops and returns the last alpha discard state. * @remarks Alpha discarding is useful to throw away pixels with zero alpha, for cutouts or as an optimization, or for certain blending techniques. - * @related TODO + * @related cf_draw_push_alpha_discard cf_draw_pop_alpha_discard cf_draw_peek_alpha_discard */ CF_API bool CF_CALL cf_draw_pop_alpha_discard(void); /** * @function cf_draw_peek_alpha_discard * @category draw - * @brief TODO + * @brief Returns the current alpha discard state. * @remarks Alpha discarding is useful to throw away pixels with zero alpha, for cutouts or as an optimization, or for certain blending techniques. - * @related TODO + * @related cf_draw_push_alpha_discard cf_draw_pop_alpha_discard cf_draw_peek_alpha_discard */ CF_API bool CF_CALL cf_draw_peek_alpha_discard(void); +/** + * @enum CF_DrawFilterMode + * @category draw + * @brief Filter modes for the draw system's texture sampling. + * @related CF_DrawFilterMode cf_draw_filter_mode_to_string cf_draw_push_filter cf_draw_pop_filter cf_draw_peek_filter + */ +#define CF_DRAW_FILTER_MODE_DEFS \ + /* @entry Nearest-neighbor filtering. Good for pixel art. Uses hardware nearest. */ \ + CF_ENUM(DRAW_FILTER_NEAREST, 0) \ + /* @entry Linear (bilinear) filtering. Uses hardware linear. */ \ + CF_ENUM(DRAW_FILTER_LINEAR, 1) \ + /* @entry Smooth filtering. Uses hardware linear combined with a shader-based smooth UV interpolation. */ \ + CF_ENUM(DRAW_FILTER_SMOOTH, 2) \ + /* @end */ + +typedef enum CF_DrawFilterMode +{ +#define CF_ENUM(K, V) CF_##K = V, + CF_DRAW_FILTER_MODE_DEFS +#undef CF_ENUM +} CF_DrawFilterMode; + +/** + * @function cf_draw_filter_mode_to_string + * @category draw + * @brief Returns a `CF_DrawFilterMode` converted to a C string. + * @related CF_DrawFilterMode cf_draw_filter_mode_to_string cf_draw_push_filter cf_draw_pop_filter cf_draw_peek_filter + */ +CF_INLINE const char* cf_draw_filter_mode_to_string(CF_DrawFilterMode mode) { + switch (mode) { +#define CF_ENUM(K, V) case CF_##K: return CF_STRINGIZE(CF_##K); + CF_DRAW_FILTER_MODE_DEFS +#undef CF_ENUM + default: return NULL; + } +} + +/** + * @function cf_draw_push_filter + * @category draw + * @brief Sets the texture filtering mode for the draw system. + * @param mode The filter mode to use. See `CF_DrawFilterMode`. + * @remarks NEAREST uses hardware nearest-neighbor filtering, good for pixel art. LINEAR uses hardware bilinear filtering. + * SMOOTH uses hardware linear combined with shader-based smooth UV interpolation for high quality upscaling. + * @related CF_DrawFilterMode cf_draw_push_filter cf_draw_pop_filter cf_draw_peek_filter + */ +CF_API void CF_CALL cf_draw_push_filter(CF_DrawFilterMode mode); + +/** + * @function cf_draw_pop_filter + * @category draw + * @brief Pops the texture filtering mode stack. + * @return Returns the popped filter mode. + * @related CF_DrawFilterMode cf_draw_push_filter cf_draw_pop_filter cf_draw_peek_filter + */ +CF_API CF_DrawFilterMode CF_CALL cf_draw_pop_filter(void); + +/** + * @function cf_draw_peek_filter + * @category draw + * @brief Returns the current texture filtering mode without popping the stack. + * @return Returns the current filter mode. + * @related CF_DrawFilterMode cf_draw_push_filter cf_draw_pop_filter cf_draw_peek_filter + */ +CF_API CF_DrawFilterMode CF_CALL cf_draw_peek_filter(void); + /** * @function cf_draw_set_texture * @category draw @@ -1409,7 +1529,7 @@ CF_API void CF_CALL cf_draw_set_uniform_color(const char* name, CF_Color val); * @category draw * @brief Applies the current draw transform to a point. * @param p The point to transform. - * @related TODO + * @related cf_draw_mul cf_draw_transform cf_draw_translate cf_draw_scale cf_draw_rotate */ CF_API CF_V2 CF_CALL cf_draw_mul(CF_V2 p); @@ -1656,7 +1776,7 @@ typedef struct CF_AtlasSubImage /* @member Must be a unique number for all sub-images across all atlases. You should start at 0 and increment for each unique id you need. */ uint64_t image_id; - /* @member The width in height, in pixels, of the sub-image. */ + /* @member The width and height, in pixels, of the sub-image. */ int w, h; /* @member u coordinate in the premade atlas. */ @@ -1736,6 +1856,8 @@ CF_INLINE void draw_sprite_9_slice_tiled(const CF_Sprite* sprite) { cf_draw_spri CF_INLINE void draw_sprite_9_slice_tiled(const CF_Sprite& sprite) { cf_draw_sprite_9_slice_tiled(&sprite); } CF_INLINE void sprite_draw_9_slice_tiled(const CF_Sprite* sprite) { cf_draw_sprite_9_slice_tiled(sprite); } CF_INLINE void sprite_draw_9_slice_tiled(const CF_Sprite& sprite) { cf_draw_sprite_9_slice_tiled(&sprite); } +CF_INLINE void draw_prefetch(const CF_Sprite* sprite) { cf_draw_prefetch(sprite); } +CF_INLINE void draw_prefetch(const CF_Sprite& sprite) { cf_draw_prefetch(&sprite); } CF_INLINE void draw_quad(CF_Aabb bb, float thickness = 1.0f, float chubbiness = 0) { cf_draw_quad(bb, thickness, chubbiness); } CF_INLINE void draw_quad(v2 p0, v2 p1, v2 p2, v2 p3, float thickness = 1.0f, float chubbiness = 0) { cf_draw_quad2(p0, p1, p2, p3, thickness, chubbiness); } CF_INLINE void draw_quad_fill(CF_Aabb bb, float chubbiness = 0) { cf_draw_quad_fill(bb, chubbiness); } @@ -1772,16 +1894,20 @@ CF_INLINE int draw_peek_layer() { return cf_draw_peek_layer(); } CF_INLINE void draw_push_color(CF_Color c) { cf_draw_push_color(c); } CF_INLINE CF_Color draw_pop_color() { return cf_draw_pop_color(); } CF_INLINE CF_Color draw_peek_color() { return cf_draw_peek_color(); } -CF_INLINE void draw_push_antialias(bool antialias) { cf_draw_push_antialias(antialias); } -CF_INLINE bool draw_pop_antialias() { return cf_draw_pop_antialias(); } -CF_INLINE bool draw_peek_antialias() { return cf_draw_peek_antialias(); } -CF_INLINE void draw_push_antialias_scale(float scale) { return cf_draw_push_antialias_scale(scale); } -CF_INLINE float draw_pop_antialias_scale() { return cf_draw_pop_antialias_scale(); } -CF_INLINE float draw_peek_antialias_scale() { return cf_draw_peek_antialias_scale(); } +CF_INLINE void draw_push_shape_aa(float aa) { cf_draw_push_shape_aa(aa); } +CF_INLINE float draw_pop_shape_aa() { return cf_draw_pop_shape_aa(); } +CF_INLINE float draw_peek_shape_aa() { return cf_draw_peek_shape_aa(); } CF_INLINE void draw_push_vertex_attributes(float r, float g, float b, float a) { cf_draw_push_vertex_attributes(r, g, b, a); } CF_INLINE void draw_push_vertex_attributes(CF_Color attributes) { cf_draw_push_vertex_attributes2(attributes); } CF_INLINE CF_Color draw_pop_vertex_attributes() { return cf_draw_pop_vertex_attributes(); } CF_INLINE CF_Color draw_peek_vertex_attributes() { return cf_draw_peek_vertex_attributes(); } +CF_INLINE void draw_push_tri_colors(CF_Color c0, CF_Color c1, CF_Color c2) { cf_draw_push_tri_colors(c0, c1, c2); } +CF_INLINE void draw_pop_tri_colors() { cf_draw_pop_tri_colors(); } +CF_INLINE void draw_peek_tri_colors(CF_Color* c0, CF_Color* c1, CF_Color* c2) { cf_draw_peek_tri_colors(c0, c1, c2); } +CF_INLINE void draw_push_tri_attributes(CF_Color a0, CF_Color a1, CF_Color a2) { cf_draw_push_tri_attributes(a0, a1, a2); } +CF_INLINE void draw_pop_tri_attributes() { cf_draw_pop_tri_attributes(); } +CF_INLINE void draw_peek_tri_attributes(CF_Color* a0, CF_Color* a1, CF_Color* a2) { cf_draw_peek_tri_attributes(a0, a1, a2); } +CF_INLINE void set_vertex_callback(CF_VertexFn* vertex_fn) { cf_set_vertex_callback(vertex_fn); } CF_INLINE CF_Result make_font(const char* path, const char* font_name) { return cf_make_font(path, font_name); } CF_INLINE CF_Result make_font_from_memory(void* data, int size, const char* font_name) { return cf_make_font_from_memory(data, size, font_name); } @@ -1804,6 +1930,9 @@ CF_INLINE v2 text_size(const char* text, int num_chars_to_render = -1) { return CF_INLINE void draw_text(const char* text, v2 position, int num_chars_to_render = -1) { cf_draw_text(text, position, num_chars_to_render); } CF_INLINE void text_effect_register(const char* name, CF_TextEffectFn* fn) { cf_text_effect_register(name, fn); } +CF_INLINE double text_effect_get_number(const CF_TextEffect* fx, const char* key, double default_val = 0) { return cf_text_effect_get_number(fx, key, default_val); } +CF_INLINE CF_Color text_effect_get_color(const CF_TextEffect* fx, const char* key, CF_Color default_val = cf_color_white()) { return cf_text_effect_get_color(fx, key, default_val); } +CF_INLINE const char* text_effect_get_string(const CF_TextEffect* fx, const char* key, const char* default_val = NULL) { return cf_text_effect_get_string(fx, key, default_val); } CF_INLINE void push_text_id(uint64_t id) { cf_push_text_id(id); } CF_INLINE uint64_t pop_text_id() { return cf_pop_text_id(); } @@ -1814,6 +1943,9 @@ CF_INLINE const char* text_without_markups(const char* text) { return cf_text_wi CF_INLINE void push_text_effect_active(bool effects_on) { cf_push_text_effect_active(effects_on); } CF_INLINE bool pop_text_effect_active() { return cf_pop_text_effect_active(); } CF_INLINE bool peek_text_effect_active() { return cf_peek_text_effect_active(); } +CF_INLINE void push_text_vertical_layout(bool layout_vertically) { cf_push_text_vertical_layout(layout_vertically); } +CF_INLINE bool pop_text_vertical_layout() { return cf_pop_text_vertical_layout(); } +CF_INLINE bool peek_text_vertical_layout() { return cf_peek_text_vertical_layout(); } CF_INLINE void draw_push_viewport(CF_Rect viewport) { cf_draw_push_viewport(viewport); } CF_INLINE CF_Rect draw_pop_viewport() { return cf_draw_pop_viewport(); } @@ -1827,12 +1959,16 @@ CF_INLINE CF_RenderState draw_peek_render_state() { return cf_draw_peek_render_s CF_INLINE void draw_set_atlas_dimensions(int width_in_pixels, int height_in_pixels) { cf_draw_set_atlas_dimensions(width_in_pixels, height_in_pixels); } CF_INLINE CF_Shader make_draw_shader(const char* path) { return cf_make_draw_shader(path); } CF_INLINE CF_Shader make_draw_shader_from_source(const char* src) { return cf_make_draw_shader_from_source(src); } +CF_INLINE CF_Shader make_draw_shader_from_bytecode(CF_DrawShaderBytecode bytecode) { return cf_make_draw_shader_from_bytecode(bytecode); } CF_INLINE void draw_push_shader(CF_Shader shader) { cf_draw_push_shader(shader); } CF_INLINE CF_Shader draw_pop_shader() { return cf_draw_pop_shader(); } CF_INLINE CF_Shader draw_peek_shader() { return cf_draw_peek_shader(); } CF_INLINE void draw_push_alpha_discard(bool true_to_enable_alpha_discard) { return cf_draw_push_alpha_discard(true_to_enable_alpha_discard); } CF_INLINE bool draw_pop_alpha_discard() { return cf_draw_pop_alpha_discard(); } CF_INLINE bool draw_peek_alpha_discard() { return cf_draw_peek_alpha_discard(); } +CF_INLINE void draw_push_filter(CF_DrawFilterMode mode) { cf_draw_push_filter(mode); } +CF_INLINE CF_DrawFilterMode draw_pop_filter() { return cf_draw_pop_filter(); } +CF_INLINE CF_DrawFilterMode draw_peek_filter() { return cf_draw_peek_filter(); } CF_INLINE void draw_set_texture(const char* name, CF_Texture texture) { cf_draw_set_texture(name, texture); } CF_INLINE void draw_set_uniform(const char* name, void* data, CF_UniformType type, int array_length) { cf_draw_set_uniform(name, data, type, array_length); } CF_INLINE void draw_set_uniform(const char* name, int val) { cf_draw_set_uniform_int(name, val); } diff --git a/include/cute_file_system.h b/include/cute_file_system.h index 8ae05ea11..ea785d914 100644 --- a/include/cute_file_system.h +++ b/include/cute_file_system.h @@ -19,159 +19,8 @@ extern "C" { #endif // __cplusplus -//-------------------------------------------------------------------------------------------------- -// Path helper functions. -// These are implemented with the C-string API in "cute_string.h"; each function returns a fully -// mutable string you must free up with `sfree` or `cf_string_free` when done. - -/** - * @function spfname - * @category path - * @brief Returns the filename portion of a path. Returns a new string. - * @param s The path string. - * @example > Example fetching a filename from a path. - * const char* filename = spfname("/data/collections/rare/big_gem.txt"); - * printf("%s\n", filename); - * // Prints: big_gem.txt - * @remarks Call `sfree` on the return value when done. `sp` stands for "sting path". - * @related spfname spfname_no_ext spext spext_equ sppop sppopn spcompact spdir_of sptop_of spnorm - */ -#define spfname(s) cf_path_get_filename(s) - -/** - * @function spfname_no_ext - * @category path - * @brief Returns the filename portion of a path without the file extension. Returns a new string. - * @param s The path string. - * @example > Example fetching a filename from a path without the extension attached. - * const char* filename = spfname("/data/collections/rare/big_gem.txt"); - * printf("%s\n", filename); - * // Prints: big_gem - * @remarks Call `sfree` on the return value when done. `sp` stands for "sting path". - * @related spfname spfname_no_ext spext spext_equ sppop sppopn spcompact spdir_of sptop_of spnorm - */ -#define spfname_no_ext(s) cf_path_get_filename_no_ext(s) - -/** - * @function spext - * @category path - * @brief Returns the extension of the file for the given path. Returns a new string. - * @param s The path string. - * @example > Example fetching a filename from a path without the extension attached. - * const char* ext = spfname("/data/collections/rare/big_gem.txt"); - * printf("%s\n", ext); - * // Prints: .txt - * @remarks Call `sfree` on the return value when done. `sp` stands for "sting path". - * @related spfname spfname_no_ext spext spext_equ sppop sppopn spcompact spdir_of sptop_of spnorm - */ -#define spext(s) cf_path_get_ext(s) - -/** - * @function spext_equ - * @category path - * @brief Returns true if the file's extension matches, false otherwise. - * @param s The path string. - * @param ext The file extension. - * @remarks `sp` stands for "sting path". - * @related spfname spfname_no_ext spext spext_equ sppop sppopn spcompact spdir_of sptop_of spnorm - */ -#define spext_equ(s, ext) cf_path_ext_equ(s, ext) - -/** - * @function sppop - * @category path - * @brief Removes the rightmost file or directory from the path. - * @param s The path string. - * @return If the string is not a dynamic string from CF's string API, a new string is returned. Otherwise the - * string is modified in-place. You must call `sfree` if a new dynamic string is returned, when done. - * @remarks `sp` stands for "sting path". - * @related spfname spfname_no_ext spext spext_equ sppop sppopn spcompact spdir_of sptop_of spnorm - */ -#define sppop(s) cf_path_pop(s) - -/** - * @function sppopn - * @category path - * @brief Removes the rightmost n files or directories from the path. - * @param s The path string. - * @param n The number of files to pop from the directory path. - * @return If the string is not a dynamic string from CF's string API, a new string is returned. Otherwise the - * string is modified in-place. You must call `sfree` if a new dynamic string is returned, when done. - * @remarks `sp` stands for "sting path". - * @related spfname spfname_no_ext spext spext_equ sppop sppopn spcompact spdir_of sptop_of spnorm - */ -#define sppopn(s, n) cf_path_pop_n(s, n) - -/** - * @function spcompact - * @category path - * @brief Squishes the path to be less than or equal to n characters in length. - * @param s The path string. - * @param n The number of files to pop from the directory path. - * @return If the string is not a dynamic string from CF's string API, a new string is returned. Otherwise the - * string is modified in-place. You must call `sfree` if a new dynamic string is returned, when done. - * @remarks This will insert ellipses "..." into the path as necessary. This function is useful for displaying paths - * and visualizing them in small boxes or windows. n includes the nul-byte. Returns a new string. - * @related spfname spfname_no_ext spext spext_equ sppop sppopn spcompact spdir_of sptop_of spnorm - */ -#define spcompact(s, n) cf_path_compact(s, n) - -/** - * @function spdir_of - * @category path - * @brief Returns the directory of a given file or directory. Returns a new string. - * @param s The path string. - * @example > Example fetching a directory a file sits within. - * const char* filename = spfname("/data/collections/rare/big_gem.txt"); - * printf("%s\n", filename); - * // Prints: /rare - * @remarks `sp` stands for "sting path". Call `sfree` on the return value when done. - * @related spfname spfname_no_ext spext spext_equ sppop sppopn spcompact spdir_of sptop_of spnorm - */ -#define spdir_of(s) cf_path_directory_of(s) - -/** - * @function sptop_of - * @category path - * @brief Returns the top-level directory of a given file or directory. Returns a new string. - * @param s The path string. - * @example > Example fetching a the top-level directory a file sits within. - * const char* filename = spfname("/data/collections/rare/big_gem.txt"); - * printf("%s\n", filename); - * // Prints: /data - * @remarks `sp` stands for "sting path". Call `sfree` on the return value when done. - * @related spfname spfname_no_ext spext spext_equ sppop sppopn spcompact spdir_of sptop_of spnorm - */ -#define sptop_of(s) cf_path_top_directory(s) - -/** - * @function spnorm - * @category path - * @brief Normalizes a path as a new string. - * @param s The path string. - * @remarks All '\\' are replaced with '/'. Any duplicate '////' are replaced with a single '/'. Trailing '/' are removed. Dot folders are resolved, e.g. - * ``` - * spnorm("/a/b/./c") -> "/a/b/c" - * spnorm("/a/b/../c") -> "/a/c" - * ``` - * The first character is always '/', unless it's a windows drive, e.g. - * ``` - * spnorm("C:\\Users\\Randy\\Documents") -> "C:/Users/Randy/Documents" - * ``` - * @related spfname spfname_no_ext spext spext_equ sppop sppopn spcompact spdir_of sptop_of spnorm - */ -#define spnorm(s) cf_path_normalize(s) - -CF_API char* CF_CALL cf_path_get_filename(const char* path); -CF_API char* CF_CALL cf_path_get_filename_no_ext(const char* path); -CF_API char* CF_CALL cf_path_get_ext(const char* path); -CF_API bool CF_CALL cf_path_ext_equ(const char* path, const char* ext); -CF_API char* CF_CALL cf_path_pop(const char* path); -CF_API char* CF_CALL cf_path_pop_n(const char* path, int n); -CF_API char* CF_CALL cf_path_compact(const char* path, int n); -CF_API char* CF_CALL cf_path_directory_of(const char* path); -CF_API char* CF_CALL cf_path_top_directory(const char* path); -CF_API char* CF_CALL cf_path_normalize(const char* path); +// Path helper macros (spfname, sppop, spnorm, etc.) are defined in ckit.h. +// Longform cf_path_* aliases are in cute_string.h. //-------------------------------------------------------------------------------------------------- // Virtual file system. @@ -243,7 +92,7 @@ CF_API char* CF_CALL cf_path_normalize(const char* path); * a single file without overwriting each other on the actual disk. * * By default CF mounts the base directory when you call `make_app`. This can be disabled by - * passing the `APP_OPTIONS_FILE_SYSTEM_DONT_DEFAULT_MOUNT` flag to `make_app`. + * passing the `CF_APP_OPTIONS_FILE_SYSTEM_DONT_DEFAULT_MOUNT_BIT` flag to `cf_make_app`. */ /** @@ -263,7 +112,7 @@ typedef struct CF_File CF_File; * @related CF_File CF_Stat cf_file_type_to_string */ #define CF_FILE_TYPE_DEFS \ - /* @entry A reguler file, such as a .txt or .pdf file. */ \ + /* @entry A regular file, such as a .txt or .pdf file. */ \ CF_ENUM(FILE_TYPE_REGULAR, 0) \ /* @entry A directory/folder. */ \ CF_ENUM(FILE_TYPE_DIRECTORY, 1) \ @@ -338,7 +187,7 @@ CF_API const char* CF_CALL cf_fs_get_base_directory(void); * @function cf_fs_get_working_directory * @category file * @brief Returns the current working directory in platform-dependent notation. - * @remarks Not all platforms have the concept of a working directory, but this function will sill try to return something sane in these cases. + * @remarks Not all platforms have the concept of a working directory, but this function will still try to return something sane in these cases. * @related cf_fs_get_base_directory cf_fs_set_write_directory cf_fs_get_user_directory cf_fs_mount cf_fs_dismount cf_fs_get_working_directory */ CF_API const char* CF_CALL cf_fs_get_working_directory(void); @@ -395,12 +244,12 @@ CF_API const char* CF_CALL cf_fs_get_user_directory(const char* company_name, co * modding or download patches, as duplicate entries will be searched for on the path as normal, * without the need to overwrite each other on the actual disk. * - * You can mount an actual directory or an archive file. If it's an archive the vitrual file + * You can mount an actual directory or an archive file. If it's an archive the virtual file * system will treat it like a normal directory for you. There are a variety of archive file * formats supported (see top of file). * * By default CF mounts the base directory when you call `cf_make_app`. This can be disabled by - * passing the `CF_APP_OPTIONS_FILE_SYSTEM_DONT_DEFAULT_MOUNT` flag to `cf_make_app`. [Virtual File System](https://randygaul.github.io/cute_framework/topics/virtual_file_system). + * passing the `CF_APP_OPTIONS_FILE_SYSTEM_DONT_DEFAULT_MOUNT_BIT` flag to `cf_make_app`. [Virtual File System](https://randygaul.github.io/cute_framework/topics/virtual_file_system). * @related cf_fs_get_base_directory cf_fs_set_write_directory cf_fs_get_user_directory cf_fs_mount cf_fs_dismount */ CF_API CF_Result CF_CALL cf_fs_mount(const char* archive, const char* mount_point, bool append_to_path); @@ -505,11 +354,11 @@ CF_API CF_Result CF_CALL cf_fs_create_directory(const char* virtual_path); * @param virtual_path The virtual path to the directory. * @return Returns any errors as a `CF_Result`. * @example > Loop over a list of all files in a directory. - * const char** list = cf_fs_enumerate_directory("/data"); - * for (const char** i = list; *i; ++i) { - * printf("Found %s\n", *i); - * } - * cf_fs_free_enumerated_directory(list); + * const char** list = cf_fs_enumerate_directory("/data"); + * for (const char** i = list; *i; ++i) { + * printf("Found %s\n", *i); + * } + * cf_fs_free_enumerated_directory(list); * @remarks Results are collected by visiting the search path for all real directories mounted on `virtual_path`. No duplicate file * names will be reported. The list itself is sorted alphabetically, though you can further sort it however you like. Free * the list up with `cf_fs_free_enumerated_directory` when done. The final element of the list is NULL. [Virtual File System](https://randygaul.github.io/cute_framework/topics/virtual_file_system). @@ -743,6 +592,11 @@ CF_INLINE CF_Result fs_write_entire_buffer_to_file(const char* virtual_path, con CF_INLINE const char* fs_get_backend_specific_error_message() { return cf_fs_get_backend_specific_error_message(); } CF_INLINE const char* fs_get_user_directory(const char* org, const char* app) { return cf_fs_get_user_directory(org, app); } CF_INLINE const char* fs_get_actual_path(const char* virtual_path) { return cf_fs_get_actual_path(virtual_path); } +CF_INLINE const char* fs_get_working_directory() { return cf_fs_get_working_directory(); } +CF_INLINE CF_Result fs_write_string_to_file(const char* virtual_path, const char* string) { return cf_fs_write_string_to_file(virtual_path, string); } +CF_INLINE CF_Result fs_write_string_range_to_file(const char* virtual_path, const char* begin, const char* end) { return cf_fs_write_string_range_to_file(virtual_path, begin, end); } +CF_INLINE CF_Result fs_init(const char* argv0) { return cf_fs_init(argv0); } +CF_INLINE void fs_destroy() { cf_fs_destroy(); } struct CF_Path { @@ -752,7 +606,7 @@ struct CF_Path CF_INLINE CF_Path(CF_Path&& p) { *this = p; } CF_INLINE ~CF_Path() { sfree(m_path); m_path = NULL; } - static CF_INLINE CF_Path steal_from(const char* cute_c_api_string) { CF_ACANARY(cute_c_api_string); CF_Path p; p.m_path = (char*)cute_c_api_string; return p; } + static CF_INLINE CF_Path steal_from(const char* cute_c_api_string) { CK_ACANARY(cute_c_api_string); CF_Path p; p.m_path = (char*)cute_c_api_string; return p; } CF_INLINE String filename() const { return String::steal_from(spfname(m_path)); } CF_INLINE String filename_no_ext() const { return String::steal_from(spfname_no_ext(m_path)); } diff --git a/include/cute_graphics.h b/include/cute_graphics.h index 0b621e53b..1f0e595f0 100644 --- a/include/cute_graphics.h +++ b/include/cute_graphics.h @@ -29,9 +29,8 @@ extern "C" { * If you want to draw sprites, lines/shapes, or text, see: cute_draw.h * * Quick list of unsupported features. CF's focus is on the 2D use case, so most of these features are - * omit since they aren't super useful for 2D. + * omitted since they aren't super useful for 2D. * - * - Blend color constant * - Multiple render targets (aka color/texture attachments) * - Cube map * - 3D textures @@ -75,6 +74,42 @@ typedef struct CF_Texture { uint64_t id; } CF_Texture; typedef struct CF_Canvas { uint64_t id; } CF_Canvas; // @end +/** + * @struct CF_Readback + * @category graphics + * @brief An opaque handle representing an async GPU readback operation. + * @example > Rendering to a canvas with cf_render_to, then reading back pixel data. + * // Create a canvas. + * CF_Canvas canvas = cf_make_canvas(cf_canvas_defaults(256, 256)); + * + * // Queue up draw calls and flush them to the canvas. + * cf_draw_sprite(&my_sprite); + * cf_render_to(canvas, true); + * + * // Initiate async readback (ends the active render pass automatically). + * // Save this `CF_Readback` for later! This is an *async* object. + * CF_Readback readback = cf_canvas_readback(canvas); + * + * // ...Elsewhere, and some # of frames later... + * // Poll each frame until the GPU finishes the download. + * if (cf_readback_ready(readback)) { + * int size = cf_readback_size(readback); + * void* pixels = cf_alloc(size); + * cf_readback_data(readback, pixels, size); + * // pixels now holds RGBA8 data (256 * 256 * 4 bytes). + * // ... use the pixel data ... + * cf_free(pixels); + * cf_destroy_readback(readback); + * } + * @remarks A readback initiates an async GPU-to-CPU copy of pixel data from a canvas. + * Poll with `cf_readback_ready` and retrieve data with `cf_readback_data`. + * The pixel format matches the canvas target format (typically RGBA8, 4 bytes per pixel). + * On web/Emscripten builds, readback is unsupported and returns a zero handle. + * @related CF_Canvas cf_canvas_readback cf_readback_ready cf_readback_data cf_readback_size cf_destroy_readback + */ +typedef struct CF_Readback { uint64_t id; } CF_Readback; +// @end + /** * @struct CF_Mesh * @category graphics @@ -110,6 +145,28 @@ typedef struct CF_Material { uint64_t id; } CF_Material; typedef struct CF_Shader { uint64_t id; } CF_Shader; // @end +/** + * @struct CF_ComputeShader + * @category graphics + * @brief An opaque handle representing a compute shader. + * @remarks A compute shader is a program that runs on the GPU outside the graphics pipeline. + * Compute shaders are only available on SDL_GPU backends (not GLES3). + * @related CF_ComputeShader cf_make_compute_shader cf_destroy_compute_shader cf_dispatch_compute + */ +typedef struct CF_ComputeShader { uint64_t id; } CF_ComputeShader; +// @end + +/** + * @struct CF_StorageBuffer + * @category graphics + * @brief An opaque handle representing a GPU storage buffer. + * @remarks Storage buffers are GPU-accessible buffers used with compute and graphics shaders. + * They are only available on SDL_GPU backends (not GLES3). + * @related CF_StorageBuffer CF_StorageBufferParams cf_make_storage_buffer cf_destroy_storage_buffer cf_update_storage_buffer + */ +typedef struct CF_StorageBuffer { uint64_t id; } CF_StorageBuffer; +// @end + //-------------------------------------------------------------------------------------------------- // Device queries. @@ -120,7 +177,7 @@ typedef struct CF_Shader { uint64_t id; } CF_Shader; * @related CF_BackendType cf_backend_type_to_string cf_query_backend */ #define CF_BACKEND_TYPE_DEFS \ - /* @entry Invalid backend type (unitialized or failed to create). */ \ + /* @entry Invalid backend type (uninitialized or failed to create). */ \ CF_ENUM(BACKEND_TYPE_INVALID, -1) \ /* @entry Vulkan backend. */ \ CF_ENUM(BACKEND_TYPE_VULKAN, 0) \ @@ -132,7 +189,7 @@ typedef struct CF_Shader { uint64_t id; } CF_Shader; CF_ENUM(BACKEND_TYPE_METAL, 3) \ /* @entry A "secret" backend for platforms under non-disclosure agreement. */ \ CF_ENUM(BACKEND_TYPE_PRIVATE, 4) \ - /* @entry OpenGL ES 3 backen.. */ \ + /* @entry OpenGL ES 3 backend. */ \ CF_ENUM(BACKEND_TYPE_GLES3, 5) \ /* @end */ @@ -390,9 +447,9 @@ CF_API bool CF_CALL cf_query_pixel_format(CF_PixelFormat format, CF_PixelFormatO /* @entry The texture will be used as read-only storage in graphics pipelines. */ \ CF_ENUM(TEXTURE_USAGE_GRAPHICS_STORAGE_READ_BIT, 0x00000008) \ /* @entry The texture will be used as read-only storage in compute pipelines. */ \ - CF_ENUM(TEXTURE_USAGE_COMPUTE_STORAGE_READ_BIT, 0x00000020) \ + CF_ENUM(TEXTURE_USAGE_COMPUTE_STORAGE_READ_BIT, 0x00000010) \ /* @entry The texture will be used as writeable storage in compute pipelines. */ \ - CF_ENUM(TEXTURE_USAGE_COMPUTE_STORAGE_WRITE_BIT, 0x00000040) \ + CF_ENUM(TEXTURE_USAGE_COMPUTE_STORAGE_WRITE_BIT, 0x00000020) \ /* @end */ typedef uint32_t CF_TextureUsageFlags; @@ -544,11 +601,11 @@ typedef struct CF_TextureParams /* @member Number of elements (usually pixels) along the height of the texture. */ int height; - /* @member 0 = auto compute from dimensions if `generate_mipmaps` is true, else specify an explicit number. */ + /* @member 0 = auto compute from dimensions if `allocate_mipmaps` is true, else specify an explicit number. */ int mip_count; - /* @member Defaulted to false, true to enable mipmap generation and will be initialized with full mipmaps. */ - bool generate_mipmaps; + /* @member Defaulted to false, true to allocate a full mipmap chain for the texture. */ + bool allocate_mipmaps; /* @member Mipmap level bias; positive = blurrier, negative = sharper. */ float mip_lod_bias; @@ -626,6 +683,14 @@ CF_API void CF_CALL cf_texture_update_mip(CF_Texture texture, void* data, int si */ CF_API void CF_CALL cf_generate_mipmaps(CF_Texture texture); +/** + * @function cf_gpu_sync + * @category graphics + * @brief Submits the command buffer, waits for GPU completion via fence, then reacquires. + * @remarks Forces GPU/CPU serialization. + */ +CF_API void CF_CALL cf_gpu_sync(void); + /** * @function cf_texture_handle * @category graphics @@ -654,10 +719,12 @@ CF_API uint64_t CF_CALL cf_texture_binding_handle(CF_Texture texture); * @related CF_Shader cf_shader_directory cf_make_shader */ #define CF_SHADER_STAGE_DEFS \ - /* @entry */ \ + /* @entry Vertex shader stage. */ \ CF_ENUM(SHADER_STAGE_VERTEX, 0) \ - /* @entry */ \ + /* @entry Fragment shader stage. */ \ CF_ENUM(SHADER_STAGE_FRAGMENT, 1) \ + /* @entry Compute shader stage. */ \ + CF_ENUM(SHADER_STAGE_COMPUTE, 2) \ /* @end */ typedef enum CF_ShaderStage @@ -762,7 +829,7 @@ CF_API CF_Shader CF_CALL cf_make_shader_from_source(const char* vertex_src, cons * @category graphics * @brief Compiles a shader to SPIR-V bytecode. * @param shader_src Raw glsl, version 450, for the shader as a string. - * @param stage The shaderstrage to differentiate between vertex or fragment shaders. + * @param stage The shader stage to differentiate between vertex or fragment shaders. * @remarks This function is good for precompiling shaders to bytecode, which can help speed up app * startup times. SPIR-V blobs can be saved straight to disk and shipped with your game. Load * the bytecode blob pair (vertex + fragment shader blobs) into a `CF_Shader` via `cf_make_shader_from_bytecode`. @@ -778,6 +845,7 @@ CF_API CF_ShaderBytecode CF_CALL cf_compile_shader_to_bytecode(const char* shade * @param bytecode The bytecode blob to free. * @remarks This function must only be called on the bytecode blob returned from `cf_compile_shader_to_bytecode`. * It cannot be called on the bytecode blob generated as a header from the `cute-shaderc` compiler. + * @related CF_ShaderBytecode cf_compile_shader_to_bytecode cf_make_shader_from_bytecode cf_free_shader_bytecode */ CF_API void CF_CALL cf_free_shader_bytecode(CF_ShaderBytecode bytecode); @@ -803,6 +871,235 @@ CF_API CF_Shader CF_CALL cf_make_shader_from_bytecode(CF_ShaderBytecode vertex_b */ CF_API void CF_CALL cf_destroy_shader(CF_Shader shader); +//-------------------------------------------------------------------------------------------------- +// Compute Shaders. +// +// Compute shaders run on the GPU outside the graphics pipeline. They are only available on +// SDL_GPU backends (Vulkan, D3D12, Metal). GLES3 stubs return null handles / no-op. +// +// Uniforms and sampled textures are supplied via a CF_Material's compute stage (cs). +// Storage buffers and storage textures are bound directly via the dispatch struct. + +/** + * @function cf_make_compute_shader + * @category graphics + * @brief Creates a compute shader from a file in the shader directory. + * @param path A virtual path to the compute shader source, relative to the shader directory. + * @remarks Compute shaders must use GLSL 450 and follow the SDL_GPU resource set layout convention. + * Resources must be assigned to specific descriptor sets depending on their type: + * + * For _COMPUTE_ shaders: + * ``` + * Set 0: Sampled textures, followed by readonly storage textures, followed by readonly storage buffers + * Set 1: Read-write storage textures, followed by read-write storage buffers + * Set 2: Uniform buffers + * ``` + * + * Example _COMPUTE_ shader: + * ```glsl + * layout (set = 0, binding = 0) uniform sampler2D u_input; + * + * layout (set = 1, binding = 0, rgba8) uniform writeonly image2D u_output; + * + * layout (set = 2, binding = 0) uniform uniform_block { + * float u_time; + * }; + * + * layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + * ``` + * + * Important notes: + * - Sampled textures (set 0) are bound via `cf_material_set_texture_cs`. + * - Uniforms (set 2) are bound via `cf_material_set_uniform_cs`. + * - Read-write storage textures (set 1) are bound via `CF_ComputeDispatch::rw_textures`. + * - Readonly storage textures (set 0, after samplers) are bound via `CF_ComputeDispatch::ro_textures`. + * - Do NOT use `return` to exit threads before a `barrier()` call. All threads in a workgroup + * must reach `barrier()` uniformly, or the pipeline will fail to compile on some backends. + * Instead, guard `imageStore` and other side-effects behind a bounds check. + * @related CF_ComputeShader cf_make_compute_shader cf_make_compute_shader_from_source cf_make_compute_shader_from_bytecode cf_destroy_compute_shader cf_dispatch_compute + */ +CF_API CF_ComputeShader CF_CALL cf_make_compute_shader(const char* path); + +/** + * @function cf_make_compute_shader_from_source + * @category graphics + * @brief Creates a compute shader from a GLSL 450 source string. + * @param src The compute shader source as a C-string. + * @remarks The source must follow the compute shader resource set layout described in `cf_make_compute_shader`. + * @related CF_ComputeShader cf_make_compute_shader cf_make_compute_shader_from_source cf_make_compute_shader_from_bytecode cf_destroy_compute_shader cf_dispatch_compute + */ +CF_API CF_ComputeShader CF_CALL cf_make_compute_shader_from_source(const char* src); + +/** + * @function cf_make_compute_shader_from_bytecode + * @category graphics + * @brief Creates a compute shader from SPIR-V bytecode. + * @param bytecode A bytecode blob from `cf_compile_shader_to_bytecode` with `CF_SHADER_STAGE_COMPUTE`. + * @related CF_ComputeShader cf_make_compute_shader cf_make_compute_shader_from_source cf_make_compute_shader_from_bytecode cf_destroy_compute_shader cf_dispatch_compute + */ +CF_API CF_ComputeShader CF_CALL cf_make_compute_shader_from_bytecode(CF_ShaderBytecode bytecode); + +/** + * @function cf_destroy_compute_shader + * @category graphics + * @brief Frees up a compute shader. + * @param shader The compute shader to destroy. + * @related CF_ComputeShader cf_make_compute_shader cf_destroy_compute_shader + */ +CF_API void CF_CALL cf_destroy_compute_shader(CF_ComputeShader shader); + +/** + * @function cf_compute_shader_reload + * @category graphics + * @brief Reloads a compute shader from its original file path. + * @param shader Pointer to the compute shader handle. Updated in-place on success. + * @return Returns true on success. + * @remarks Only works for compute shaders originally created with `cf_make_compute_shader`. The shader's + * path is recorded internally at creation time. + * @related CF_ComputeShader cf_make_compute_shader cf_destroy_compute_shader + */ +CF_API bool CF_CALL cf_compute_shader_reload(CF_ComputeShader* shader); + +//-------------------------------------------------------------------------------------------------- +// Storage Buffers. + +/** + * @struct CF_StorageBufferParams + * @category graphics + * @brief Parameters for creating a storage buffer. + * @related CF_StorageBuffer cf_storage_buffer_defaults cf_make_storage_buffer + */ +typedef struct CF_StorageBufferParams +{ + /* @member Size in bytes. */ + int size; + + /* @member GPU can read in compute stage (default true). */ + bool compute_readable; + + /* @member GPU can write in compute stage (default false). */ + bool compute_writable; + + /* @member GPU can read in graphics vertex/fragment stage (default false). */ + bool graphics_readable; +} CF_StorageBufferParams; +// @end + +/** + * @function cf_storage_buffer_defaults + * @category graphics + * @brief Returns sensible defaults for `CF_StorageBufferParams`. + * @param size The size in bytes of the storage buffer. + * @related CF_StorageBuffer CF_StorageBufferParams cf_make_storage_buffer + */ +CF_INLINE CF_StorageBufferParams cf_storage_buffer_defaults(int size) { + CF_StorageBufferParams params; + params.size = size; + params.compute_readable = true; + params.compute_writable = false; + params.graphics_readable = false; + return params; +} + +/** + * @function cf_make_storage_buffer + * @category graphics + * @brief Creates a GPU storage buffer. + * @param params Parameters for the storage buffer, see `CF_StorageBufferParams`. + * @related CF_StorageBuffer CF_StorageBufferParams cf_storage_buffer_defaults cf_destroy_storage_buffer cf_update_storage_buffer + */ +CF_API CF_StorageBuffer CF_CALL cf_make_storage_buffer(CF_StorageBufferParams params); + +/** + * @function cf_update_storage_buffer + * @category graphics + * @brief Uploads CPU data into a storage buffer. + * @param buffer The storage buffer to update. + * @param data Pointer to the data to upload. + * @param size Size in bytes of the data to upload. + * @related CF_StorageBuffer cf_make_storage_buffer cf_destroy_storage_buffer + */ +CF_API void CF_CALL cf_update_storage_buffer(CF_StorageBuffer buffer, const void* data, int size); + +/** + * @function cf_destroy_storage_buffer + * @category graphics + * @brief Frees up a storage buffer. + * @param buffer The storage buffer to destroy. + * @related CF_StorageBuffer cf_make_storage_buffer + */ +CF_API void CF_CALL cf_destroy_storage_buffer(CF_StorageBuffer buffer); + +//-------------------------------------------------------------------------------------------------- +// Compute Dispatch. + +/** + * @struct CF_ComputeDispatch + * @category graphics + * @brief Parameters for dispatching a compute shader. + * @remarks Read-write resources are bound at compute pass creation time (SDL_GPU requirement). + * Read-only storage resources are bound after the pipeline bind. + * Sampled textures and uniforms come from the material (name-matched). + * @related CF_ComputeShader CF_ComputeDispatch cf_compute_dispatch_defaults cf_dispatch_compute + */ +typedef struct CF_ComputeDispatch +{ + /* @member Read-write storage buffers (bound at pass creation). */ + CF_StorageBuffer* rw_buffers; + /* @member Number of read-write storage buffers. */ + int rw_buffer_count; + /* @member Read-write storage textures (bound at pass creation). */ + CF_Texture* rw_textures; + /* @member Number of read-write storage textures. */ + int rw_texture_count; + + /* @member Read-only storage buffers (bound after pipeline bind). */ + CF_StorageBuffer* ro_buffers; + /* @member Number of read-only storage buffers. */ + int ro_buffer_count; + /* @member Read-only storage textures (bound after pipeline bind). */ + CF_Texture* ro_textures; + /* @member Number of read-only storage textures. */ + int ro_texture_count; + + /* @member Workgroup count X. */ + int group_count_x; + /* @member Workgroup count Y. */ + int group_count_y; + /* @member Workgroup count Z. */ + int group_count_z; +} CF_ComputeDispatch; +// @end + +/** + * @function cf_compute_dispatch_defaults + * @category graphics + * @brief Returns sensible defaults for `CF_ComputeDispatch`. + * @param gx Workgroup count X. + * @param gy Workgroup count Y. + * @param gz Workgroup count Z. + * @related CF_ComputeDispatch cf_dispatch_compute + */ +CF_INLINE CF_ComputeDispatch cf_compute_dispatch_defaults(int gx, int gy, int gz) { + CF_ComputeDispatch d; + CF_MEMSET(&d, 0, sizeof(d)); + d.group_count_x = gx; + d.group_count_y = gy; + d.group_count_z = gz; + return d; +} + +/** + * @function cf_dispatch_compute + * @category graphics + * @brief Dispatches a compute shader. + * @param shader The compute shader to dispatch. + * @param material Holds uniforms and sampled textures for the compute stage (set via `cf_material_set_uniform_cs` / `cf_material_set_texture_cs`). + * @param dispatch Storage resources and workgroup counts. + * @related CF_ComputeShader CF_ComputeDispatch CF_Material cf_material_set_uniform_cs cf_material_set_texture_cs + */ +CF_API void CF_CALL cf_dispatch_compute(CF_ComputeShader shader, CF_Material material, CF_ComputeDispatch dispatch); + //-------------------------------------------------------------------------------------------------- // Render Canvases. @@ -928,6 +1225,64 @@ CF_API CF_Texture CF_CALL cf_canvas_get_depth_stencil_target(CF_Canvas canvas); */ CF_API void CF_CALL cf_clear_canvas(CF_Canvas canvas); +//-------------------------------------------------------------------------------------------------- +// Readback. + +/** + * @function cf_canvas_readback + * @category graphics + * @brief Initiates an async GPU-to-CPU copy of pixel data from a canvas. + * @param canvas The canvas to read back pixel data from. + * @return Returns a `CF_Readback` handle. Returns a zero handle on failure or if unsupported (e.g. web/Emscripten). + * @remarks Ends any active render pass silently. Each readback uses its own command buffer and fence. + * For screen readback, use `cf_canvas_readback(cf_app_get_canvas())`. + * @related CF_Readback cf_readback_ready cf_readback_data cf_readback_size cf_destroy_readback + */ +CF_API CF_Readback CF_CALL cf_canvas_readback(CF_Canvas canvas); + +/** + * @function cf_readback_ready + * @category graphics + * @brief Polls whether the async readback has completed. + * @param readback The readback handle to poll. + * @return Returns true once the GPU has finished the download. Caches the result after first true. + * @related CF_Readback cf_canvas_readback cf_readback_data cf_readback_size cf_destroy_readback + */ +CF_API bool CF_CALL cf_readback_ready(CF_Readback readback); + +/** + * @function cf_readback_data + * @category graphics + * @brief Copies readback pixel data into the provided buffer. + * @param readback The readback handle. + * @param data Pointer to the destination buffer. + * @param size Size of the destination buffer in bytes. + * @return Returns the number of bytes copied, or 0 if the readback is not yet ready. Copies `min(size, total)` bytes. + * @remarks Pixel format matches the canvas target format (typically RGBA8, 4 bytes per pixel). + * @related CF_Readback cf_canvas_readback cf_readback_ready cf_readback_size cf_destroy_readback + */ +CF_API int CF_CALL cf_readback_data(CF_Readback readback, void* data, int size); + +/** + * @function cf_readback_size + * @category graphics + * @brief Returns the total size in bytes of the readback data. + * @param readback The readback handle. + * @return Returns `w * h * bytes_per_pixel` for the readback, or 0 if the handle is invalid. + * @related CF_Readback cf_canvas_readback cf_readback_ready cf_readback_data cf_destroy_readback + */ +CF_API int CF_CALL cf_readback_size(CF_Readback readback); + +/** + * @function cf_destroy_readback + * @category graphics + * @brief Destroys a readback handle and releases all associated resources. + * @param readback The readback handle to destroy. + * @remarks If the readback is still in-flight, this will block until the GPU completes. + * @related CF_Readback cf_canvas_readback cf_readback_ready cf_readback_data cf_readback_size + */ +CF_API void CF_CALL cf_destroy_readback(CF_Readback readback); + //-------------------------------------------------------------------------------------------------- // Mesh. @@ -1012,7 +1367,7 @@ typedef enum CF_VertexFormat /** * @function cf_vertex_format_string * @category graphics - * @brief Frees up a `CF_Canvas` created by `cf_make_canvas`. + * @brief Returns a `CF_VertexFormat` converted to a C string. * @related CF_VertexFormat cf_vertex_format_string CF_VertexAttribute cf_mesh_set_attributes */ CF_INLINE const char* cf_vertex_format_string(CF_VertexFormat format) { @@ -1123,7 +1478,7 @@ CF_API void CF_CALL cf_mesh_update_index_data(CF_Mesh mesh, void* data, int coun * @brief Overwrites the instance data of a mesh. * @param mesh The mesh. * @param data A pointer to instance data. - * @param count Number of isntances in `data`. + * @param count Number of instances in `data`. * @related CF_Mesh cf_make_mesh cf_destroy_mesh cf_mesh_set_instance_buffer */ CF_API void CF_CALL cf_mesh_update_instance_data(CF_Mesh mesh, void* data, int count); @@ -1293,7 +1648,7 @@ typedef enum CF_BlendOp * @function cf_blend_op_string * @category graphics * @brief Returns a `CF_BlendOp` converted to a C string. - * @related CF_StencilOp cf_stencil_op_string CF_StencilFunction + * @related CF_BlendOp cf_blend_op_string CF_BlendFactor CF_RenderState */ CF_INLINE const char* cf_blend_op_string(CF_BlendOp op) { switch (op) { @@ -1474,7 +1829,7 @@ typedef struct CF_StencilParams * P = S + D * * Recap: P is the pixel to write, S is the source factor, while D is the destination factor. Usually the D (destination factor) is - * the old pixel value, while S (source factor) is a new image getting draw over old pixel contents. Therefor, P is the final color + * the old pixel value, while S (source factor) is a new image getting draw over old pixel contents. Therefore, P is the final color * after compositing a new image on top of an old image. * * Blend factors (see `CF_BlendFactor`) are components of a color, including the alpha component. The most common setup for your @@ -1762,12 +2117,37 @@ CF_API void CF_CALL cf_material_set_uniform_vs(CF_Material material, const char* */ CF_API void CF_CALL cf_material_set_uniform_fs(CF_Material material, const char* name, void* data, CF_UniformType type, int array_length); +/** + * @function cf_material_set_texture_cs + * @category graphics + * @brief Sets up a sampled texture for the compute shader stage. + * @param material The material. + * @param name The name of the texture as it appears in the compute shader. + * @param texture Data (usually an image) for a shader to access. + * @related CF_Material cf_material_set_uniform_cs cf_dispatch_compute + */ +CF_API void CF_CALL cf_material_set_texture_cs(CF_Material material, const char* name, CF_Texture texture); + +/** + * @function cf_material_set_uniform_cs + * @category graphics + * @brief Sets up a uniform value for the compute shader stage. + * @param material The material. + * @param name The name of the uniform as it appears in the compute shader. + * @param data The value of the uniform. + * @param type The type of the uniform. See `CF_UniformType`. + * @param array_length The number of elements in the uniform array. + * @remarks Same name-matching behavior as `cf_material_set_uniform_vs`/`cf_material_set_uniform_fs`. + * @related CF_Material cf_material_set_texture_cs cf_dispatch_compute + */ +CF_API void CF_CALL cf_material_set_uniform_cs(CF_Material material, const char* name, void* data, CF_UniformType type, int array_length); + /** * @function cf_material_clear_uniforms * @category graphics - * @brief Clears any uniforms previously set by `cf_material_set_uniform_vs` or `cf_material_set_uniform_fs`. + * @brief Clears any uniforms previously set by `cf_material_set_uniform_vs`, `cf_material_set_uniform_fs`, or `cf_material_set_uniform_cs`. * @param material The material. - * @related CF_UniformType CF_Material cf_make_material cf_destroy_material cf_material_set_render_state cf_material_set_texture_vs cf_material_set_texture_fs cf_material_set_uniform_vs cf_material_set_uniform_fs + * @related CF_UniformType CF_Material cf_make_material cf_destroy_material cf_material_set_render_state cf_material_set_texture_vs cf_material_set_texture_fs cf_material_set_uniform_vs cf_material_set_uniform_fs cf_material_set_uniform_cs */ CF_API void CF_CALL cf_material_clear_uniforms(CF_Material material); @@ -1835,17 +2215,19 @@ CF_API void CF_CALL cf_apply_scissor(int x, int y, int w, int h); * @category graphics * @brief Sets the stencil reference value. * @param reference The value to set the stencil reference to. + * @related cf_apply_stencil_reference cf_apply_blend_constants cf_apply_canvas cf_apply_viewport cf_apply_scissor */ CF_API void CF_CALL cf_apply_stencil_reference(int reference); /** * @function cf_apply_blend_constants * @category graphics - * @brief Sets the stencil reference value. + * @brief Sets the blend constant values used for blend modes that reference blend constants. * @param r The red blend constant. * @param g The green blend constant. * @param b The blue blend constant. * @param a The alpha blend constant. + * @related cf_apply_stencil_reference cf_apply_blend_constants cf_apply_canvas cf_apply_viewport cf_apply_scissor */ CF_API void CF_CALL cf_apply_blend_constants(float r, float g, float b, float a); @@ -1910,6 +2292,11 @@ CF_INLINE void destroy_canvas(CF_Canvas canvas) { cf_destroy_canvas(canvas); } CF_INLINE CF_Texture canvas_get_target(CF_Canvas canvas) { return cf_canvas_get_target(canvas); } CF_INLINE CF_Texture canvas_get_depth_stencil_target(CF_Canvas canvas) { return cf_canvas_get_depth_stencil_target(canvas); } CF_INLINE void clear_canvas(CF_Canvas canvas) { cf_clear_canvas(canvas); } +CF_INLINE CF_Readback canvas_readback(CF_Canvas canvas) { return cf_canvas_readback(canvas); } +CF_INLINE bool readback_ready(CF_Readback readback) { return cf_readback_ready(readback); } +CF_INLINE int readback_data(CF_Readback readback, void* data, int size) { return cf_readback_data(readback, data, size); } +CF_INLINE int readback_size(CF_Readback readback) { return cf_readback_size(readback); } +CF_INLINE void destroy_readback(CF_Readback readback) { cf_destroy_readback(readback); } CF_INLINE CF_Mesh make_mesh(int vertex_buffer_size_in_bytes, const CF_VertexAttribute* attributes, int attribute_count, int vertex_stride) { return cf_make_mesh(vertex_buffer_size_in_bytes, attributes, attribute_count, vertex_stride); } CF_INLINE void destroy_mesh(CF_Mesh mesh) { cf_destroy_mesh(mesh); } CF_INLINE void mesh_update_vertex_data(CF_Mesh mesh, void* data, int count) { cf_mesh_update_vertex_data(mesh, data, count); } @@ -1924,6 +2311,8 @@ CF_INLINE void material_set_texture_fs(CF_Material material, const char* name, C CF_INLINE void material_clear_textures(CF_Material material) { cf_material_clear_textures(material); } CF_INLINE void material_set_uniform_vs(CF_Material material, const char* name, void* data, CF_UniformType type, int array_length) { cf_material_set_uniform_vs(material, name, data, type, array_length); } CF_INLINE void material_set_uniform_fs(CF_Material material, const char* name, void* data, CF_UniformType type, int array_length) { cf_material_set_uniform_fs(material, name, data, type, array_length); } +CF_INLINE void material_set_texture_cs(CF_Material material, const char* name, CF_Texture texture) { cf_material_set_texture_cs(material, name, texture); } +CF_INLINE void material_set_uniform_cs(CF_Material material, const char* name, void* data, CF_UniformType type, int array_length) { cf_material_set_uniform_cs(material, name, data, type, array_length); } CF_INLINE void material_clear_uniforms(CF_Material material) { cf_material_clear_uniforms(material); } CF_INLINE void apply_canvas(CF_Canvas canvas, bool clear = false) { cf_apply_canvas(canvas, clear); } CF_INLINE void apply_viewport(int x, int y, int w, int h) { cf_apply_viewport(x, y, w, h); } @@ -1931,9 +2320,28 @@ CF_INLINE void apply_scissor(int x, int y, int w, int h) { cf_apply_scissor(x, y CF_INLINE void apply_mesh(CF_Mesh mesh) { cf_apply_mesh(mesh); } CF_INLINE void apply_shader(CF_Shader shader, CF_Material material) { cf_apply_shader(shader, material); } CF_INLINE void draw_elements() { cf_draw_elements(); } +CF_INLINE bool query_pixel_format(CF_PixelFormat format, CF_PixelFormatOp op) { return cf_query_pixel_format(format, op); } +CF_INLINE void texture_update_mip(CF_Texture texture, void* data, int size, int mip_level) { cf_texture_update_mip(texture, data, size, mip_level); } +CF_INLINE void generate_mipmaps(CF_Texture texture) { cf_generate_mipmaps(texture); } +CF_INLINE uint64_t texture_handle(CF_Texture texture) { return cf_texture_handle(texture); } +CF_INLINE uint64_t texture_binding_handle(CF_Texture texture) { return cf_texture_binding_handle(texture); } +CF_INLINE void mesh_set_index_buffer(CF_Mesh mesh, int index_buffer_size_in_bytes, int index_bit_count) { cf_mesh_set_index_buffer(mesh, index_buffer_size_in_bytes, index_bit_count); } +CF_INLINE void mesh_set_instance_buffer(CF_Mesh mesh, int instance_buffer_size_in_bytes, int instance_stride) { cf_mesh_set_instance_buffer(mesh, instance_buffer_size_in_bytes, instance_stride); } +CF_INLINE void clear_depth_stencil(float depth, uint32_t stencil) { cf_clear_depth_stencil(depth, stencil); } +CF_INLINE void apply_stencil_reference(int reference) { cf_apply_stencil_reference(reference); } +CF_INLINE void apply_blend_constants(float r, float g, float b, float a) { cf_apply_blend_constants(r, g, b, a); } +CF_INLINE CF_ComputeShader make_compute_shader(const char* path) { return cf_make_compute_shader(path); } +CF_INLINE CF_ComputeShader make_compute_shader_from_source(const char* src) { return cf_make_compute_shader_from_source(src); } +CF_INLINE CF_ComputeShader make_compute_shader_from_bytecode(CF_ShaderBytecode bytecode) { return cf_make_compute_shader_from_bytecode(bytecode); } +CF_INLINE void destroy_compute_shader(CF_ComputeShader shader) { cf_destroy_compute_shader(shader); } +CF_INLINE CF_StorageBufferParams storage_buffer_defaults(int size) { return cf_storage_buffer_defaults(size); } +CF_INLINE CF_StorageBuffer make_storage_buffer(CF_StorageBufferParams params) { return cf_make_storage_buffer(params); } +CF_INLINE void update_storage_buffer(CF_StorageBuffer buffer, const void* data, int size) { cf_update_storage_buffer(buffer, data, size); } +CF_INLINE void destroy_storage_buffer(CF_StorageBuffer buffer) { cf_destroy_storage_buffer(buffer); } +CF_INLINE CF_ComputeDispatch compute_dispatch_defaults(int gx, int gy, int gz) { return cf_compute_dispatch_defaults(gx, gy, gz); } +CF_INLINE void dispatch_compute(CF_ComputeShader shader, CF_Material material, CF_ComputeDispatch dispatch) { cf_dispatch_compute(shader, material, dispatch); } } -void cf_clear_canvas(CF_Canvas canvas_handle); CF_INLINE bool operator==(const CF_RenderState& a, const CF_RenderState& b) { return !CF_MEMCMP(&a, &b, sizeof(a)); } CF_INLINE bool operator==(CF_Shader a, CF_Shader b) { return a.id == b.id; } diff --git a/include/cute_guid.h b/include/cute_guid.h index 0ee26fe5c..c42d06450 100644 --- a/include/cute_guid.h +++ b/include/cute_guid.h @@ -38,7 +38,7 @@ typedef struct CF_Guid * @remarks The bytes are generated in a cryptographically secure way. * @related CF_Guid cf_make_guid cf_guid_equal */ -CF_API CF_Guid CF_CALL cf_make_guid(); +CF_API CF_Guid CF_CALL cf_make_guid(void); /** * @function cf_guid_equal diff --git a/include/cute_haptics.h b/include/cute_haptics.h index 44d8af42d..a0c44784b 100644 --- a/include/cute_haptics.h +++ b/include/cute_haptics.h @@ -127,7 +127,7 @@ CF_INLINE const char* cf_haptic_type_to_string(CF_HapticType type) /** * @struct CF_HapticLeftRight * @category haptic - * @brief The leftright haptic allows direct control of one larger and one smaller freqeuncy motors, as commonly found in game controllers. + * @brief The leftright haptic allows direct control of one larger and one smaller frequency motors, as commonly found in game controllers. * @related CF_Haptic CF_HapticType cf_haptic_open cf_haptic_close CF_HapticEffect cf_haptic_create_effect */ typedef struct CF_HapticLeftRight @@ -259,7 +259,7 @@ typedef struct CF_HapticData * @function cf_haptic_open * @category haptic * @brief Attempts to open a joypad for haptics use. - * @param player_index An index represeting the joypad for a particular player, starting at 0. + * @param player_index An index representing the joypad for a particular player, starting at 0. * @return Returns a new `CF_Haptic`. * @remarks Returns `NULL` upon any errors, including missing support from the underlying device. * @related CF_Haptic CF_Joypad cf_haptic_open cf_haptic_close cf_haptic_create_effect cf_haptic_run_effect cf_haptic_rumble_play @@ -471,7 +471,7 @@ CF_INLINE const char* to_string(HapticWaveType type) } } -CF_INLINE Haptic haptic_open(Joypad joypad) { return cf_haptic_open(joypad); } +CF_INLINE Haptic haptic_open(int player_index) { return cf_haptic_open(player_index); } CF_INLINE void haptic_close(Haptic haptic) { cf_haptic_close(haptic); } CF_INLINE bool haptic_supports(Haptic haptic, HapticType type) { return cf_haptic_supports(haptic,type); } CF_INLINE void haptic_set_gain(Haptic haptic, float gain) { cf_haptic_set_gain(haptic,gain); } diff --git a/include/cute_hashtable.h b/include/cute_hashtable.h deleted file mode 100644 index a4112e0ce..000000000 --- a/include/cute_hashtable.h +++ /dev/null @@ -1,762 +0,0 @@ -/* - Cute Framework - Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ - - This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info -*/ - -#ifndef CF_HASHTABLE_H -#define CF_HASHTABLE_H - -#include "cute_defines.h" -#include "cute_c_runtime.h" -#include "cute_alloc.h" - -//-------------------------------------------------------------------------------------------------- -// C API - -/** - * @function htbl - * @category hash - * @brief An empty macro used in the C API to markup hastables. - * @example > Showcase of base htbl features. - * htbl CF_V2* pts = NULL; - * hset(pts, 0, cf_v2(3, 5)); // Contructs a new table on-the-spot. The table is *hidden* behind `pts`. - * hset(pts, 10, cf_v2(-1, -1); - * hset(pts, -2, cf_v2(0, 0)); - * - * // Use `hget` to fetch values. - * CF_V2 a = hget(pts, 0); - * CF_V2 b = hget(pts, 10); - * CF_V2 c = hget(pts, -2); - * - * // Loop over {key, item} pairs like so: - * const uint64_t* keys = hkeys(pts); - * for (int i = 0; i < hcount(pts); ++i) { - * uint64_t key = keys[i]; - * CF_V2 v = pts[i]; - * // ... - * } - * - * hfree(pts); - * @remarks This is an optional and _completely_ empty macro. It's only purpose is to provide a bit of visual indication a type is a - * hashtable. One downside of the C-macro API is the opaque nature of the pointer type. Since the macros use polymorphism - * on typed pointers, there's no actual hashtable struct type. It can get really annoying to sometimes forget if a pointer is an - * array, a hashtable, or just a pointer. This macro can be used to markup the type to make it much more clear for function - * parameters or struct member definitions. It's saying "Hey, I'm a hashtable!" to mitigate this downside. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define htbl - -/** - * @function hset - * @category hash - * @brief Add's a {key, item} pair. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @param k The key for lookups. Each {key, item} pair must be unique. Keys are always typecasted to `uint64_t` e.g. you can use pointers as keys. - * @param ... An item to place in the table. - * @example > Set and get a few elements from a hashtable. - * htbl int* table = NULL; - * hset(table, 0, 5); - * hset(table, 1, 12); - * CF_ASSERT(hget(table, 0) == 5); - * CF_ASSERT(hget(table, 1) == 12); - * hfree(table); - * @return Returns a pointer to the item set into the table. - * @remarks If the item does not exist in the table it is added. The pointer returned is not stable. Internally the table can be resized, - * invalidating _all_ pointers to any elements within the table. Therefor, no items may store pointers to themselves or other items. - * Indices however, are totally fine. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hset(h, k, ...) cf_hashtable_set(h, k, (__VA_ARGS__)) - -/** - * @function hadd - * @category hash - * @brief Add's a {key, item} pair. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @param k The key for lookups. Each {key, item} pair must be unique. Keys are always typecasted to `uint64_t` e.g. you can use pointers as keys. - * @param ... An item to place in the table. - * @example > Set and get a few elements from a hashtable. - * htbl int* table = NULL; - * hadd(table, 0, 5); - * hadd(table, 1, 12); - * CF_ASSERT(hget(table, 0) == 5); - * CF_ASSERT(hget(table, 1) == 12); - * hfree(table); - * @return Returns a pointer to the item set into the table. - * @remarks This function works the same as `hset`. If the item already exists in the table, it's simply updated to a new value. - * The pointer returned is not stable. Internally the table can be resized, invalidating _all_ pointers to any elements - * within the table. Therefor, no items may store pointers to themselves or other items. Indices however, are totally fine. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hadd(h, k, ...) cf_hashtable_add(h, k, (__VA_ARGS__)) - -/** - * @function hget - * @category hash - * @brief Fetches the item that `k` maps to. - * @param h The hashtable. Can not be `NULL`. Needs to be a pointer to the type of items in the table. - * @param k The key for lookups. Each {key, item} pair must be unique. Keys are always typecasted to `uint64_t` e.g. you can use pointers as keys. - * @example > Set and get a few elements from a hashtable. - * htbl int* table = NULL; - * hadd(table, 0, 5); - * hadd(table, 1, 12); - * CF_ASSERT(hget(table, 0) == 5); - * CF_ASSERT(hget(table, 1) == 12); - * hfree(table); - * @return Returns a pointer to the item set into the table. - * @remarks Items are returned by value, not pointer. If the item doesn't exist a zero'd out item is instead returned. If you want to get a pointer - * (so you can see if it's `NULL` in case the item didn't exist, then use `hget_ptr`). You can also call `hhas` for a bool. This function does - * the same as `hfind`. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hget(h, k) cf_hashtable_get(h, k) - -/** - * @function hfind - * @category hash - * @brief Fetches the item that `k` maps to. - * @param h The hashtable. Can not be `NULL`. Needs to be a pointer to the type of items in the table. - * @param k The key for lookups. Each {key, item} pair must be unique. Keys are always typecasted to `uint64_t` e.g. you can use pointers as keys. - * @example > Set and get a few elements from a hashtable. - * htbl int* table = NULL; - * hadd(table, 0, 5); - * hadd(table, 1, 12); - * CF_ASSERT(hfind(table, 0) == 5); - * CF_ASSERT(hfind(table, 1) == 12); - * hfree(table); - * @return Returns a pointer to the item set into the table. - * @remarks Items are returned by value, not pointer. If the item doesn't exist a zero'd out item is instead returned. If you want to get a pointer - * (so you can see if it's `NULL` in case the item didn't exist, then use `hfind_ptr`). You can also call `hhas` for a bool. This function does - * the same as `hget`. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hfind(h, k) cf_hashtable_find(h, k) - -/** - * @function hget_ptr - * @category hash - * @brief Fetches a pointer to the item that `k` maps to. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @param k The key for lookups. Each {key, item} pair must be unique. Keys are always typecasted to `uint64_t` e.g. you can use pointers as keys. - * @example > Set and get a few elements from a hashtable. - * htbl CF_V2* table = NULL; - * hadd(table, 10, cf_v2(-1, 1)); - * CF_V2* v = hget_ptr(table, 10); - * CF_ASSERT(v); - * CF_ASSERT(v->x == -1); - * CF_ASSERT(v->y == 1); - * hfree(table); - * @return Returns a pointer to an item. Returns `NULL` if not found. - * @remarks If you want to fetch an item by value, you can use `hget` or `hfind`. Does the same thing as `hfind_ptr`. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hget_ptr(h, k) cf_hashtable_get_ptr(h, k) - -/** - * @function hfind_ptr - * @category hash - * @brief Fetches a pointer to the item that `k` maps to. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @param k The key for lookups. Each {key, item} pair must be unique. Keys are always typecasted to `uint64_t` e.g. you can use pointers as keys. - * @example > Set and get a few elements from a hashtable. - * htbl CF_V2* table = NULL; - * hadd(table, 10, cf_v2(-1, 1)); - * CF_V2* v = hfind_ptr(table, 10); - * CF_ASSERT(v); - * CF_ASSERT(v->x == -1); - * CF_ASSERT(v->y == 1); - * hfree(table); - * @return Returns a pointer to an item. Returns `NULL` if not found. - * @remarks If you want to fetch an item by value, you can use `hget` or `hfind`. Does the same thing as `hget_ptr`. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hfind_ptr(h, k) cf_hashtable_find_ptr(h, k) - -/** - * @function hhas - * @category hash - * @brief Check to see if an item exists in the table. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @param k The key for lookups. Each {key, item} pair must be unique. Keys are always typecasted to `uint64_t` e.g. you can use pointers as keys. - * @example > Checks if an item exists in the table. - * htbl v2* table = NULL; - * hadd(table, 10, V2(-1, 1)); - * CF_ASSERT(hhas(table, 10)); - * hfree(table); - * @return Returns true if the item was found, false otherwise. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hhas(h, k) cf_hashtable_has(h, k) - -/** - * @function hdel - * @category hash - * @brief Removes an item from the table. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @param k The key for lookups. Each {key, item} pair must be unique. Keys are always typecasted to `uint64_t` e.g. you can use pointers as keys. - * @example > Removes an item in the table. - * htbl CF_V2* table = NULL; - * hadd(table, 10, cf_v2(-1, 1)); - * hdel(table, 10); - * hfree(table); - * @remarks Asserts if the item does not exist. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hdel(h, k) cf_hashtable_del(h, k) - -/** - * @function hclear - * @category hash - * @brief Clears the hashtable. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @remarks The count of items will now be zero. Does not free any memory. Call `hfree` when you are done. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hclear(h) cf_hashtable_clear(h) - -/** - * @function hkeys - * @category hash - * @brief Get a const pointer to the array of keys. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @example > Loop over all {key, item} pairs of a table. - * htbl CF_V2* table = my_table(); - * const uint64_t* keys = hkeys(table); - * for (int i = 0; i < hcount(table); ++i) { - * uint64_t key = keys[i]; - * CF_V2 item = table[i]; - * // ... - * } - * @remarks The keys are type `uint64_t`. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hkeys(h) cf_hashtable_keys(h) - -/** - * @function hitems - * @category hash - * @brief Get a pointer to the array of items. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @example > Loop over all {key, item} pairs of a table. - * htbl CF_V2* table = my_table(); - * const uint64_t* keys = hkeys(table); - * for (int i = 0; i < hcount(table); ++i) { - * uint64_t key = keys[i]; - * CF_V2 item = table[i]; // Could also do `hitems(table)` here. - * // ... - * } - * @remarks This macro doesn't do much as `h` is already a valid pointer to the items. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hitems(h) cf_hashtable_items(h) - -/** - * @function hswap - * @category hash - * @brief Swaps internal ordering of two {key, item} pairs without ruining the hashing. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @param index_a Index to the first item to swap. - * @param index_b Index to the second item to swap. - * @example > Loop over all {key, item} pairs of a table. - * htbl CF_V2* table = my_table(); - * const uint64_t* keys = hkeys(table); - * for (int i = 0; i < hcount(table); ++i) { - * for (int j = 0; j < hcount(table); ++j) { - * if (my_need_swap(table, i, j)) { - * hswap(table, i, j); - * } - * } - * } - * @remarks Use this for e.g. implementing a priority queue on top of the hash table. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree hsort hssort hsisort - */ -#define hswap(h, index_a, index_b) cf_hashtable_swap(h, index_a, index_b) - -/** - * @function hsort - * @category hash - * @brief Sorts the {key, item} pairs in the table by keys. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @remarks The keys and items returned by `hkeys` and `hitems` will be sorted. Recall that all keys in the hashtable are treated - * as `uint64_t`, so the sorting simply sorts the keys from least to greatest as `uint64_t`. This is _not_ a stable sort. - * @related htbl hswap hsort hssort hsisort - */ -#define hsort(h) cf_hashtable_sort(h) - -/** - * @function hssort - * @category hash - * @brief Sorts the {key, item} pairs in the table by keys, where the keys are treated as C-strings. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @remarks The keys and items returned by `hkeys` and `hitems` will be sorted. Normally it's not valid to store strings as keys, - * since all keys are simply typecasted to `uint64_t`. However, if you use `sintern` each unique string has a unique and - * stable pointer, making them valid keys for hashtables. This is _not_ a stable sort. - * @related htbl hswap hsort hssort hsisort sintern - */ -#define hssort(h) cf_hashtable_ssort(h) - - -/** - * @function hsisort - * @category hash - * @brief Sorts the {key, item} pairs in the table by keys, where the keys are treated as C-strings (case ignored). - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @remarks The keys and items returned by `hkeys` and `hitems` will be sorted. Normally it's not valid to store strings as keys, - * since all keys are simply typecasted to `uint64_t`. However, if you use `sintern` each unique string has a unique and - * stable pointer, making them valid keys for hashtables. This is _not_ a stable sort. - * @related htbl hswap hsort hssort hsisort sintern - */ -#define hsisort(h) cf_hashtable_sisort(h) - -/** - * @function hsize - * @category hash - * @brief The number of {key, item} pairs in the table. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @remarks `h` can be `NULL`. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hsize(h) cf_hashtable_size(h) - -/** - * @function hcount - * @category hash - * @brief The number of {key, item} pairs in the table. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @remarks `h` can be `NULL`. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hcount(h) cf_hashtable_count(h) - -/** - * @function hfree - * @category hash - * @brief Frees up all resources used and sets `h` to `NULL`. - * @param h The hashtable. Can be `NULL`. Needs to be a pointer to the type of items in the table. - * @remarks `h` can be `NULL`. - * @related htbl hset hadd hget hfind hget_ptr hfind_ptr hhas hdel hclear hkeys hitems hswap hsize hcount hfree - */ -#define hfree(h) cf_hashtable_free(h) - -//-------------------------------------------------------------------------------------------------- -// Longform C API. - -#define cf_htbl -#define cf_hashtable_set(h, k, ...) ((h) ? (h) : (*(void**)&(h) = cf_hashtable_make_impl(sizeof(uint64_t), sizeof(*(h)), 1)), CF_HCANARY(h), (h)[-1] = (__VA_ARGS__), *(void**)&(h) = cf_hashtable_insert_impl(CF_HHDR(h), (uint64_t)k), (h) + CF_HHDR(h)->return_index) -#define cf_hashtable_add(h, k, ...) cf_hashtable_set(h, k, (__VA_ARGS__)) -#define cf_hashtable_get(h, k) ((h)[cf_hashtable_find_impl(CF_HHDR(h), (uint64_t)k)]) -#define cf_hashtable_find(h, k) cf_hashtable_get(h, k) -#define cf_hashtable_get_ptr(h, k) (cf_hashtable_find_impl(CF_HHDR(h), (uint64_t)k), CF_HHDR(h)->return_index < 0 ? NULL : (h) + CF_HHDR(h)->return_index) -#define cf_hashtable_find_ptr(h, k) cf_hashtable_get_ptr(h, k) -#define cf_hashtable_has(h, k) ((h) ? cf_hashtable_has_impl(CF_HHDR(h), (uint64_t)k) : false) -#define cf_hashtable_del(h, k) ((h) ? cf_hashtable_remove_impl(CF_HHDR(h), (uint64_t)k) : (void)0) -#define cf_hashtable_clear(h) ((h) ? CF_HCANARY(h), cf_hashtable_clear_impl(CF_HHDR(h)) : (void)0) -#define cf_hashtable_keys(h) (CF_HCANARY(h), h ? (const uint64_t*)cf_hashtable_keys_impl(CF_HHDR(h)) : (const uint64_t*)NULL) -#define cf_hashtable_items(h) (CF_HCANARY(h), h) -#define cf_hashtable_swap(h, index_a, index_b) (CF_HCANARY(h), cf_hashtable_swap_impl(CF_HHDR(h), index_a, index_b)) -#define cf_hashtable_sort(h) (*(void**)&(h) = cf_hashtable_sort_impl(CF_HHDR(h))) -#define cf_hashtable_ssort(h) (*(void**)&(h) = cf_hashtable_ssort_impl(CF_HHDR(h))) -#define cf_hashtable_sisort(h) (*(void**)&(h) = cf_hashtable_sisort_impl(CF_HHDR(h))) -#define cf_hashtable_size(h) (h ? cf_hashtable_count_impl(CF_HHDR(h)) : 0) -#define cf_hashtable_count(h) cf_hashtable_size(h) -#define cf_hashtable_free(h) do { CF_HCANARY(h); if (h) cf_hashtable_free_impl(CF_HHDR(h)); h = NULL; } while (0) - -//-------------------------------------------------------------------------------------------------- -// Hidden API - Not intended for direct use. - -#define CF_HHDR(h) (((CF_Hhdr*)(h - 1) - 1)) // Converts pointer from the user-array to table header. -#define CF_HCOOKIE 0xE6F7E359 // Magic number used for sanity/type checks. -#define CF_HCANARY(h) (h ? CF_ASSERT(CF_HHDR(h)->cookie == CF_HCOOKIE) : (void)0) // Sanity/type check. - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -typedef struct CF_Hslot -{ - uint32_t key_hash; - int item_index; - int base_count; -} CF_Hslot; - -typedef struct CF_Hhdr -{ - int key_size; - int item_size; - int item_capacity; - int count; - int slot_capacity; - CF_Hslot* slots; - void* items_key; - int* items_slot_index; - int return_index; - void* hidden_item; - void* items_data; - void* temp_key; - void* temp_item; - uint32_t cookie; -} CF_Hhdr; - -CF_API void* CF_CALL cf_hashtable_make_impl(int key_size, int item_size, int capacity); -CF_API void CF_CALL cf_hashtable_free_impl(CF_Hhdr* table); -CF_API void* CF_CALL cf_hashtable_insert_impl(CF_Hhdr* table, uint64_t key); -CF_API void* CF_CALL cf_hashtable_insert_impl2(CF_Hhdr* table, const void* key, const void* item); -CF_API void* CF_CALL cf_hashtable_insert_impl3(CF_Hhdr* table, const void* key); -CF_API void CF_CALL cf_hashtable_remove_impl(CF_Hhdr* table, uint64_t key); -CF_API void CF_CALL cf_hashtable_remove_impl2(CF_Hhdr* table, const void* key); -CF_API bool CF_CALL cf_hashtable_has_impl(CF_Hhdr* table, uint64_t key); -CF_API int CF_CALL cf_hashtable_find_impl(const CF_Hhdr* table, uint64_t key); -CF_API int CF_CALL cf_hashtable_find_impl2(const CF_Hhdr* table, const void* key); -CF_API int CF_CALL cf_hashtable_count_impl(const CF_Hhdr* table); -CF_API void* CF_CALL cf_hashtable_items_impl(const CF_Hhdr* table); -CF_API void* CF_CALL cf_hashtable_keys_impl(const CF_Hhdr* table); -CF_API void CF_CALL cf_hashtable_clear_impl(CF_Hhdr* table); -CF_API void CF_CALL cf_hashtable_swap_impl(CF_Hhdr* table, int index_a, int index_b); -CF_API void* CF_CALL cf_hashtable_sort_impl(CF_Hhdr* table); -CF_API void* CF_CALL cf_hashtable_ssort_impl(CF_Hhdr* table); -CF_API void* CF_CALL cf_hashtable_sisort_impl(CF_Hhdr* table); - -#ifdef __cplusplus -} -#endif // __cplusplus - -//-------------------------------------------------------------------------------------------------- -// C++ API - -#ifdef CF_CPP - -namespace Cute -{ - -// General purpose {key, item} pair mapping via internal hash table. -// Keys are treated as mere byte buffers (Plain Old Data). -// Items have contructors/destructors called, but are *not* allowed to store references/pointers to themselves. -template -struct Map -{ - Map(); - Map(const Map& other); - Map(Map&& other); - Map(int capacity); - ~Map(); - - T& get(const K& key); - T& find(const K& key) { return get(key); } - const T& get(const K& key) const; - const T& find(const K& key) const { return get(key); } - T* try_get(const K& key); - T* try_find(const K& key) { return try_get(key); } - const T* try_get(const K& key) const; - const T* try_find(const K& key) const { return try_get(key); } - bool has(const K& key) const { return try_get(key) ? true : false; } - - T* insert(const K& key); - T* insert(const K& key, const T& val); - T* insert(const K& key, T&& val); - T* add(const K& key) { return insert(key); } - T* add(const K& key, const T& val) { return insert(key, val); } - T* add(const K& key, T&& val) { return insert(key, val); } - void remove(const K& key); - - void clear(); - - int count() const; - T* items(); - const T* items() const; - T* vals() { return items(); } - const T* vals() const { return items(); } - K* keys(); - const K* keys() const; - - void swap(int index_a, int index_b); - - template - void sort_by_keys(P predicate); - - template - void sort_by_items(P predicate); - - Map& operator=(const Map& rhs); - Map& operator=(Map&& rhs); - -private: - CF_Hhdr* m_table = NULL; - - template - void sort_keys(int offset, int count, P predicate); - - template - void sort_items(int offset, int count, P predicate); -}; - -// ------------------------------------------------------------------------------------------------- - -template -Map::Map() -{ - m_table = CF_HHDR((T*)cf_hashtable_make_impl(sizeof(K), sizeof(T), 32)); -} - -template -Map::Map(const Map& other) -{ - int n = other.count(); - const T* items = other.items(); - const K* keys = other.keys(); - if (n) { - m_table = CF_HHDR((T*)cf_hashtable_make_impl(sizeof(K), sizeof(T), n)); - for (int i = 0; i < n; ++i) { - insert(keys[i], items[i]); - } - } -} - -template -Map::Map(Map&& other) -{ - m_table = other.m_table; - other.m_table = NULL; -} - -template -Map::Map(int capacity) -{ - m_table = CF_HHDR((T*)cf_hashtable_make_impl(sizeof(K), sizeof(T), capacity)); -} - -template -Map::~Map() -{ - T* elements = items(); - for (int i = 0; i < count(); ++i) { - (elements + i)->~T(); - } - cf_hashtable_free_impl(m_table); - m_table = NULL; -} - -template -T& Map::get(const K& key) -{ - int index = cf_hashtable_find_impl2(m_table, &key); - return items()[index]; -} - -template -const T& Map::get(const K& key) const -{ - int index = cf_hashtable_find_impl2(m_table, &key); - return items()[index]; -} - -template -T* Map::try_get(const K& key) -{ - if (!m_table) return NULL; - int index = cf_hashtable_find_impl2(m_table, &key); - if (index >= 0) return items() + index; - else return NULL; -} - -template -const T* Map::try_get(const K& key) const -{ - if (!m_table) return NULL; - int index = cf_hashtable_find_impl2(m_table, &key); - if (index >= 0) return items() + index; - else return NULL; -} - -template -T* Map::insert(const K& key) -{ - m_table = CF_HHDR((T*)cf_hashtable_insert_impl3(m_table, &key)); - int index = m_table->return_index; - if (index < 0) return NULL; - T* result = items() + index; - CF_PLACEMENT_NEW(result) T(); - return result; -} - -template -T* Map::insert(const K& key, const T& val) -{ - m_table = CF_HHDR((T*)cf_hashtable_insert_impl2(m_table, &key, &val)); - int index = m_table->return_index; - if (index < 0) return NULL; - T* result = items() + index; - CF_PLACEMENT_NEW(result) T(val); - return result; -} - -template -T* Map::insert(const K& key, T&& val) -{ - m_table = CF_HHDR((T*)cf_hashtable_insert_impl2(m_table, &key, &val)); - int index = m_table->return_index; - if (index < 0) return NULL; - T* result = items() + index; - CF_PLACEMENT_NEW(result) T(cf_move(val)); - return result; -} - -template -void Map::remove(const K& key) -{ - T* slot = try_find(key); - if (slot) { - slot->~T(); - cf_hashtable_remove_impl2(m_table, &key); - } -} - -template -void Map::clear() -{ - T* elements = items(); - for (int i = 0; i < count(); ++i) { - (elements + i)->~T(); - } - if (m_table) cf_hashtable_clear_impl(m_table); -} - -template -int Map::count() const -{ - return m_table ? cf_hashtable_count_impl(m_table) : 0; -} - -template -T* Map::items() -{ - return m_table ? (T*)(m_table + 1) + 1 : NULL; -} - -template -const T* Map::items() const -{ - return m_table ? (const T*)(m_table + 1) + 1 : NULL; -} - -template -K* Map::keys() -{ - return m_table ? (K*)cf_hashtable_keys_impl(m_table) : NULL; -} - -template -const K* Map::keys() const -{ - return m_table ? (const K*)cf_hashtable_keys_impl(m_table) : NULL; -} - -template -void Map::swap(int index_a, int index_b) -{ - cf_hashtable_swap_impl(m_table, index_a, index_b); -} - -template -Map& Map::operator=(const Map& rhs) -{ - clear(); - int n = rhs.count(); - const T* items = rhs.items(); - const K* keys = rhs.keys(); - if (n) { - m_table = CF_HHDR((T*)cf_hashtable_make_impl(sizeof(K), sizeof(T), n)); - for (int i = 0; i < n; ++i) { - insert(keys[i], items[i]); - } - } - return *this; -} - -template -Map& Map::operator=(Map&& rhs) -{ - this->~Map(); - m_table = rhs.m_table; - rhs.m_table = NULL; - return *this; -} - -template -template -void Map::sort_keys(int offset, int count, P predicate) -{ - if (count <= 1) return; - - auto key = [=, this](int index) -> K& { - uint8_t* keys = (uint8_t*)m_table->items_key; - void* k = keys + index * m_table->key_size; - return *(K*)k; - }; - auto swap = [=, this](int ia, int ib) { cf_hashtable_swap_impl(m_table, offset + ia, offset + ib); }; - - const K& pivot_key = key(count - 1); - int lo = 0; - for (int hi = 0; hi < count - 1; ++hi) { - const K& hi_key = key(hi); - if (predicate(hi_key, pivot_key)) { - swap(lo, hi); - lo++; - } - } - - swap(count - 1, lo); - - sort_keys(0, lo, predicate); - sort_keys(lo + 1, count - 1 - lo, predicate); -} - -template -template -void Map::sort_by_keys(P predicate) -{ - sort_keys(0, m_table->count, predicate); -} - -template -template -void Map::sort_items(int offset, int count, P predicate) -{ - if (count <= 1) return; - - auto item = [=, this](int index) -> T& { - uint8_t* items = (uint8_t*)m_table->items_data; - void* item = items + index * m_table->item_size; - return *(T*)item; - }; - auto swap = [=, this](int ia, int ib) { cf_hashtable_swap_impl(m_table, offset + ia, offset + ib); }; - - const K& pivot = item(count - 1); - int lo = 0; - for (int hi = 0; hi < count - 1; ++hi) { - const K& val = item(hi); - if (predicate(val, pivot)) { - swap(lo, hi); - lo++; - } - } - - swap(count - 1, lo); - - sort_items(0, lo, predicate); - sort_items(lo + 1, count - 1 - lo, predicate); -} - -template -template -void Map::sort_by_items(P predicate) -{ - sort_items(0, m_table->count, predicate); -} - -} - -#endif // CF_CPP - -#endif // CF_HASHTABLE_H diff --git a/include/cute_https.h b/include/cute_https.h index 6417bef16..5447558fd 100644 --- a/include/cute_https.h +++ b/include/cute_https.h @@ -12,7 +12,7 @@ #include "cute_result.h" #include "cute_c_runtime.h" #include "cute_array.h" -#include "cute_hashtable.h" +#include "cute_map.h" #ifndef CF_EMSCRIPTEN @@ -181,7 +181,7 @@ typedef enum CF_HttpsResult /** * @function cf_https_result_to_string * @category web - * @brief Convert an enum `CF_HttpsState` to a c-style string. + * @brief Convert a `CF_HttpsResult` to a C string. * @param state The state to convert to a string. * @related CF_HttpsRequest cf_https_process cf_https_result_to_string */ @@ -265,10 +265,10 @@ CF_API int CF_CALL cf_https_response_headers_count(CF_HttpsResponse response); * @brief Returns an array of response headers. * @remarks Intended to be used with `cf_https_response_headers_count`. Do not free this array, it will * get cleaned up when the originating `CF_HttpsRequest` is destroyed via `cf_https_destroy`. - * If you're familiar with `htbl` you may treat this pointer as a hashtable key'd by strings. + * The returned array is indexed by position, use `cf_https_response_find_header` to look up by name. * @related CF_HttpsHeader CF_HttpsResponse cf_https_response_find_header cf_https_response_headers */ -CF_API htbl const CF_HttpsHeader* CF_CALL cf_https_response_headers(CF_HttpsResponse response); +CF_API dyna const CF_HttpsHeader* CF_CALL cf_https_response_headers(CF_HttpsResponse response); #ifdef __cplusplus } @@ -294,7 +294,7 @@ CF_INLINE int https_response_content_length(CF_HttpsResponse response) { return CF_INLINE char* https_response_content(CF_HttpsResponse response) { return cf_https_response_content(response); } CF_INLINE CF_HttpsHeader https_response_find_header(CF_HttpsResponse response, const char* header_name) { return cf_https_response_find_header(response, header_name); } CF_INLINE int https_response_headers_count(CF_HttpsResponse response) { return cf_https_response_headers_count(response); } -CF_INLINE htbl const CF_HttpsHeader* https_response_headers(CF_HttpsResponse response) { return cf_https_response_headers(response); } +CF_INLINE dyna const CF_HttpsHeader* https_response_headers(CF_HttpsResponse response) { return cf_https_response_headers(response); } } diff --git a/include/cute_image.h b/include/cute_image.h index e301808cb..683ed8aaf 100644 --- a/include/cute_image.h +++ b/include/cute_image.h @@ -26,7 +26,7 @@ extern "C" { * @remarks You probably do not need this. In Cute Framework loading images manually is not often * necessary, as most games can use CF's [Draw API](https://randygaul.github.io/cute_framework/topics/drawing) to get sprites onto the screen. * However, a good use case is, for example, if you want to implement some custom shader and feed it a texture. - * @related CF_Image CF_ImageIndexed cf_image_load_png cf_image_premultiply + * @related CF_Image CF_ImageIndexed cf_image_load_png cf_image_save_png cf_image_save_png_to_memory cf_image_premultiply */ typedef struct CF_Image { @@ -79,7 +79,7 @@ typedef struct CF_ImageIndexed * @param virtual_path A virtual path to the image file. See [Virtual File System](https://randygaul.github.io/cute_framework/topics/virtual_file_system). * @param img Out parameter for the image. * @return Check the `CF_Result` for errors. - * @related CF_Image cf_image_load_png cf_image_free cf_image_load_png_from_memory cf_image_load_png_wh cf_image_load_png_indexed cf_image_premultiply + * @related CF_Image cf_image_load_png cf_image_free cf_image_load_png_from_memory cf_image_load_png_wh cf_image_load_png_indexed cf_image_save_png cf_image_save_png_to_memory cf_image_premultiply */ CF_API CF_Result CF_CALL cf_image_load_png(const char* virtual_path, CF_Image* img); @@ -91,20 +91,20 @@ CF_API CF_Result CF_CALL cf_image_load_png(const char* virtual_path, CF_Image* i * @param size The number of bytes in the `data` pointer. * @param img Out parameter for the image. * @return Check the `CF_Result` for errors. - * @related CF_Image cf_image_load_png cf_image_load_png_from_memory cf_image_load_png_wh cf_image_load_png_indexed cf_image_premultiply + * @related CF_Image cf_image_load_png cf_image_load_png_from_memory cf_image_load_png_wh cf_image_load_png_indexed cf_image_save_png cf_image_save_png_to_memory cf_image_premultiply */ CF_API CF_Result CF_CALL cf_image_load_png_from_memory(const void* data, int size, CF_Image* img); /** * @function cf_image_load_png_wh * @category image - * @brief Loads a just the width/height out of a png image, without processing the pixels. + * @brief Loads just the width/height out of a png image, without processing the pixels. * @param data Pointer to the png file in memory. * @param size The number of bytes in the `data` pointer. * @param w Out parameter for the width of the image. * @param h Out parameter for the height of the image. * @return Check the `CF_Result` for errors. - * @related CF_Image cf_image_load_png cf_image_load_png_from_memory cf_image_load_png_wh cf_image_load_png_indexed cf_image_premultiply + * @related CF_Image cf_image_load_png cf_image_load_png_from_memory cf_image_load_png_wh cf_image_load_png_indexed cf_image_save_png cf_image_save_png_to_memory cf_image_premultiply */ CF_API CF_Result CF_CALL cf_image_load_png_wh(const void* data, int size, int* w, int* h); @@ -114,7 +114,7 @@ CF_API CF_Result CF_CALL cf_image_load_png_wh(const void* data, int size, int* w * @brief Frees a png image. * @param img The image to free. * @return Check the `CF_Result` for errors. - * @related CF_Image cf_image_load_png cf_image_free cf_image_load_png_from_memory cf_image_load_png_wh cf_image_load_png_indexed cf_image_premultiply + * @related CF_Image cf_image_load_png cf_image_free cf_image_load_png_from_memory cf_image_load_png_wh cf_image_load_png_indexed cf_image_save_png cf_image_save_png_to_memory cf_image_premultiply */ CF_API void CF_CALL cf_image_free(CF_Image* img); @@ -125,7 +125,7 @@ CF_API void CF_CALL cf_image_free(CF_Image* img); * @param virtual_path A virtual path to the image file. See [Virtual File System](https://randygaul.github.io/cute_framework/topics/virtual_file_system). * @param img Out parameter for the image. * @return Check the `CF_Result` for errors. - * @related CF_ImageIndexed cf_image_load_png_indexed cf_image_load_png_from_memory_indexed cf_image_free_indexed cf_image_depallete + * @related CF_ImageIndexed cf_image_load_png_indexed cf_image_load_png_from_memory_indexed cf_image_free_indexed cf_image_depalette */ CF_API CF_Result CF_CALL cf_image_load_png_indexed(const char* virtual_path, CF_ImageIndexed* img); @@ -137,7 +137,7 @@ CF_API CF_Result CF_CALL cf_image_load_png_indexed(const char* virtual_path, CF_ * @param size The number of bytes in the `data` pointer. * @param img Out parameter for the image. * @return Check the `CF_Result` for errors. - * @related CF_ImageIndexed cf_image_load_png_indexed cf_image_load_png_from_memory_indexed cf_image_free_indexed cf_image_depallete + * @related CF_ImageIndexed cf_image_load_png_indexed cf_image_load_png_from_memory_indexed cf_image_free_indexed cf_image_depalette */ CF_API CF_Result CF_CALL cf_image_load_png_from_memory_indexed(const void* data, int size, CF_ImageIndexed* img); @@ -147,22 +147,48 @@ CF_API CF_Result CF_CALL cf_image_load_png_from_memory_indexed(const void* data, * @brief Frees an indexed png image. * @param img The image to free. * @return Check the `CF_Result` for errors. - * @related CF_ImageIndexed cf_image_load_png_indexed cf_image_load_png_from_memory_indexed cf_image_free_indexed cf_image_depallete + * @related CF_ImageIndexed cf_image_load_png_indexed cf_image_load_png_from_memory_indexed cf_image_free_indexed cf_image_depalette */ CF_API void CF_CALL cf_image_free_indexed(CF_ImageIndexed* img); +// ------------------------------------------------------------------------------------------------- +// PNG saving. + +/** + * @function cf_image_save_png + * @category image + * @brief Saves an image to a PNG file. + * @param virtual_path A virtual path to write the PNG to. See [Virtual File System](https://randygaul.github.io/cute_framework/topics/virtual_file_system). + * @param img The image to save. + * @return Check the `CF_Result` for errors. + * @related CF_Image cf_image_load_png cf_image_free cf_image_save_png cf_image_save_png_to_memory + */ +CF_API CF_Result CF_CALL cf_image_save_png(const char* virtual_path, CF_Image* img); + +/** + * @function cf_image_save_png_to_memory + * @category image + * @brief Saves an image to a PNG in memory. + * @param img The image to save. + * @param out_data Out parameter for the PNG data. Caller must free with `cf_free`. + * @param out_size Out parameter for the size of the PNG data in bytes. + * @return Check the `CF_Result` for errors. + * @related CF_Image cf_image_load_png cf_image_free cf_image_save_png cf_image_save_png_to_memory + */ +CF_API CF_Result CF_CALL cf_image_save_png_to_memory(CF_Image* img, void** out_data, int* out_size); + // ------------------------------------------------------------------------------------------------- // Image operations. /** - * @function cf_image_depallete + * @function cf_image_depalette * @category image * @brief Converts a paletted image to a typical image with an array of pixels. * @param img The image to depalette. * @return Returns an image without a palette. * @related CF_Image CF_ImageIndexed cf_image_load_png_indexed */ -CF_API CF_Image CF_CALL cf_image_depallete(CF_ImageIndexed* img); +CF_API CF_Image CF_CALL cf_image_depalette(CF_ImageIndexed* img); /** * @function cf_image_premultiply @@ -187,8 +213,7 @@ CF_API void CF_CALL cf_image_flip_horizontal(CF_Image* img); * @function cf_debug_dump_greyscale_pixels * @category image * @brief Saves out greyscale image to a png file on disk. - * @remarks Does *not* use the virtual file system. Instead it uses plain old platform-dependent path notation. This - * function is not really optimized whatsoever and outputs poorly compressed file sizes. + * @remarks Does *not* use the virtual file system. Instead it uses plain old platform-dependent path notation. * @related CF_Image cf_debug_dump_greyscale_pixels cf_debug_dump_pixels */ CF_API void CF_CALL cf_debug_dump_greyscale_pixels(const char* path, uint8_t* pixels, int w, int h); @@ -197,8 +222,7 @@ CF_API void CF_CALL cf_debug_dump_greyscale_pixels(const char* path, uint8_t* pi * @function cf_debug_dump_pixels * @category image * @brief Saves out an image to a png file on disk. - * @remarks Does *not* use the virtual file system. Instead it uses plain old platform-dependent path notation. This - * function is not really optimized whatsoever and outputs poorly compressed file sizes. + * @remarks Does *not* use the virtual file system. Instead it uses plain old platform-dependent path notation. * @related CF_Image cf_debug_dump_greyscale_pixels cf_debug_dump_pixels */ CF_API void CF_CALL cf_debug_dump_pixels(const char* path, CF_Pixel* pixels, int w, int h); @@ -227,10 +251,16 @@ CF_INLINE CF_Result image_load_png_indexed(const char* virtual_path, CF_ImageInd CF_INLINE CF_Result image_load_png_mem_indexed(const void* data, int size, CF_ImageIndexed* img) { return cf_image_load_png_from_memory_indexed(data, size, img); } CF_INLINE void image_free(CF_ImageIndexed* img) { cf_image_free_indexed(img); } +// ------------------------------------------------------------------------------------------------- +// PNG saving. + +CF_INLINE CF_Result image_save_png(const char* path, CF_Image* img) { return cf_image_save_png(path, img); } +CF_INLINE CF_Result image_save_png_to_memory(CF_Image* img, void** out_data, int* out_size) { return cf_image_save_png_to_memory(img, out_data, out_size); } + // ------------------------------------------------------------------------------------------------- // Image operations. -CF_INLINE CF_Image image_depallete(CF_ImageIndexed* img) { return cf_image_depallete(img); } +CF_INLINE CF_Image image_depalette(CF_ImageIndexed* img) { return cf_image_depalette(img); } CF_INLINE void image_premultiply(CF_Image* img) { cf_image_premultiply(img); } CF_INLINE void image_flip_horizontal(CF_Image* img) { cf_image_flip_horizontal(img); } CF_INLINE void debug_dump_greyscale_pixels(const char* path, uint8_t* pixels, int w, int h) { cf_debug_dump_greyscale_pixels(path, pixels, w, h); } diff --git a/include/cute_input.h b/include/cute_input.h index e927f36a7..70046e04c 100644 --- a/include/cute_input.h +++ b/include/cute_input.h @@ -662,7 +662,7 @@ CF_API void CF_CALL cf_register_key_callback(void (*key_callback)(CF_KeyButton k * @function cf_mouse_x * @category input * @brief Returns the current mouse x-coordinate in pixels. - * @remarks (0, 0) is the top-left of the screen, y-downards. + * @remarks (0, 0) is the top-left of the screen, y-downwards. * @related CF_MouseButton cf_mouse_down cf_mouse_x cf_mouse_y cf_mouse_motion_x cf_mouse_motion_y */ CF_API float CF_CALL cf_mouse_x(void); @@ -806,7 +806,7 @@ CF_API void CF_CALL cf_input_text_add_utf8(const char* text); /** * @function cf_input_text_pop_utf32 * @category input - * @brief Pops a utf8 codepoint off of the input buffer of the application. + * @brief Pops a utf32 codepoint off of the input buffer of the application. * @remarks The input text functions are for dealing with text input. Not all text inputs come from a single key-stroke, as some are comprised of * multiple keystrokes, especially when dealing with non-Latin based inputs. * @related cf_input_text_add_utf8 cf_input_text_pop_utf32 cf_input_text_has_data cf_input_text_clear @@ -1038,6 +1038,11 @@ CF_INLINE void input_set_ime_rect(int x, int y, int w, int h) { cf_input_set_ime CF_INLINE bool input_get_ime_composition(CF_ImeComposition* composition) { return cf_input_get_ime_composition(composition); } +CF_INLINE void register_key_callback(void (*key_callback)(CF_KeyButton key, bool true_down_false_up)) { cf_register_key_callback(key_callback); } +CF_INLINE float mouse_motion_x() { return cf_mouse_motion_x(); } +CF_INLINE float mouse_motion_y() { return cf_mouse_motion_y(); } +CF_INLINE void mouse_set_relative_mode(bool true_for_relative) { cf_mouse_set_relative_mode(true_for_relative); } +CF_INLINE int touch_get_all(CF_Touch** touches) { return cf_touch_get_all(touches); } CF_INLINE bool touch_get(uint64_t id, CF_Touch* touch) { return cf_touch_get(id,touch); } } diff --git a/include/cute_json.h b/include/cute_json.h index 124758f2c..24b6c91e6 100644 --- a/include/cute_json.h +++ b/include/cute_json.h @@ -342,7 +342,7 @@ typedef struct CF_JIter * const char* val = cf_json_get_string(cf_json_iter_val(i)); * printf("%s\n", val); * } - * @remarks The `CF_JIter` can be used in foor loops, and can traverse both JSON arrays and objects. When + * @remarks The `CF_JIter` can be used in for loops, and can traverse both JSON arrays and objects. When * traversing arrays do not call `cf_json_iter_key`. * @related CF_JVal cf_json_get cf_json_array_at cf_json_array_get cf_json_iter cf_json_iter_remove */ @@ -981,6 +981,7 @@ struct JVal CF_INLINE bool is_null() const { return cf_json_is_null(v); } CF_INLINE bool is_int() const { return cf_json_is_int(v); } CF_INLINE bool is_float() const { return cf_json_is_float(v); } + CF_INLINE bool is_bool() const { return cf_json_is_bool(v); } CF_INLINE bool is_string() const { return cf_json_is_string(v); } CF_INLINE bool is_array() const { return cf_json_is_array(v); } CF_INLINE bool is_object() const { return cf_json_is_object(v); } @@ -1026,6 +1027,8 @@ struct JVal CF_INLINE void add(const char* val) { cf_json_array_add_string(d, v, val); } CF_INLINE void add_range(const char* begin, const char* end) { cf_json_array_add_string_range(d, v, begin, end); } CF_INLINE void add(JVal val) { cf_json_array_add(v, val.v); } + CF_INLINE JVal add_array() { return JVal(cf_json_array_add_array(d, v), d); } + CF_INLINE JVal add_object() { return JVal(cf_json_array_add_object(d, v), d); } CF_INLINE JVal pop() { return JVal(cf_json_array_pop(v), d); } // Adding properties. diff --git a/include/cute_map.h b/include/cute_map.h new file mode 100644 index 000000000..447ac7899 --- /dev/null +++ b/include/cute_map.h @@ -0,0 +1,554 @@ +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +#ifndef CF_MAP_H +#define CF_MAP_H + +#include "cute_defines.h" +#include "cute_c_runtime.h" +#include "cute_alloc.h" +#include "cute/ckit.h" + +// Shortform map macros (map_get, map_set, etc.) are provided by ckit.h. +// You may use them directly if you wish, or use the cf_ longform prefixes below. + +//-------------------------------------------------------------------------------------------------- +// C API + +/** + * @function CF_MAP + * @category map + * @brief Declares a map (hashtable) type for values of type `T`. + * @param T The value type stored in the map. + * @example > Showcase of basic map features. + * CF_MAP(CF_V2) pts = NULL; + * map_set(pts, 0, cf_v2(3, 5)); // Constructs a new table on-the-spot. The table is *hidden* behind `pts`. + * map_set(pts, 10, cf_v2(-1, -1)); + * map_set(pts, 2, cf_v2(0, 0)); + * + * // Use `map_get` to fetch values. + * CF_V2 a = map_get(pts, 0); + * CF_V2 b = map_get(pts, 10); + * CF_V2 c = map_get(pts, 2); + * + * // Loop over {key, item} pairs like so: + * uint64_t* keys = map_keys(pts); + * for (int i = 0; i < map_size(pts); ++i) { + * uint64_t key = keys[i]; + * CF_V2 v = pts[i]; + * // ... + * } + * + * map_free(pts); + * @remarks `CF_MAP(T)` resolves to `T*`. The map data structure is hidden just before the pointer in memory + * (stretchy-buffer style). An empty/uninitialized map is simply `NULL`. One downside of the C-macro + * API is the opaque nature of the pointer type. Since the macros use polymorphism on typed pointers, + * there's no actual map struct type visible. `CF_MAP(T)` helps visually indicate a type is a map, not + * just a plain pointer. Keys are always `uint64_t`. Use `sintern` for string keys (casting the pointer + * to `uint64_t`). + * @related cf_map_set cf_map_get cf_map_get_ptr cf_map_has cf_map_del cf_map_clear cf_map_keys cf_map_items cf_map_swap cf_map_size cf_map_free + */ +#define CF_MAP(T) CK_MAP(T) + +/** + * @function cf_map_set + * @category map + * @brief Adds or updates a {key, item} pair in the map. + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @param k The key for lookups. Keys are always typecast to `uint64_t`, so you can use pointers as keys. + * @param v The item to place in the map. + * @example > Set and get a few elements from a map. + * CF_MAP(int) table = NULL; + * map_set(table, 0, 5); + * map_set(table, 1, 12); + * CF_ASSERT(map_get(table, 0) == 5); + * CF_ASSERT(map_get(table, 1) == 12); + * map_free(table); + * @remarks If the key already exists, the value is overwritten. The map may reallocate internally, invalidating + * _all_ pointers to elements within the map. Therefore, items may not store pointers to themselves or + * other items in the same map. Indices, however, are stable. + * @related CF_MAP cf_map_get cf_map_get_ptr cf_map_has cf_map_del cf_map_clear cf_map_keys cf_map_items cf_map_swap cf_map_size cf_map_free + */ +#define cf_map_set(m, k, v) map_set(m, k, v) + +/** + * @function cf_map_get + * @category map + * @brief Fetches the item that `k` maps to. + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @param k The key for lookups. Keys are always typecast to `uint64_t`. + * @return Returns the item by value. If the key doesn't exist, returns a zero'd item. + * @example > Set and get a few elements from a map. + * CF_MAP(int) table = NULL; + * map_set(table, 0, 5); + * map_set(table, 1, 12); + * CF_ASSERT(map_get(table, 0) == 5); + * CF_ASSERT(map_get(table, 1) == 12); + * map_free(table); + * @remarks Items are returned by value, not pointer. If you want a pointer (to check for `NULL` when the item + * doesn't exist), use `map_get_ptr`. You can also call `map_has` to check existence. + * @related CF_MAP cf_map_set cf_map_get_ptr cf_map_has cf_map_del cf_map_clear cf_map_keys cf_map_items cf_map_swap cf_map_size cf_map_free + */ +#define cf_map_get(m, k) map_get(m, k) + +/** + * @function cf_map_get_ptr + * @category map + * @brief Fetches a pointer to the item that `k` maps to. + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @param k The key for lookups. Keys are always typecast to `uint64_t`. + * @return Returns a pointer to the item, or `NULL` if not found. + * @example > Get a pointer to an element in a map. + * CF_MAP(CF_V2) table = NULL; + * map_set(table, 10, cf_v2(-1, 1)); + * CF_V2* v = map_get_ptr(table, 10); + * CF_ASSERT(v); + * CF_ASSERT(v->x == -1); + * CF_ASSERT(v->y == 1); + * map_free(table); + * @remarks If you want to fetch an item by value, use `map_get`. The returned pointer is not stable across + * insertions -- adding new items may cause reallocation and invalidate all pointers. + * @related CF_MAP cf_map_set cf_map_get cf_map_has cf_map_del cf_map_clear cf_map_keys cf_map_items cf_map_swap cf_map_size cf_map_free + */ +#define cf_map_get_ptr(m, k) map_get_ptr(m, k) + +/** + * @function cf_map_has + * @category map + * @brief Check if a key exists in the map. + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @param k The key for lookups. Keys are always typecast to `uint64_t`. + * @return Returns true if the key was found, false otherwise. + * @example > Check if an item exists in the map. + * CF_MAP(CF_V2) table = NULL; + * map_set(table, 10, cf_v2(-1, 1)); + * CF_ASSERT(map_has(table, 10)); + * CF_ASSERT(!map_has(table, 99)); + * map_free(table); + * @related CF_MAP cf_map_set cf_map_get cf_map_get_ptr cf_map_del cf_map_clear cf_map_keys cf_map_items cf_map_swap cf_map_size cf_map_free + */ +#define cf_map_has(m, k) map_has(m, k) + +/** + * @function cf_map_del + * @category map + * @brief Removes an item from the map. + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @param k The key for lookups. Keys are always typecast to `uint64_t`. + * @return Returns 1 if the item was found and deleted, 0 if not found. + * @example > Remove an item from the map. + * CF_MAP(CF_V2) table = NULL; + * map_set(table, 10, cf_v2(-1, 1)); + * map_del(table, 10); + * CF_ASSERT(!map_has(table, 10)); + * map_free(table); + * @related CF_MAP cf_map_set cf_map_get cf_map_get_ptr cf_map_has cf_map_clear cf_map_keys cf_map_items cf_map_swap cf_map_size cf_map_free + */ +#define cf_map_del(m, k) map_del(m, k) + +/** + * @function cf_map_clear + * @category map + * @brief Removes all items from the map without freeing memory. + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @remarks After clearing, `map_size` returns 0 but the backing memory is retained. Call `map_free` when done. + * @related CF_MAP cf_map_set cf_map_get cf_map_get_ptr cf_map_has cf_map_del cf_map_keys cf_map_items cf_map_swap cf_map_size cf_map_free + */ +#define cf_map_clear(m) map_clear(m) + +/** + * @function cf_map_keys + * @category map + * @brief Get a pointer to the array of keys. + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @return Returns `uint64_t*` pointing to the keys array, or `NULL` if map is empty. + * @example > Loop over all {key, item} pairs. + * CF_MAP(CF_V2) table = my_table(); + * uint64_t* keys = map_keys(table); + * for (int i = 0; i < map_size(table); ++i) { + * uint64_t key = keys[i]; + * CF_V2 item = table[i]; + * // ... + * } + * @remarks The keys array is parallel to the items array. Use with `map_size` for iteration bounds. + * @related CF_MAP cf_map_set cf_map_get cf_map_get_ptr cf_map_has cf_map_del cf_map_clear cf_map_items cf_map_swap cf_map_size cf_map_free + */ +#define cf_map_keys(m) map_keys(m) + +/** + * @function cf_map_items + * @category map + * @brief Get a pointer to the array of items (values). + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @return Returns the map pointer itself (which points to the items array). + * @example > Loop over all {key, item} pairs. + * CF_MAP(CF_V2) table = my_table(); + * uint64_t* keys = map_keys(table); + * CF_V2* items = map_items(table); // Same as just using `table` directly + * for (int i = 0; i < map_size(table); ++i) { + * uint64_t key = keys[i]; + * CF_V2 item = items[i]; // Or just: table[i] + * // ... + * } + * @remarks Since `CF_MAP(T)` is `T*`, the map pointer itself is already the items array. This macro exists + * for symmetry with `map_keys` and clarity in iteration code. + * @related CF_MAP cf_map_set cf_map_get cf_map_get_ptr cf_map_has cf_map_del cf_map_clear cf_map_keys cf_map_swap cf_map_size cf_map_free + */ +#define cf_map_items(m) map_items(m) + +/** + * @function cf_map_swap + * @category map + * @brief Swaps two {key, item} pairs by index without breaking the hash lookup. + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @param index_a Index of the first item to swap. + * @param index_b Index of the second item to swap. + * @example > Swap elements during iteration (e.g., for custom sorting). + * CF_MAP(CF_V2) table = my_table(); + * for (int i = 0; i < map_size(table); ++i) { + * for (int j = i + 1; j < map_size(table); ++j) { + * if (my_need_swap(table, i, j)) { + * map_swap(table, i, j); + * } + * } + * } + * @remarks Use this for implementing priority queues or custom sort orders on top of the map. + * @related CF_MAP cf_map_sort cf_map_ssort cf_map_keys cf_map_items cf_map_size + */ +#define cf_map_swap(m, i, j) map_swap(m, i, j) + +/** + * @function cf_map_sort + * @category map + * @brief Sorts the {key, item} pairs by values using a comparator. + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @param cmp Comparator function: `int cmp(const void* a, const void* b)`. Receives pointers to items. + * @remarks The keys and items returned by `map_keys` and `map_items` will be sorted. This is _not_ a stable sort. + * @related CF_MAP cf_map_ssort cf_map_swap cf_map_keys cf_map_items cf_map_size + */ +#define cf_map_sort(m, cmp) map_sort(m, cmp) + +/** + * @function cf_map_ssort + * @category map + * @brief Sorts the {key, item} pairs by keys, treating keys as interned string pointers. + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @param ignore_case If true, sorts case-insensitively. + * @remarks Normally you can't store strings as keys since keys are `uint64_t`. However, if you use `sintern`, + * each unique string gets a unique stable pointer, making them valid map keys. This is _not_ a stable sort. + * @related CF_MAP cf_map_sort cf_map_swap cf_map_keys cf_map_items cf_map_size sintern + */ +#define cf_map_ssort(m, ignore_case) map_ssort(m, ignore_case) + +/** + * @function cf_map_size + * @category map + * @brief Returns the number of {key, item} pairs in the map. + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @return Returns the count as `int`. Returns 0 if `m` is `NULL`. + * @related CF_MAP cf_map_set cf_map_get cf_map_get_ptr cf_map_has cf_map_del cf_map_clear cf_map_keys cf_map_items cf_map_swap cf_map_free + */ +#define cf_map_size(m) map_size(m) + +/** + * @function cf_map_free + * @category map + * @brief Frees all memory used by the map and sets it to `NULL`. + * @param m The map. Can be `NULL`. Must be declared with `CF_MAP(T)`. + * @remarks After calling, `m` will be `NULL`. + * @related CF_MAP cf_map_set cf_map_get cf_map_get_ptr cf_map_has cf_map_del cf_map_clear cf_map_keys cf_map_items cf_map_swap cf_map_size + */ +#define cf_map_free(m) map_free(m) + +//-------------------------------------------------------------------------------------------------- +// Longform C API aliases. + +#define cf_map_add(m, k, v) map_add(m, k, v) +#define cf_map_find(m, k) map_find(m, k) + +//-------------------------------------------------------------------------------------------------- +// C++ API + +#ifdef CF_CPP + +namespace Cute +{ + +// General purpose {key, item} pair mapping via CF_MAP (stretchy-buffer style). +// Keys are always uint64_t. Use sintern for string keys (casting the pointer to uint64_t). +// Items have constructors/destructors called, but are *not* allowed to store references/pointers to themselves. +template +struct Map +{ + Map() = default; + Map(const Map& other); + Map(Map&& other); + ~Map(); + + T& get(uint64_t key); + T& find(uint64_t key) { return get(key); } + const T& get(uint64_t key) const; + const T& find(uint64_t key) const { return get(key); } + T* try_get(uint64_t key); + T* try_find(uint64_t key) { return try_get(key); } + const T* try_get(uint64_t key) const; + const T* try_find(uint64_t key) const { return try_get(key); } + bool has(uint64_t key) const { return try_get(key) != NULL; } + + T* insert(uint64_t key); + T* insert(uint64_t key, const T& val); + T* insert(uint64_t key, T&& val); + T* add(uint64_t key) { return insert(key); } + T* add(uint64_t key, const T& val) { return insert(key, val); } + T* add(uint64_t key, T&& val) { return insert(key, cf_move(val)); } + void remove(uint64_t key); + + // Pointer-key overloads (template prevents int/0 ambiguity with uint64_t overloads). + template T& get(P* key) { return get((uint64_t)(uintptr_t)key); } + template T& find(P* key) { return get((uint64_t)(uintptr_t)key); } + template const T& get(P* key) const { return get((uint64_t)(uintptr_t)key); } + template const T& find(P* key) const { return get((uint64_t)(uintptr_t)key); } + template T* try_get(P* key) { return try_get((uint64_t)(uintptr_t)key); } + template T* try_find(P* key) { return try_get((uint64_t)(uintptr_t)key); } + template const T* try_get(P* key) const { return try_get((uint64_t)(uintptr_t)key); } + template const T* try_find(P* key) const { return try_get((uint64_t)(uintptr_t)key); } + template bool has(P* key) const { return has((uint64_t)(uintptr_t)key); } + template T* insert(P* key) { return insert((uint64_t)(uintptr_t)key); } + template T* insert(P* key, const T& val) { return insert((uint64_t)(uintptr_t)key, val); } + template T* insert(P* key, T&& val) { return insert((uint64_t)(uintptr_t)key, cf_move(val)); } + template T* add(P* key) { return insert((uint64_t)(uintptr_t)key); } + template T* add(P* key, const T& val) { return insert((uint64_t)(uintptr_t)key, val); } + template T* add(P* key, T&& val) { return insert((uint64_t)(uintptr_t)key, cf_move(val)); } + template void remove(P* key) { remove((uint64_t)(uintptr_t)key); } + + void clear(); + + int count() const; + T* items(); + const T* items() const; + T* vals() { return items(); } + const T* vals() const { return items(); } + uint64_t* keys(); + const uint64_t* keys() const; + + void swap(int index_a, int index_b); + void sort(int (*cmp)(const void* a, const void* b)); + + template + void sort_by_items(Fn fn); + + Map& operator=(const Map& rhs); + Map& operator=(Map&& rhs); + +private: + CK_MAP(T) m_map = nullptr; +}; + +// ------------------------------------------------------------------------------------------------- + +template +Map::Map(const Map& other) +{ + int n = other.count(); + const T* vals = other.items(); + const uint64_t* ks = other.keys(); + for (int i = 0; i < n; ++i) { + insert(ks[i], vals[i]); + } +} + +template +Map::Map(Map&& other) +{ + m_map = other.m_map; + other.m_map = nullptr; +} + +template +Map::~Map() +{ + T* elements = items(); + for (int i = 0; i < count(); ++i) { + (elements + i)->~T(); + } + if (m_map) ck_map_free_impl(CK_MHDR(m_map)); +} + +template +T& Map::get(uint64_t key) +{ + void* ptr = m_map ? ck_map_get_ptr_impl(CK_MHDR(m_map), key) : nullptr; + CF_ASSERT(ptr); + return *(T*)ptr; +} + +template +const T& Map::get(uint64_t key) const +{ + void* ptr = m_map ? ck_map_get_ptr_impl(CK_MHDR(m_map), key) : nullptr; + CF_ASSERT(ptr); + return *(const T*)ptr; +} + +template +T* Map::try_get(uint64_t key) +{ + return m_map ? (T*)ck_map_get_ptr_impl(CK_MHDR(m_map), key) : nullptr; +} + +template +const T* Map::try_get(uint64_t key) const +{ + return m_map ? (const T*)ck_map_get_ptr_impl(CK_MHDR(m_map), key) : nullptr; +} + +template +T* Map::insert(uint64_t key) +{ + // Reserve space first, then placement new. + T dummy; + CF_MEMSET(&dummy, 0, sizeof(T)); + ck_map_set_stretchy((void**)&m_map, key, &dummy, (int)sizeof(T)); + T* result = (T*)ck_map_get_ptr_impl(CK_MHDR(m_map), key); + CF_ASSERT(result); + CF_PLACEMENT_NEW(result) T(); + return result; +} + +template +T* Map::insert(uint64_t key, const T& val) +{ + T dummy; + CF_MEMSET(&dummy, 0, sizeof(T)); + ck_map_set_stretchy((void**)&m_map, key, &dummy, (int)sizeof(T)); + T* result = (T*)ck_map_get_ptr_impl(CK_MHDR(m_map), key); + CF_ASSERT(result); + CF_PLACEMENT_NEW(result) T(val); + return result; +} + +template +T* Map::insert(uint64_t key, T&& val) +{ + T dummy; + CF_MEMSET(&dummy, 0, sizeof(T)); + ck_map_set_stretchy((void**)&m_map, key, &dummy, (int)sizeof(T)); + T* result = (T*)ck_map_get_ptr_impl(CK_MHDR(m_map), key); + CF_ASSERT(result); + CF_PLACEMENT_NEW(result) T(cf_move(val)); + return result; +} + +template +void Map::remove(uint64_t key) +{ + T* slot = try_get(key); + if (slot) { + slot->~T(); + ck_map_del_impl(CK_MHDR(m_map), key); + } +} + +template +void Map::clear() +{ + T* elements = items(); + for (int i = 0; i < count(); ++i) { + (elements + i)->~T(); + } + if (m_map) ck_map_clear_impl(CK_MHDR(m_map)); +} + +template +int Map::count() const +{ + return map_size(m_map); +} + +template +T* Map::items() +{ + return m_map; +} + +template +const T* Map::items() const +{ + return m_map; +} + +template +uint64_t* Map::keys() +{ + return map_keys(m_map); +} + +template +const uint64_t* Map::keys() const +{ + return map_keys(m_map); +} + +template +void Map::swap(int index_a, int index_b) +{ + if (m_map) ck_map_swap_impl(CK_MHDR(m_map), index_a, index_b); +} + +template +void Map::sort(int (*cmp)(const void* a, const void* b)) +{ + if (m_map) ck_map_sort_impl(CK_MHDR(m_map), cmp); +} + +template +template +void Map::sort_by_items(Fn fn) +{ + // Insertion sort using the callable on T values. + int n = count(); + for (int i = 1; i < n; ++i) { + int j = i; + while (j > 0 && fn(items()[j], items()[j - 1])) { + swap(j, j - 1); + --j; + } + } +} + +template +Map& Map::operator=(const Map& rhs) +{ + if (this == &rhs) return *this; + clear(); + int n = rhs.count(); + const T* vals = rhs.items(); + const uint64_t* ks = rhs.keys(); + for (int i = 0; i < n; ++i) { + insert(ks[i], vals[i]); + } + return *this; +} + +template +Map& Map::operator=(Map&& rhs) +{ + T* elements = items(); + for (int i = 0; i < count(); ++i) { + (elements + i)->~T(); + } + if (m_map) ck_map_free_impl(CK_MHDR(m_map)); + m_map = rhs.m_map; + rhs.m_map = nullptr; + return *this; +} + +} + +#endif // CF_CPP + +#endif // CF_MAP_H diff --git a/include/cute_math.h b/include/cute_math.h index b68cc4e57..500ddc08d 100644 --- a/include/cute_math.h +++ b/include/cute_math.h @@ -71,6 +71,16 @@ #define CF_LOG2F log2f #endif +#ifndef CF_POWF + #include + #define CF_POWF powf +#endif + +#ifndef CF_EXPF + #include + #define CF_EXPF expf +#endif + #ifndef CF_SQRT #include #define CF_SQRT sqrt @@ -126,6 +136,16 @@ #define CF_LOG2 log2 #endif +#ifndef CF_POW + #include + #define CF_POW pow +#endif + +#ifndef CF_EXP + #include + #define CF_EXP exp +#endif + //-------------------------------------------------------------------------------------------------- // C API @@ -178,7 +198,7 @@ typedef struct CF_V2 * @struct CF_SinCos * @category math * @brief Rotation about an axis composed of cos/sin pair. - * @remarks You can construct a `CF_SinCos` with the function `cf_sin_cos()/cf_sin_cos(1.0f)` function. + * @remarks You can construct a `CF_SinCos` with the function `cf_sincos()`/`cf_sincos_f()`. * @related CF_SinCos cf_sincos cf_sincos_f cf_x_axis cf_y_axis cf_mul_sc_v2 cf_mul_T_sc_v2 */ typedef struct CF_SinCos @@ -316,7 +336,7 @@ typedef struct CF_Circle * @struct CF_Aabb * @category math * @brief Axis-aligned bounding box. A box that cannot rotate. - * @remarks There are many ways to describ an AABB, such as a point + half-extents, or a point and width/height pair. However, of all + * @remarks There are many ways to describe an AABB, such as a point + half-extents, or a point and width/height pair. However, of all * these options the min/max choice is the simplest when it comes to the majority of collision detection routine implementations. * @related CF_Aabb cf_make_aabb cf_width cf_height cf_center cf_top_left cf_top_right cf_bottom_left cf_bottom_right cf_contains_point cf_overlaps cf_make_aabb_verts cf_aabb_verts cf_circle_to_aabb cf_aabb_to_aabb cf_aabb_to_capsule cf_aabb_to_poly cf_ray_to_aabb cf_circle_to_aabb_manifold cf_aabb_to_aabb_manifold cf_aabb_to_capsule_manifold cf_aabb_to_poly_manifold */ @@ -325,7 +345,7 @@ typedef struct CF_Aabb /* @member The min corner of the box. */ CF_V2 min; - /* @member Top max corner of the box. */ + /* @member The max corner of the box. */ CF_V2 max; } CF_Aabb; // @end @@ -421,6 +441,7 @@ typedef struct CF_Manifold * @function CF_PI * @category math * @brief PI the numeric constant. + * @related CF_PI CF_TAU */ #define CF_PI 3.14159265f @@ -428,6 +449,7 @@ typedef struct CF_Manifold * @function CF_TAU * @category math * @brief TAU (PI * 2) the numeric constant. + * @related CF_PI CF_TAU */ #define CF_TAU (2.0f*3.14159265f) @@ -490,7 +512,7 @@ CF_INLINE CF_V2 cf_min_v2 (CF_V2 a, CF_V2 b) { return (CF_V2){ cf_min_f * @brief Returns the maximum of two values. * @related cf_min cf_max */ -#define cf_max(a, b) ((a) < (b) ? (a) : (b)) +#define cf_max(a, b) ((a) > (b) ? (a) : (b)) #undef cf_max #ifdef __cplusplus } // extern "C" @@ -742,7 +764,7 @@ CF_INLINE CF_V2 cf_sign_v2 (CF_V2 x) { return (CF_V2){ cf_sign_f(x.x), cf_ /** * @function cf_intersect * @category math - * @brief Given the distances of two points `a` and `b` to a plane (`da` and `db` respectively), compute the insterection + * @brief Given the distances of two points `a` and `b` to a plane (`da` and `db` respectively), compute the intersection * value used to lerp from `a` to `b` to find the intersection point. * @related cf_min cf_max cf_clamp cf_clamp01 cf_sign cf_intersect cf_safe_invert cf_lerp cf_remap cf_mod cf_fract */ @@ -791,7 +813,7 @@ CF_INLINE CF_V2 cf_div_v2_f(CF_V2 a, float b) { return cf_v2(a.x / b, a.y / b); /** * @function cf_lesser * @category math - * @brief Returns true if `a.x < b.y` and `a.y < b.y`. + * @brief Returns true if `a.x < b.x` and `a.y < b.y`. * @related CF_V2 cf_round cf_lesser cf_greater cf_lesser_equal cf_greater_equal cf_parallel */ CF_INLINE int cf_lesser(CF_V2 a, CF_V2 b) { return a.x < b.x && a.y < b.y; } @@ -799,7 +821,7 @@ CF_INLINE int cf_lesser(CF_V2 a, CF_V2 b) { return a.x < b.x && a.y < b.y; } /** * @function cf_greater * @category math - * @brief Returns true if `a.x > b.y` and `a.y > b.y`. + * @brief Returns true if `a.x > b.x` and `a.y > b.y`. * @related CF_V2 cf_round cf_lesser cf_greater cf_lesser_equal cf_greater_equal cf_parallel */ CF_INLINE int cf_greater(CF_V2 a, CF_V2 b) { return a.x > b.x && a.y > b.y; } @@ -807,7 +829,7 @@ CF_INLINE int cf_greater(CF_V2 a, CF_V2 b) { return a.x > b.x && a.y > b.y; } /** * @function cf_lesser_equal * @category math - * @brief Returns true if `a.x <= b.y` and `a.y <= b.y`. + * @brief Returns true if `a.x <= b.x` and `a.y <= b.y`. * @related CF_V2 cf_round cf_lesser cf_greater cf_lesser_equal cf_greater_equal cf_parallel */ CF_INLINE int cf_lesser_equal(CF_V2 a, CF_V2 b) { return a.x <= b.x && a.y <= b.y; } @@ -815,7 +837,7 @@ CF_INLINE int cf_lesser_equal(CF_V2 a, CF_V2 b) { return a.x <= b.x && a.y <= b. /** * @function cf_greater_equal * @category math - * @brief Returns true if `a.x >= b.y` and `a.y >= b.y`. + * @brief Returns true if `a.x >= b.x` and `a.y >= b.y`. * @related CF_V2 cf_round cf_lesser cf_greater cf_lesser_equal cf_greater_equal cf_parallel */ CF_INLINE int cf_greater_equal(CF_V2 a, CF_V2 b) { return a.x >= b.x && a.y >= b.y; } @@ -823,16 +845,16 @@ CF_INLINE int cf_greater_equal(CF_V2 a, CF_V2 b) { return a.x >= b.x && a.y >= b /** * @function cf_equal * @category math - * @brief TODO - * @related TODO + * @brief Returns true if two vectors are exactly equal. + * @related cf_equal cf_not_equal cf_less cf_greater cf_less_equal cf_greater_equal */ CF_INLINE int cf_equal(CF_V2 a, CF_V2 b) { return a.x == b.x && a.y == b.y; } /** * @function cf_equals * @category math - * @brief TODO - * @related TODO + * @brief Returns true if two vectors are exactly equal (alias for `cf_equal`). + * @related cf_equal cf_not_equal cf_less cf_greater cf_less_equal cf_greater_equal */ CF_INLINE int cf_equals(CF_V2 a, CF_V2 b) { return a.x == b.x && a.y == b.y; } @@ -1147,6 +1169,54 @@ CF_INLINE double cf_sqrt_d(double x) { return CF_SQRT(x); } )(x) #endif +/** + * @function cf_pow + * @category math + * @brief Returns `base` raised to the power of `exp`. + * @related cf_abs cf_sqrt cf_square cf_exp + */ +#define cf_pow(base, exp) +#undef cf_pow +#ifdef __cplusplus +} // extern "C" +CF_INLINE float cf_pow(float base, float exp) { return CF_POWF(base, exp); } +CF_INLINE double cf_pow(double base, double exp) { return CF_POW(base, exp); } +extern "C" { +#else +CF_INLINE float cf_pow_f(float base, float exp) { return CF_POWF(base, exp); } +CF_INLINE double cf_pow_d(double base, double exp) { return CF_POW(base, exp); } +#define cf_pow(base, exp) \ + _Generic((base), \ + float: cf_pow_f, \ + double: cf_pow_d, \ + default: cf_pow_f \ + )((base), (exp)) +#endif + +/** + * @function cf_exp + * @category math + * @brief Returns e raised to the power of `x`. + * @related cf_abs cf_sqrt cf_pow cf_log2 + */ +#define cf_exp(x) +#undef cf_exp +#ifdef __cplusplus +} // extern "C" +CF_INLINE float cf_exp(float x) { return CF_EXPF(x); } +CF_INLINE double cf_exp(double x) { return CF_EXP(x); } +extern "C" { +#else +CF_INLINE float cf_exp_f(float x) { return CF_EXPF(x); } +CF_INLINE double cf_exp_d(double x) { return CF_EXP(x); } +#define cf_exp(x) \ + _Generic((x), \ + float: cf_exp_f, \ + double: cf_exp_d, \ + default: cf_exp_f \ + )(x) +#endif + //-------------------------------------------------------------------------------------------------- // Easing functions. // Adapted from Noel Berry: https://github.com/NoelFB/blah/blob/master/include/blah_ease.h @@ -1156,6 +1226,7 @@ CF_INLINE double cf_sqrt_d(double x) { return CF_SQRT(x); } * @category math * @brief Returns the smoothstep of a float from 0.0f to 1.0f. * @remarks Here is a great link to [visualize each easing function](https://easings.net/). + * @related cf_smoothstep cf_quad_in cf_quad_out cf_cube_in cf_cube_out */ CF_INLINE float cf_smoothstep(float x) { return x * x * (3.0f - 2.0f * x); } @@ -1485,13 +1556,11 @@ extern "C" { CF_INLINE float cf_atan2_360_f_f(float y, float x) { return CF_ATAN2F(-y, -x) + CF_PI; } CF_INLINE float cf_atan2_360_sc(CF_SinCos r) { return cf_atan2_360_f_f(r.s, r.c); } CF_INLINE float cf_atan2_360_v2(CF_V2 v) { return CF_ATAN2F(-v.y, -v.x) + CF_PI; } -#define cf_atan2_360(a, b) \ - _Generic((a), \ - CF_V2: cf_atan2_360_v2, \ - CF_SinCos: cf_atan2_360_sc, \ - float: cf_atan2_360, \ - default: cf_atan2_360 \ - )((a), (b)) + +#define _CF_ATAN2_360_1ARG(a) _Generic((a), CF_V2: cf_atan2_360_v2, CF_SinCos: cf_atan2_360_sc)(a) +#define _CF_ATAN2_360_SELECT(_1, _2, NAME, ...) NAME +#define cf_atan2_360(...) \ + CF_EXPAND(_CF_ATAN2_360_SELECT(__VA_ARGS__, cf_atan2_360_f_f, _CF_ATAN2_360_1ARG)(__VA_ARGS__)) #endif /** @@ -1511,12 +1580,9 @@ extern "C" { CF_INLINE CF_M3x2 cf_make_translation_f_f(float x, float y) { CF_M3x2 m; m.m.x = cf_v2(1,0); m.m.y = cf_v2(0,1); m.p = cf_v2(x,y); return m; } CF_INLINE CF_M3x2 cf_make_translation_v2(CF_V2 p) { return cf_make_translation_f_f(p.x,p.y); } -#define cf_make_translation(a, b) \ - _Generic((a), \ - CF_V2: cf_make_translation_v2, \ - float: cf_make_translation_f_f,\ - default: cf_make_translation_f_f \ - )((a), (b)) +#define _CF_MAKE_TRANSLATION_SELECT(_1, _2, NAME, ...) NAME +#define cf_make_translation(...) \ + CF_EXPAND(_CF_MAKE_TRANSLATION_SELECT(__VA_ARGS__, cf_make_translation_f_f, cf_make_translation_v2)(__VA_ARGS__)) #endif /** @@ -1601,7 +1667,7 @@ CF_INLINE CF_V2 cf_project(CF_Halfspace h, CF_V2 p) { float d = cf_distance_hs(h * @brief Returns the intersection point of two points to a plane. * @remarks The distance to the plane are provided as `da` and `db`. You can compute these with e.g. `cf_distance_hs`, or instead * call the similar function `cf_intersect_halfspace2`. - * @related CF_Halfspace cf_plane cf_origin cf_distance_hs cf_project cf_mul_tf_hs cf_mul_T_tf_hs cf_intersect_halfspace cf_intersect_haflspace2 cf_intersect_haflspace3 + * @related CF_Halfspace cf_plane cf_origin cf_distance_hs cf_project cf_mul_tf_hs cf_mul_T_tf_hs cf_intersect_halfspace cf_intersect_halfspace2 cf_intersect_halfspace3 */ CF_INLINE CF_V2 cf_intersect_halfspace(CF_V2 a, CF_V2 b, float da, float db) { float d = (da / (da - db)); CF_V2 ab = cf_sub(b, a); return cf_add(a, cf_v2(ab.x * d, ab.y * d)); } @@ -1609,7 +1675,7 @@ CF_INLINE CF_V2 cf_intersect_halfspace(CF_V2 a, CF_V2 b, float da, float db) { f * @function cf_intersect_halfspace2 * @category math * @brief Returns the intersection point of two points to a plane. - * @related CF_Halfspace cf_plane cf_origin cf_distance_hs cf_project cf_mul_tf_hs cf_mul_T_tf_hs cf_intersect_halfspace cf_intersect_haflspace2 cf_intersect_haflspace3 + * @related CF_Halfspace cf_plane cf_origin cf_distance_hs cf_project cf_mul_tf_hs cf_mul_T_tf_hs cf_intersect_halfspace cf_intersect_halfspace2 cf_intersect_halfspace3 */ CF_INLINE CF_V2 cf_intersect_halfspace2(CF_Halfspace h, CF_V2 a, CF_V2 b) { return cf_intersect_halfspace(a, b, cf_distance_hs(h, a), cf_distance_hs(h, b)); } @@ -1617,7 +1683,7 @@ CF_INLINE CF_V2 cf_intersect_halfspace2(CF_Halfspace h, CF_V2 a, CF_V2 b) { retu * @function cf_intersect_halfspace3 * @category math * @brief Returns the intersection point of two planes. - * @related CF_Halfspace cf_plane cf_origin cf_distance_hs cf_project cf_mul_tf_hs cf_mul_T_tf_hs cf_intersect_halfspace cf_intersect_haflspace2 cf_intersect_haflspace3 + * @related CF_Halfspace cf_plane cf_origin cf_distance_hs cf_project cf_mul_tf_hs cf_mul_T_tf_hs cf_intersect_halfspace cf_intersect_halfspace2 cf_intersect_halfspace3 */ CF_INLINE CF_V2 cf_intersect_halfspace3(CF_Halfspace ha, CF_Halfspace hb) { CF_V2 a = {ha.n.x, hb.n.x}, b = {ha.n.y, hb.n.y}, c = {ha.d, hb.d}; float x = cf_det2(c, b) / cf_det2(a, b); float y = cf_det2(a, c) / cf_det2(a, b); return cf_v2(x, y); } @@ -1639,6 +1705,45 @@ CF_INLINE CF_M2x2 cf_mul_T_m2(CF_M2x2 a, CF_M2x2 b) { CF_M2x2 c; c.x = cf_mul_T_ CF_INLINE CF_V2 cf_mul_m32_v2(CF_M3x2 a, CF_V2 b) { return cf_add(cf_mul_m2_v2(a.m, b), a.p); } CF_INLINE CF_M3x2 cf_mul_m32(CF_M3x2 a, CF_M3x2 b) { CF_M3x2 c; c.m = cf_mul_m2(a.m, b.m); c.p = cf_add(cf_mul_m2_v2(a.m, b.p), a.p); return c; } +// Macro alternatives -- flat expansion, guaranteed inline in debug builds. +// MSVC /Od ignores __forceinline, so the above functions become real calls. +// These macros avoid the deep call chains (mul_m32 -> mul_m2 -> mul_m2_v2 -> ...). +// All arguments must be side-effect-free lvalues or temporaries. + +// M3x2 * V2 -> writes CF_V2 into dst. +#define CF_MUL_M32_V2(dst, a, b) do { \ + float _cf_bx = (b).x, _cf_by = (b).y; \ + (dst).x = (a).m.x.x * _cf_bx + (a).m.y.x * _cf_by + (a).p.x; \ + (dst).y = (a).m.x.y * _cf_bx + (a).m.y.y * _cf_by + (a).p.y; \ +} while (0) + +// M3x2 * M3x2 -> writes CF_M3x2 into dst. Safe when dst aliases a or b. +#define CF_MUL_M32_M32(dst, a, b) do { \ + float _cf_axx = (a).m.x.x, _cf_axy = (a).m.x.y; \ + float _cf_ayx = (a).m.y.x, _cf_ayy = (a).m.y.y; \ + float _cf_apx = (a).p.x, _cf_apy = (a).p.y; \ + float _cf_bxx = (b).m.x.x, _cf_bxy = (b).m.x.y; \ + float _cf_byx = (b).m.y.x, _cf_byy = (b).m.y.y; \ + float _cf_bpx = (b).p.x, _cf_bpy = (b).p.y; \ + (dst).m.x.x = _cf_axx * _cf_bxx + _cf_ayx * _cf_bxy; \ + (dst).m.x.y = _cf_axy * _cf_bxx + _cf_ayy * _cf_bxy; \ + (dst).m.y.x = _cf_axx * _cf_byx + _cf_ayx * _cf_byy; \ + (dst).m.y.y = _cf_axy * _cf_byx + _cf_ayy * _cf_byy; \ + (dst).p.x = _cf_axx * _cf_bpx + _cf_ayx * _cf_bpy + _cf_apx; \ + (dst).p.y = _cf_axy * _cf_bpx + _cf_ayy * _cf_bpy + _cf_apy; \ +} while (0) + +// Build M3x2 from position + scale + radians. +#define CF_MAKE_TSR(dst, pos, scl, rad) do { \ + float _cf_c = CF_COSF(rad), _cf_s = CF_SINF(rad); \ + float _cf_sx = (scl).x, _cf_sy = (scl).y; \ + (dst).m.x.x = _cf_c * _cf_sx; \ + (dst).m.x.y = -_cf_s * _cf_sx; \ + (dst).m.y.x = _cf_s * _cf_sy; \ + (dst).m.y.y = _cf_c * _cf_sy; \ + (dst).p = (pos); \ +} while (0) + CF_INLINE CF_V2 cf_mul_tf_v2(CF_Transform a, CF_V2 b) { return cf_add(cf_mul_sc_v2(a.r, b), a.p); } CF_INLINE CF_V2 cf_mul_T_tf_v2(CF_Transform a, CF_V2 b) { return cf_mul_T_sc_v2(a.r, cf_sub(b, a.p)); } CF_INLINE CF_Transform cf_mul_tf(CF_Transform a, CF_Transform b) { CF_Transform c; c.r = cf_mul_sc(a.r, b.r); c.p = cf_add(cf_mul_sc_v2(a.r, b.p), a.p); return c; } @@ -2339,7 +2444,7 @@ CF_INLINE CF_V2 cf_bottom(CF_Aabb bb) { return cf_v2((bb.min.x + bb.max.x) * 0.5 /** * @function cf_right * @category math - * @brief Returns the bottom of the aabb. + * @brief Returns the right side of the aabb. * @related CF_Aabb cf_min_aabb cf_max_aabb cf_midpoint cf_center cf_top_left cf_top_right cf_bottom_left cf_bottom_right */ CF_INLINE CF_V2 cf_right(CF_Aabb bb) { return cf_v2(bb.max.x, (bb.min.y + bb.max.y) * 0.5f); } @@ -2488,7 +2593,7 @@ CF_INLINE CF_Circle cf_mul_tf_circle(CF_Transform tx, CF_Circle a) { CF_Circle b /** * @function cf_make_ray * @category collision - * @brief Returns an initialize `CF_Ray`. + * @brief Returns an initialized `CF_Ray`. * @related CF_Ray */ CF_INLINE CF_Ray cf_make_ray(CF_V2 start, CF_V2 direction_normalized, float length) { CF_Ray ray; ray.p = start; ray.d = direction_normalized; ray.t = length; return ray; } @@ -2566,31 +2671,31 @@ CF_INLINE float cf_distance_sq(CF_V2 a, CF_V2 b, CF_V2 p) /** * @function cf_center_of_mass * @category collision - * @brief TODO - * @related TODO + * @brief Returns the center of mass of a convex polygon. + * @related CF_Poly cf_center_of_mass cf_calc_area cf_make_poly cf_centroid */ CF_API CF_V2 CF_CALL cf_center_of_mass(CF_Poly poly); /** * @function cf_calc_area * @category collision - * @brief TODO - * @related TODO + * @brief Returns the signed area of a convex polygon. + * @related CF_Poly cf_center_of_mass cf_calc_area cf_make_poly cf_centroid */ CF_API float CF_CALL cf_calc_area(CF_Poly poly); /** * @struct CF_SliceOutput * @category collision - * @brief TODO - * @related TODO + * @brief Contains the two halves of a polygon after slicing by a halfspace. + * @related CF_SliceOutput cf_slice CF_Poly CF_Halfspace */ typedef struct CF_SliceOutput { - /* @member TODO */ + /* @member The polygon on the front (positive) side of the slice plane. */ CF_Poly front; - /* @member TODO */ + /* @member The polygon on the back (negative) side of the slice plane. */ CF_Poly back; } CF_SliceOutput; // @end @@ -2598,8 +2703,8 @@ typedef struct CF_SliceOutput /** * @function cf_slice * @category collision - * @brief TODO - * @related TODO + * @brief Slices a convex polygon by a halfspace, returning the two resulting halves. + * @related CF_SliceOutput cf_slice CF_Poly CF_Halfspace */ CF_API CF_SliceOutput CF_CALL cf_slice(CF_Halfspace slice_plane, CF_Poly slice_me, const float k_epsilon); @@ -2704,6 +2809,7 @@ CF_API void CF_CALL cf_make_poly(CF_Poly* p); * @function cf_centroid * @category math * @brief Returns the centroid of a set of vertices. + * @related CF_Poly cf_centroid cf_center_of_mass cf_hull cf_make_poly */ CF_API CF_V2 CF_CALL cf_centroid(const CF_V2* verts, int count); @@ -2774,7 +2880,7 @@ CF_API bool CF_CALL cf_capsule_to_capsule(CF_Capsule A, CF_Capsule B); * @remarks For information about _how_ two shapes are intersecting (and not just boolean result), see `cf_circle_to_poly_manifold`. * @related CF_Circle CF_Poly cf_circle_to_poly cf_circle_to_poly_manifold */ -CF_API bool CF_CALL cf_circle_to_poly(CF_Circle A, const CF_Poly* B, const CF_Transform* bx); +CF_API bool CF_CALL cf_circle_to_poly(CF_Circle A, const CF_Poly* B); /** * @function cf_aabb_to_poly @@ -2783,7 +2889,7 @@ CF_API bool CF_CALL cf_circle_to_poly(CF_Circle A, const CF_Poly* B, const CF_Tr * @remarks For information about _how_ two shapes are intersecting (and not just boolean result), see `cf_aabb_to_poly_manifold`. * @related CF_Aabb CF_Poly cf_aabb_to_poly cf_aabb_to_poly_manifold */ -CF_API bool CF_CALL cf_aabb_to_poly(CF_Aabb A, const CF_Poly* B, const CF_Transform* bx); +CF_API bool CF_CALL cf_aabb_to_poly(CF_Aabb A, const CF_Poly* B); /** * @function cf_capsule_to_poly @@ -2792,7 +2898,7 @@ CF_API bool CF_CALL cf_aabb_to_poly(CF_Aabb A, const CF_Poly* B, const CF_Transf * @remarks For information about _how_ two shapes are intersecting (and not just boolean result), see `cf_capsule_to_poly_manifold`. * @related CF_Capsule CF_Poly cf_capsule_to_poly cf_capsule_to_poly_manifold */ -CF_API bool CF_CALL cf_capsule_to_poly(CF_Capsule A, const CF_Poly* B, const CF_Transform* bx); +CF_API bool CF_CALL cf_capsule_to_poly(CF_Capsule A, const CF_Poly* B); /** * @function cf_poly_to_poly @@ -2801,7 +2907,7 @@ CF_API bool CF_CALL cf_capsule_to_poly(CF_Capsule A, const CF_Poly* B, const CF_ * @remarks For information about _how_ two shapes are intersecting (and not just boolean result), see `cf_poly_to_poly_manifold`. * @related CF_Poly cf_poly_to_poly cf_poly_to_poly_manifold */ -CF_API bool CF_CALL cf_poly_to_poly(const CF_Poly* A, const CF_Transform* ax, const CF_Poly* B, const CF_Transform* bx); +CF_API bool CF_CALL cf_poly_to_poly(const CF_Poly* A, const CF_Poly* B); /** * @function cf_ray_to_circle @@ -2845,7 +2951,7 @@ CF_API CF_Raycast CF_CALL cf_ray_to_capsule(CF_Ray A, CF_Capsule B); * @return `CF_Raycast` results are placed here. See `CF_RayCast`. * @related CF_Ray CF_Poly CF_Raycast cf_ray_to_circle cf_ray_to_aabb cf_ray_to_capsule cf_ray_to_poly */ -CF_API CF_Raycast CF_CALL cf_ray_to_poly(CF_Ray A, const CF_Poly* B, const CF_Transform* bx_ptr); +CF_API CF_Raycast CF_CALL cf_ray_to_poly(CF_Ray A, const CF_Poly* B); /** * @function cf_circle_to_circle_manifold @@ -2929,7 +3035,7 @@ CF_API CF_Manifold CF_CALL cf_capsule_to_capsule_manifold(CF_Capsule A, CF_Capsu * @remarks This function is slightly slower than the boolean version `cf_circle_to_poly`. * @related CF_Manifold CF_Circle CF_Poly cf_circle_to_circle_manifold cf_circle_to_aabb_manifold cf_circle_to_capsule_manifold cf_aabb_to_aabb_manifold cf_aabb_to_capsule_manifold cf_circle_to_poly_manifold cf_aabb_to_poly_manifold cf_capsule_to_poly_manifold cf_poly_to_poly_manifold */ -CF_API CF_Manifold CF_CALL cf_circle_to_poly_manifold(CF_Circle A, const CF_Poly* B, const CF_Transform* bx); +CF_API CF_Manifold CF_CALL cf_circle_to_poly_manifold(CF_Circle A, const CF_Poly* B); /** * @function cf_aabb_to_poly_manifold @@ -2941,7 +3047,7 @@ CF_API CF_Manifold CF_CALL cf_circle_to_poly_manifold(CF_Circle A, const CF_Poly * @remarks This function is slightly slower than the boolean version `cf_aabb_to_poly`. * @related CF_Manifold CF_Aabb CF_Poly cf_circle_to_circle_manifold cf_circle_to_aabb_manifold cf_circle_to_capsule_manifold cf_aabb_to_aabb_manifold cf_aabb_to_capsule_manifold cf_circle_to_poly_manifold cf_aabb_to_poly_manifold cf_capsule_to_poly_manifold cf_poly_to_poly_manifold */ -CF_API CF_Manifold CF_CALL cf_aabb_to_poly_manifold(CF_Aabb A, const CF_Poly* B, const CF_Transform* bx); +CF_API CF_Manifold CF_CALL cf_aabb_to_poly_manifold(CF_Aabb A, const CF_Poly* B); /** * @function cf_capsule_to_poly_manifold @@ -2953,7 +3059,7 @@ CF_API CF_Manifold CF_CALL cf_aabb_to_poly_manifold(CF_Aabb A, const CF_Poly* B, * @remarks This function is slightly slower than the boolean version `cf_capsule_to_poly`. * @related CF_Manifold CF_Capsule CF_Poly cf_circle_to_circle_manifold cf_circle_to_aabb_manifold cf_circle_to_capsule_manifold cf_aabb_to_aabb_manifold cf_aabb_to_capsule_manifold cf_circle_to_poly_manifold cf_aabb_to_poly_manifold cf_capsule_to_poly_manifold cf_poly_to_poly_manifold */ -CF_API CF_Manifold CF_CALL cf_capsule_to_poly_manifold(CF_Capsule A, const CF_Poly* B, const CF_Transform* bx); +CF_API CF_Manifold CF_CALL cf_capsule_to_poly_manifold(CF_Capsule A, const CF_Poly* B); /** * @function cf_poly_to_poly_manifold @@ -2965,14 +3071,14 @@ CF_API CF_Manifold CF_CALL cf_capsule_to_poly_manifold(CF_Capsule A, const CF_Po * @remarks This function is slightly slower than the boolean version `cf_poly_to_poly`. * @related CF_Manifold CF_Poly cf_circle_to_circle_manifold cf_circle_to_aabb_manifold cf_circle_to_capsule_manifold cf_aabb_to_aabb_manifold cf_aabb_to_capsule_manifold cf_circle_to_poly_manifold cf_aabb_to_poly_manifold cf_capsule_to_poly_manifold cf_poly_to_poly_manifold */ -CF_API CF_Manifold CF_CALL cf_poly_to_poly_manifold(const CF_Poly* A, const CF_Transform* ax, const CF_Poly* B, const CF_Transform* bx); +CF_API CF_Manifold CF_CALL cf_poly_to_poly_manifold(const CF_Poly* A, const CF_Poly* B); /** * @struct CF_GjkCache * @category collision * @brief This struct is only for advanced usage of the `cf_gjk` function. See comments inside of the `cf_gjk` function for more details. - * @remarks Contains cached geometric information to speed up successive calls to `cf_gjk` where the shapes don't move much relatie to - * one other from one call to the next. + * @remarks Contains cached geometric information to speed up successive calls to `cf_gjk` where the shapes don't move much relative to + * one another from one call to the next. * @related CF_GjkCache cf_gjk */ typedef struct CF_GjkCache @@ -2989,7 +3095,7 @@ typedef struct CF_GjkCache /* An index into shape B. */ int iB[3]; - /* The divisor (used in baryecentric coordinates internally) for the vertex B - A. */ + /* The divisor (used in barycentric coordinates internally) for the vertex B - A. */ float div; } CF_GjkCache; // @end @@ -3000,10 +3106,8 @@ typedef struct CF_GjkCache * @brief Returns the distance between two shapes, and computes the closest two points of the shapes. * @param A The first shape. * @param typeA The `CF_ShapeType` of the first shape `A`. - * @param ax_ptr Can be `NULL` to represent an identity transform. An optional pointer to a `CF_Transform` to transform `A`. * @param B The second shape. - * @param typeA The `CF_ShapeType` of the second shape `B`. - * @param bx_ptr Can be `NULL` to represent an identity transform. An optional pointer to a `CF_Transform` to transform `B`. + * @param typeB The `CF_ShapeType` of the second shape `B`. * @param outA The closest point on `A` to `B`. Not well defined if the two shapes are already intersecting. * @param outB The closest point on `B` to `A`. Not well defined if the two shapes are already intersecting. * @param use_radius True if you want to use the radius of any `CF_Circle` or `CF_Capsule` inputs, false to treat them as a point/line segment respectively (a radius of zero). @@ -3011,14 +3115,14 @@ typedef struct CF_GjkCache * @param cache Can be `NULL`. An optional cache to a previous call of this function. See `CF_GjkCache` for details. * @return Returns the distance between the two shapes. * @remarks This is an advanced function, intended to be used by people who know what they're doing. - * + * * The GJK function is sensitive to large shapes, since it internally will compute signed area values. `cf_gjk` is called throughout * this file in many ways, so try to make sure all of your collision shapes are not gigantic. For example, try to keep the volume of * all your shapes less than 100.0f. If you need large shapes, you should use tiny collision geometry for all function here, and simply * render the geometry larger on-screen by scaling it up. * @related cf_gjk CF_GjkCache CF_ShapeType */ -CF_API float CF_CALL cf_gjk(const void* A, CF_ShapeType typeA, const CF_Transform* ax_ptr, const void* B, CF_ShapeType typeB, const CF_Transform* bx_ptr, CF_V2* outA, CF_V2* outB, bool use_radius, int* iterations, CF_GjkCache* cache); +CF_API float CF_CALL cf_gjk(const void* A, CF_ShapeType typeA, const void* B, CF_ShapeType typeB, CF_V2* outA, CF_V2* outB, bool use_radius, int* iterations, CF_GjkCache* cache); /** * @struct CF_ToiResult @@ -3052,28 +3156,26 @@ typedef struct CF_ToiResult * @brief Computes the time of impact of two shapes. * @param A The first shape. * @param typeA The `CF_ShapeType` of the first shape `A`. - * @param ax_ptr Can be `NULL` to represent an identity transform. An optional pointer to a `CF_Transform` to transform `A`. * @param vA The velocity of `A`. * @param B The second shape. - * @param typeA The `CF_ShapeType` of the second shape `B`. - * @param bx_ptr Can be `NULL` to represent an identity transform. An optional pointer to a `CF_Transform` to transform `B`. + * @param typeB The `CF_ShapeType` of the second shape `B`. * @param vB The velocity of `B`. * @param use_radius True if you want to use the radius of any `CF_Circle` or `CF_Capsule` inputs, false to treat them as a point/line segment respectively (a radius of zero). * @return Returns a `CF_ToiResult` containing information about the time of impact. * @remarks This is an advanced function, intended to be used by people who know what they're doing. - * + * * Computes the time of impact from shape A and shape B. The velocity of each shape is provided by `vA` and `vB` respectively. The shapes are * _not_ allowed to rotate over time. The velocity is assumed to represent the change in motion from time 0 to time 1, and so the return value * will be a number from 0 to 1. To move each shape to the colliding configuration, multiply `vA` and `vB` each by the return value. - * + * * IMPORTANT NOTE - * + * * The `cf_toi` function can be used to implement a "swept character controller", but it can be difficult to do so. Say we compute a time * of impact with `cf_toi` and move the shapes to the time of impact, and adjust the velocity by zeroing out the velocity along the surface * normal. If we then call `cf_toi` again, it will fail since the shapes will be considered to start in a colliding configuration. There are * many styles of tricks to get around this problem, and all of them involve giving the next call to `cf_toi` some breathing room. It is * recommended to use some variation of the following algorithm: - * + * * 1. Call `cf_toi`. * 2. Move the shapes to the TOI. * 3. Slightly inflate the size of one, or both, of the shapes so they will be intersecting. @@ -3084,37 +3186,32 @@ typedef struct CF_ToiResult * 5. Gently push the shapes apart. This will give the next call to `cf_toi` some breathing room. * @related CF_ToiResult cf_toi CF_ShapeType */ -CF_API CF_ToiResult CF_CALL cf_toi(const void* A, CF_ShapeType typeA, const CF_Transform* ax_ptr, CF_V2 vA, const void* B, CF_ShapeType typeB, const CF_Transform* bx_ptr, CF_V2 vB, int use_radius); +CF_API CF_ToiResult CF_CALL cf_toi(const void* A, CF_ShapeType typeA, CF_V2 vA, const void* B, CF_ShapeType typeB, CF_V2 vB, int use_radius); /** * @function cf_collided * @category collision - * @brief Returns a true if two shapes collided. + * @brief Returns true if two shapes collided. * @param A The first shape. * @param typeA The `CF_ShapeType` of the first shape `A`. - * @param ax_ptr Can be `NULL` to represent an identity transform. An optional pointer to a `CF_Transform` to transform `A`. - * @param vA The velocity of `A`. * @param B The second shape. - * @param typeA The `CF_ShapeType` of the second shape `B`. - * @param bx_ptr Can be `NULL` to represent an identity transform. An optional pointer to a `CF_Transform` to transform `B`. - * @related cf_collided cf_collide CF_Transform CF_ShapeType + * @param typeB The `CF_ShapeType` of the second shape `B`. + * @related cf_collided cf_collide CF_ShapeType */ -CF_API int CF_CALL cf_collided(const void* A, const CF_Transform* ax, CF_ShapeType typeA, const void* B, const CF_Transform* bx, CF_ShapeType typeB); +CF_API int CF_CALL cf_collided(const void* A, CF_ShapeType typeA, const void* B, CF_ShapeType typeB); /** * @function cf_collide * @category collision * @brief Computes a `CF_Manifold` between two shapes. * @param A The first shape. - * @param ax Can be `NULL` to represent an identity transform. An optional pointer to a `CF_Transform` to transform `A`. * @param typeA The `CF_ShapeType` of the first shape `A`. * @param B The second shape. - * @param bx Can be `NULL` to represent an identity transform. An optional pointer to a `CF_Transform` to transform `B`. - * @param typeA The `CF_ShapeType` of the second shape `B`. + * @param typeB The `CF_ShapeType` of the second shape `B`. * @param m Contains information about the intersection. `m->count` is set to zero for no-intersection. See `CF_Manifold` for details. - * @related cf_collided cf_collide CF_Transform CF_ShapeType CF_Manifold + * @related cf_collided cf_collide CF_ShapeType CF_Manifold */ -CF_API void CF_CALL cf_collide(const void* A, const CF_Transform* ax, CF_ShapeType typeA, const void* B, const CF_Transform* bx, CF_ShapeType typeB, CF_Manifold* m); +CF_API void CF_CALL cf_collide(const void* A, CF_ShapeType typeA, const void* B, CF_ShapeType typeB, CF_Manifold* m); /** * @function cf_cast_ray @@ -3123,12 +3220,11 @@ CF_API void CF_CALL cf_collide(const void* A, const CF_Transform* ax, CF_ShapeTy * @param A The ray. * @param B The shape. * @param typeB The `CF_ShapeType` of the shape `B`. - * @param bx_ptr Can be `NULL` to represent an identity transform. An optional pointer to a `CF_Transform` to transform `B`. * @param out Can be `NULL`. `CF_Raycast` results are placed here (contains normal + time of impact). * @return Returns true if the ray hit the shape. - * @related CF_Ray CF_Raycast CF_Transform CF_ShapeType cf_cast_ray + * @related CF_Ray CF_Raycast CF_ShapeType cf_cast_ray */ -CF_API bool CF_CALL cf_cast_ray(CF_Ray A, const void* B, const CF_Transform* bx, CF_ShapeType typeB, CF_Raycast* out); +CF_API bool CF_CALL cf_cast_ray(CF_Ray A, const void* B, CF_ShapeType typeB, CF_Raycast* out); #ifdef __cplusplus } @@ -3345,15 +3441,15 @@ CF_INLINE bool circle_to_capsule(CF_Circle A, CF_Capsule B) { return cf_circle_t CF_INLINE bool aabb_to_aabb(CF_Aabb A, CF_Aabb B) { return cf_aabb_to_aabb(A, B); } CF_INLINE bool aabb_to_capsule(CF_Aabb A, CF_Capsule B) { return cf_aabb_to_capsule(A, B); } CF_INLINE bool capsule_to_capsule(CF_Capsule A, CF_Capsule B) { return cf_capsule_to_capsule(A, B); } -CF_INLINE bool circle_to_poly(CF_Circle A, const CF_Poly* B, const CF_Transform* bx) { return cf_circle_to_poly(A, B, bx); } -CF_INLINE bool aabb_to_poly(CF_Aabb A, const CF_Poly* B, const CF_Transform* bx) { return cf_aabb_to_poly(A, B, bx); } -CF_INLINE bool capsule_to_poly(CF_Capsule A, const CF_Poly* B, const CF_Transform* bx) { return cf_capsule_to_poly(A, B, bx); } -CF_INLINE bool poly_to_poly(const CF_Poly* A, const CF_Transform* ax, const CF_Poly* B, const CF_Transform* bx) { return cf_poly_to_poly(A, ax, B, bx); } +CF_INLINE bool circle_to_poly(CF_Circle A, const CF_Poly* B) { return cf_circle_to_poly(A, B); } +CF_INLINE bool aabb_to_poly(CF_Aabb A, const CF_Poly* B) { return cf_aabb_to_poly(A, B); } +CF_INLINE bool capsule_to_poly(CF_Capsule A, const CF_Poly* B) { return cf_capsule_to_poly(A, B); } +CF_INLINE bool poly_to_poly(const CF_Poly* A, const CF_Poly* B) { return cf_poly_to_poly(A, B); } CF_INLINE CF_Raycast ray_to_circle(CF_Ray A, CF_Circle B) { return cf_ray_to_circle(A, B); } CF_INLINE CF_Raycast ray_to_aabb(CF_Ray A, CF_Aabb B) { return cf_ray_to_aabb(A, B); } CF_INLINE CF_Raycast ray_to_capsule(CF_Ray A, CF_Capsule B) { return cf_ray_to_capsule(A, B); } -CF_INLINE CF_Raycast ray_to_poly(CF_Ray A, const CF_Poly* B, const CF_Transform* bx_ptr = NULL) { return cf_ray_to_poly(A, B, bx_ptr); } +CF_INLINE CF_Raycast ray_to_poly(CF_Ray A, const CF_Poly* B) { return cf_ray_to_poly(A, B); } CF_INLINE CF_Manifold circle_to_circle_manifold(CF_Circle A, CF_Circle B) { return cf_circle_to_circle_manifold(A, B); } CF_INLINE CF_Manifold circle_to_aabb_manifold(CF_Circle A, CF_Aabb B) { return cf_circle_to_aabb_manifold(A, B); } @@ -3361,24 +3457,24 @@ CF_INLINE CF_Manifold circle_to_capsule_manifold(CF_Circle A, CF_Capsule B) { re CF_INLINE CF_Manifold aabb_to_aabb_manifold(CF_Aabb A, CF_Aabb B) { return cf_aabb_to_aabb_manifold(A, B); } CF_INLINE CF_Manifold aabb_to_capsule_manifold(CF_Aabb A, CF_Capsule B) { return cf_aabb_to_capsule_manifold(A, B); } CF_INLINE CF_Manifold capsule_to_capsule_manifold(CF_Capsule A, CF_Capsule B) { return cf_capsule_to_capsule_manifold(A, B); } -CF_INLINE CF_Manifold circle_to_poly_manifold(CF_Circle A, const CF_Poly* B, const CF_Transform* bx) { return cf_circle_to_poly_manifold(A, B, bx); } -CF_INLINE CF_Manifold aabb_to_poly_manifold(CF_Aabb A, const CF_Poly* B, const CF_Transform* bx) { return cf_aabb_to_poly_manifold(A, B, bx); } -CF_INLINE CF_Manifold capsule_to_poly_manifold(CF_Capsule A, const CF_Poly* B, const CF_Transform* bx) { return cf_capsule_to_poly_manifold(A, B, bx); } -CF_INLINE CF_Manifold poly_to_poly_manifold(const CF_Poly* A, const CF_Transform* ax, const CF_Poly* B, const CF_Transform* bx) { return cf_poly_to_poly_manifold(A, ax, B, bx); } +CF_INLINE CF_Manifold circle_to_poly_manifold(CF_Circle A, const CF_Poly* B) { return cf_circle_to_poly_manifold(A, B); } +CF_INLINE CF_Manifold aabb_to_poly_manifold(CF_Aabb A, const CF_Poly* B) { return cf_aabb_to_poly_manifold(A, B); } +CF_INLINE CF_Manifold capsule_to_poly_manifold(CF_Capsule A, const CF_Poly* B) { return cf_capsule_to_poly_manifold(A, B); } +CF_INLINE CF_Manifold poly_to_poly_manifold(const CF_Poly* A, const CF_Poly* B) { return cf_poly_to_poly_manifold(A, B); } -CF_INLINE float gjk(const void* A, CF_ShapeType typeA, const CF_Transform* ax_ptr, const void* B, CF_ShapeType typeB, const CF_Transform* bx_ptr, v2* outA, v2* outB, int use_radius, int* iterations, CF_GjkCache* cache) +CF_INLINE float gjk(const void* A, CF_ShapeType typeA, const void* B, CF_ShapeType typeB, v2* outA, v2* outB, int use_radius, int* iterations, CF_GjkCache* cache) { - return cf_gjk(A, typeA, ax_ptr, B, typeB, bx_ptr, (CF_V2*)outA, (CF_V2*)outB, use_radius, iterations, cache); + return cf_gjk(A, typeA, B, typeB, (CF_V2*)outA, (CF_V2*)outB, use_radius, iterations, cache); } -CF_INLINE CF_ToiResult toi(const void* A, CF_ShapeType typeA, const CF_Transform* ax_ptr, v2 vA, const void* B, CF_ShapeType typeB, const CF_Transform* bx_ptr, v2 vB, int use_radius) +CF_INLINE CF_ToiResult toi(const void* A, CF_ShapeType typeA, v2 vA, const void* B, CF_ShapeType typeB, v2 vB, int use_radius) { - return cf_toi(A, typeA, ax_ptr, vA, B, typeB, bx_ptr, vB, use_radius); + return cf_toi(A, typeA, vA, B, typeB, vB, use_radius); } -CF_INLINE int collided(const void* A, const CF_Transform* ax, CF_ShapeType typeA, const void* B, const CF_Transform* bx, CF_ShapeType typeB) { return cf_collided(A, ax, typeA, B, bx, typeB); } -CF_INLINE void collide(const void* A, const CF_Transform* ax, CF_ShapeType typeA, const void* B, const CF_Transform* bx, CF_ShapeType typeB, CF_Manifold* m) { return cf_collide(A, ax, typeA, B, bx, typeB, m); } -CF_INLINE bool cast_ray(CF_Ray A, const void* B, const CF_Transform* bx, CF_ShapeType typeB, CF_Raycast* out) { return cf_cast_ray(A, B, bx, typeB, out); } +CF_INLINE int collided(const void* A, CF_ShapeType typeA, const void* B, CF_ShapeType typeB) { return cf_collided(A, typeA, B, typeB); } +CF_INLINE void collide(const void* A, CF_ShapeType typeA, const void* B, CF_ShapeType typeB, CF_Manifold* m) { return cf_collide(A, typeA, B, typeB, m); } +CF_INLINE bool cast_ray(CF_Ray A, const void* B, CF_ShapeType typeB, CF_Raycast* out) { return cf_cast_ray(A, B, typeB, out); } } diff --git a/include/cute_multithreading.h b/include/cute_multithreading.h index 502f508ce..99bedea8b 100644 --- a/include/cute_multithreading.h +++ b/include/cute_multithreading.h @@ -95,7 +95,7 @@ typedef cute_thread_fn CF_ThreadFn; * @category multithreading * @brief An opaque handle representing a read-write lock. * @remarks A read/write lock can have a large number of simultaneous readers, but only one writer at a time. This can be - * used as an opimization where a resources can be safely read from many threads. Then, when the resource must be + * used as an optimization where a resources can be safely read from many threads. Then, when the resource must be * modified a writer can wait for all readers to leave, and then exclusively lock to perform a write update. * @related CF_ReadWriteLock cf_make_rw_lock cf_destroy_rw_lock cf_read_lock cf_read_unlock cf_write_lock cf_write_unlock */ @@ -118,7 +118,7 @@ typedef cute_threadpool_t CF_Threadpool; * @remarks Destroy the mutex with `cf_destroy_mutex` when done. * @related CF_Mutex cf_make_mutex cf_destroy_mutex cf_mutex_lock cf_mutex_unlock cf_mutex_try_lock */ -CF_API CF_Mutex CF_CALL cf_make_mutex(); +CF_API CF_Mutex CF_CALL cf_make_mutex(void); /** * @function cf_destroy_mutex @@ -165,7 +165,7 @@ CF_API bool CF_CALL cf_mutex_try_lock(CF_Mutex* mutex); * @remarks Destroy the mutex with `cf_destroy_cv` when done. * @related CF_ConditionVariable cf_make_cv cf_destroy_cv cf_cv_wake_all cf_cv_wake_one cf_cv_wait */ -CF_API CF_ConditionVariable CF_CALL cf_make_cv(); +CF_API CF_ConditionVariable CF_CALL cf_make_cv(void); /** * @function cf_destroy_cv @@ -280,7 +280,7 @@ CF_API CF_Result CF_CALL cf_sem_value(CF_Semaphore* semaphore); /** * @function cf_thread_create * @category multithreading - * @brief Creates a new thread and runs it's thread function (`CF_ThreadFn`). + * @brief Creates a new thread and runs its thread function (`CF_ThreadFn`). * @param func The function to run for the thread. * @param name The name of this thread. Must be unique. * @param udata Can be `NULL`. This gets handed back to you in your `func`. @@ -321,10 +321,9 @@ CF_API CF_ThreadId CF_CALL cf_thread_get_id(CF_Thread* thread); * @function cf_thread_id * @category multithreading * @brief Returns the unique id of the calling thread. - * @param thread The thread. * @related CF_Thread CF_ThreadFn cf_thread_create cf_thread_detach cf_thread_get_id cf_thread_id cf_thread_wait */ -CF_API CF_ThreadId CF_CALL cf_thread_id(); +CF_API CF_ThreadId CF_CALL cf_thread_id(void); /** * @function cf_thread_wait @@ -340,10 +339,10 @@ CF_API CF_Result CF_CALL cf_thread_wait(CF_Thread* thread); /** * @function cf_core_count * @category CPU - * @brief Returns the number of cores on the CPU. Can be affected my machine dependent technology, such as Intel's hyperthreading. + * @brief Returns the number of cores on the CPU. Can be affected by machine dependent technology, such as Intel's hyperthreading. * @related cf_core_count */ -CF_API int CF_CALL cf_core_count(); +CF_API int CF_CALL cf_core_count(void); /** * @function cf_cacheline_size @@ -351,7 +350,7 @@ CF_API int CF_CALL cf_core_count(); * @brief Returns the number of bytes in a single cache line of the CPU L1 memory cache. * @related cf_core_count */ -CF_API int CF_CALL cf_cacheline_size(); +CF_API int CF_CALL cf_cacheline_size(void); /** * @function cf_atomic_zero @@ -360,7 +359,7 @@ CF_API int CF_CALL cf_cacheline_size(); * @remarks Atomics are an advanced topic. You've been warned! * @related cf_atomic_zero cf_atomic_add cf_atomic_set cf_atomic_get cf_atomic_cas cf_atomic_ptr_set cf_atomic_ptr_get cf_atomic_ptr_cas */ -CF_API CF_AtomicInt CF_CALL cf_atomic_zero(); +CF_API CF_AtomicInt CF_CALL cf_atomic_zero(void); /** * @function cf_atomic_add @@ -448,7 +447,7 @@ CF_API CF_Result CF_CALL cf_atomic_ptr_cas(void** atomic, void* expected, void* * @remarks Call `cf_destroy_rw_lock` when done. * @related CF_ReadWriteLock cf_make_rw_lock cf_destroy_rw_lock cf_read_lock cf_read_unlock cf_write_lock cf_write_unlock */ -CF_API CF_ReadWriteLock CF_CALL cf_make_rw_lock(); +CF_API CF_ReadWriteLock CF_CALL cf_make_rw_lock(void); /** * @function cf_destroy_rw_lock @@ -540,7 +539,7 @@ CF_API void CF_CALL cf_destroy_threadpool(CF_Threadpool* pool); * @param pool The pool. * @param task The task for a thread in the pool to perform. * @param param Can be `NULL`. This gets handed to the `CF_TaskFn` when it gets called. - * @remarks Once a task is added to the pool `cf_threadpool_kick_and_wait` or `cf_threadpool_kick` must be called wake threads. Once + * @remarks Once a task is added to the pool `cf_threadpool_kick_and_wait` or `cf_threadpool_kick` must be called to wake threads. Once * awake, threads will process the tasks. The order of start/finish for the tasks is not deterministic. * @related CF_TaskFn cf_make_threadpool cf_destroy_threadpool cf_threadpool_add_task cf_threadpool_kick_and_wait cf_threadpool_kick */ @@ -582,7 +581,7 @@ CF_INLINE CF_Mutex make_mutex() { return cf_make_mutex(); } CF_INLINE void destroy_mutex(CF_Mutex* mutex) { cf_destroy_mutex(mutex); } CF_INLINE void mutex_lock(CF_Mutex* mutex) { cf_mutex_lock(mutex); } CF_INLINE void mutex_unlock(CF_Mutex* mutex) { cf_mutex_unlock(mutex); } -CF_INLINE bool Mutexrylock(CF_Mutex* mutex) { return cf_mutex_try_lock(mutex); } +CF_INLINE bool mutex_try_lock(CF_Mutex* mutex) { return cf_mutex_try_lock(mutex); } CF_INLINE CF_ConditionVariable make_cv() { return cf_make_cv(); } CF_INLINE void destroy_cv(CF_ConditionVariable* cv) { cf_destroy_cv(cv); } diff --git a/include/cute_networking.h b/include/cute_networking.h index 5ba8b7bce..b41bdc9b2 100644 --- a/include/cute_networking.h +++ b/include/cute_networking.h @@ -8,12 +8,12 @@ #ifndef CF_NETWORKING_H #define CF_NETWORKING_H -#include +#include "cute_defines.h" // This entire file makes no sense for web builds, since web doesn't allow UDP. #ifndef CF_EMSCRIPTEN -#include +#include "cute_result.h" #include //-------------------------------------------------------------------------------------------------- @@ -89,12 +89,12 @@ typedef enum cn_address_type_t CF_AddressType; * @related CF_Address */ #define CF_ADDRESS_TYPE_DEFS \ - /* @entry */ \ - CF_ENUM(ADDRESS_TYPE_NONE, 0) \ - /* @entry */ \ - CF_ENUM(ADDRESS_TYPE_IPV4, 1) \ - /* @entry */ \ - CF_ENUM(ADDRESS_TYPE_IPV6, 2) \ + /* @entry No or invalid address type. */ \ + CF_ENUM(ADDRESS_TYPE_NONE, 0) \ + /* @entry IPv4 address type. */ \ + CF_ENUM(ADDRESS_TYPE_IPV4, 1) \ + /* @entry IPv6 address type. */ \ + CF_ENUM(ADDRESS_TYPE_IPV6, 2) \ /* @end */ enum @@ -107,7 +107,7 @@ enum /** * @function cf_address_init * @category net - * @brief Initialze a `CF_Address` from a C string. + * @brief Initialize a `CF_Address` from a C string. * @return Returns 0 on success, -1 on failure. * @related CF_Address cf_address_init cf_address_to_string cf_address_equals */ @@ -167,7 +167,7 @@ CF_API void CF_CALL cf_crypto_random_bytes(void* data, int byte_count); * @function cf_crypto_sign_keygen * @category net * @brief Generates a cryptographically secure keypair, used for facilitating connect tokens. - * @param public_key The public key of the keypair. Freely share this publicy. + * @param public_key The public key of the keypair. Freely share this publicly. * @param secret_key The secret key of the keypair. Keep this safe and hidden within your servers. * @related CF_CryptoKey cf_crypto_generate_key cf_generate_connect_token */ @@ -282,7 +282,7 @@ CF_API bool CF_CALL cf_client_pop_packet(CF_Client* client, void** packet, int* /** * @function cf_client_free_packet * @category net - * @brief Free's a packet created by `cf_client_pop_packet`. + * @brief Frees a packet created by `cf_client_pop_packet`. * @related CF_Client cf_client_pop_packet cf_client_free_packet cf_client_send */ CF_API void CF_CALL cf_client_free_packet(CF_Client* client, void* packet); @@ -320,25 +320,25 @@ CF_API CF_Result CF_CALL cf_client_send(CF_Client* client, const void* packet, i * @related CF_ClientState cf_client_state_to_string cf_client_state_get */ #define CF_CLIENT_STATE_DEFS \ - /* @entry */ \ + /* @entry The connect token has expired. */ \ CF_ENUM(CLIENT_STATE_CONNECT_TOKEN_EXPIRED, -6) \ - /* @entry */ \ + /* @entry The connect token is invalid. */ \ CF_ENUM(CLIENT_STATE_INVALID_CONNECT_TOKEN, -5) \ - /* @entry */ \ + /* @entry The connection attempt timed out. */ \ CF_ENUM(CLIENT_STATE_CONNECTION_TIMED_OUT, -4) \ - /* @entry */ \ + /* @entry The challenge response timed out. */ \ CF_ENUM(CLIENT_STATE_CHALLENGE_RESPONSE_TIMED_OUT, -3) \ - /* @entry */ \ + /* @entry The connection request timed out. */ \ CF_ENUM(CLIENT_STATE_CONNECTION_REQUEST_TIMED_OUT, -2) \ - /* @entry */ \ + /* @entry The connection was denied by the server. */ \ CF_ENUM(CLIENT_STATE_CONNECTION_DENIED, -1) \ - /* @entry */ \ + /* @entry The client is disconnected. */ \ CF_ENUM(CLIENT_STATE_DISCONNECTED, 0) \ - /* @entry */ \ + /* @entry The client is sending a connection request. */ \ CF_ENUM(CLIENT_STATE_SENDING_CONNECTION_REQUEST, 1) \ - /* @entry */ \ + /* @entry The client is sending a challenge response. */ \ CF_ENUM(CLIENT_STATE_SENDING_CHALLENGE_RESPONSE, 2) \ - /* @entry */ \ + /* @entry The client is connected. */ \ CF_ENUM(CLIENT_STATE_CONNECTED, 3) \ /* @end */ @@ -420,7 +420,7 @@ typedef struct CF_ServerConfig /* @member The public part of your public key cryptography used for connect tokens. This can be safely shared with your players publicly. See `CF_CryptoSignPublic`. */ CF_CryptoSignPublic public_key; - /* @member The secret part of your public key cryptography used for connect tokens. This must never be shared publicly and remain a complete secret only know to your servers. See `CF_CryptoSignSecret`. */ + /* @member The secret part of your public key cryptography used for connect tokens. This must never be shared publicly and remain a complete secret only known to your servers. See `CF_CryptoSignSecret`. */ CF_CryptoSignSecret secret_key; } CF_ServerConfig; // @end diff --git a/include/cute_noise.h b/include/cute_noise.h index d0b31ff1d..741454344 100644 --- a/include/cute_noise.h +++ b/include/cute_noise.h @@ -5,8 +5,8 @@ This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info */ -#ifndef CUTE_PERLIN_H -#define CUTE_PERLIN_H +#ifndef CF_NOISE_H +#define CF_NOISE_H #include "cute_defines.h" #include "cute_graphics.h" @@ -211,4 +211,4 @@ CF_INLINE CF_Pixel* noise_fbm_pixels_wrapped(int w, int h, uint64_t seed, float #endif // CF_CPP -#endif // CUTE_PERLIN_H +#endif // CF_NOISE_H diff --git a/include/cute_png_cache.h b/include/cute_png_cache.h deleted file mode 100644 index 2280102d8..000000000 --- a/include/cute_png_cache.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - Cute Framework - Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ - - This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info -*/ - -#ifndef CF_PNG_CACHE_H -#define CF_PNG_CACHE_H - -#include "cute_defines.h" -#include "cute_result.h" -#include "cute_color.h" -#include "cute_sprite.h" - -#include "cute/cute_png.h" - -//-------------------------------------------------------------------------------------------------- -// C API - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -/** - * @struct CF_Png - * @category png_cache - * @brief A single image of raw pixels, loaded from a png cache. - * @remarks The png cache is used to load png images from disk in order to make sprites. The png cache - * system is an advanced option for people who want lower level access to creating their own - * custom sprites, for example by loading sprites from their own custom animation format. If you just - * want a really easy way to load sprites, look at cute_sprite.h for the easy sprite API (search for - * `easy_make_sprite` or `make_sprite` functions). - * - * You will mostly just care about these three functions. - * - * cf_png_cache_load - * cf_png_cache_unload - * cf_make_png_cache_sprite - * - * It's a cache, which means it actually caches images loaded in RAM, so subsequent - * calls to `cf_png_cache_load` won't have to fetch the image off of disk, as long as - * the image is currently cached in RAM. - * @related CF_Png cf_png_defaults cf_png_cache_load cf_make_png_cache_animation cf_make_png_cache_sprite - */ -typedef struct CF_Png -{ - /* @member Path to the associated png on-disk. */ - const char* path; - - /* @member Unique identifier assigned by the cache. */ - uint64_t id; - - /* @member Pointer to the pixels of the png image. */ - CF_Pixel* pix; - - /* @member Width of the image in pixels. */ - int w; - - /* @member Height of the image in pixels. */ - int h; -} CF_Png; -// @end - -/** - * @function cf_png_defaults - * @category png_cache - * @brief Initialize an empty `CF_Png` struct. - * @return Returns an empty `CF_Png` struct. - * @related CF_Png cf_png_defaults cf_png_cache_load cf_make_png_cache_animation cf_make_png_cache_sprite - */ -CF_INLINE CF_Png cf_png_defaults(void) -{ - CF_Png result = { 0 }; - result.id = ~0; - return result; -} - -/** - * @function cf_png_cache_load - * @category png_cache - * @brief Returns an image `CF_Png` from the cache. - * @remarks If it does not exist in the cache, it is loaded from disk and placed into the cache. - * @related CF_Png cf_png_defaults cf_png_cache_load cf_make_png_cache_animation cf_make_png_cache_sprite - */ -CF_API CF_Result CF_CALL cf_png_cache_load(const char* png_path, CF_Png* png /*= NULL*/); - -/** - * @function cf_png_cache_load_from_memory - * @category png_cache - * @brief Returns an image `CF_Png` from the cache. - * @remarks If it does not exist in the cache, it is loaded from memory and placed into the cache. - * @related CF_Png cf_png_defaults cf_png_cache_load cf_make_png_cache_animation cf_make_png_cache_sprite - */ -CF_API CF_Result CF_CALL cf_png_cache_load_from_memory(const char* png_path, const void* memory, size_t size, CF_Png* png /*= NULL*/); - -/** - * @function cf_png_cache_unload - * @category png_cache - * @brief Unloads an image from the cache. - * @related CF_Png cf_png_defaults cf_png_cache_load cf_make_png_cache_animation cf_make_png_cache_sprite - */ -CF_API void CF_CALL cf_png_cache_unload(CF_Png png); - -/** - * @function cf_make_png_cache_animation - * @category png_cache - * @brief Constructs an animation out of an array of frames, along with their delays in milliseconds, or returns one from the cache if it already exists. - * @param name A unique name for the animation. - * @param pngs An array of pngs, see `CF_Png`. - * @param pngs_count The number of images in the `pngs` array. - * @param delays A delay in milliseconds for each frame of the animation. - * @param delays_count The number of floats in the `delays` array. This must match `pngs_count`. - * @return Returns a `CF_Animation`, representing a single animation sequence. - * @remarks Since png files do not contain any kind of animation information (frame delays or sets of frames) - * you must specify all of the animation data yourself in order to make sprites. To flip between different - * animations you can construct an animation table with `cf_make_png_cache_animation_table`, which returns - * a table of unique animation names to individual `CF_Animation`'s. - * @related CF_Png cf_png_cache_load cf_make_png_cache_animation cf_make_png_cache_animation_table cf_make_png_cache_sprite - */ -CF_API const CF_Animation* CF_CALL cf_make_png_cache_animation(const char* name, const CF_Png* pngs, int pngs_count, const float* delays, int delays_count); - -/** - * @function cf_png_cache_get_animation - * @category png_cache - * @brief Looks up an animation within the png cache by name. - * @param name A unique name for the animation. - * @return Returns a `CF_Animation`. - * @remarks You may use this function if you wish to implement your own sprites. However, it's recommended to instead use - * `cf_make_png_cache_sprite` and `CF_Sprite`. - * @related CF_Png cf_png_cache_load cf_make_png_cache_animation cf_make_png_cache_animation_table cf_make_png_cache_sprite - */ -CF_API const CF_Animation* CF_CALL cf_png_cache_get_animation(const char* name); - -/** - * @function cf_make_png_cache_animation_table - * @category png_cache - * @brief Constructs an animation table given an array of animations, or returns one from the cache if it already exists. - * @param sprite_name A unique name for the animation table. - * @return Returns a `CF_Animation` hashtable, see `htbl`. - * @remarks The table returned is a map of animation names to individual `CF_Animation`'s. This is represents the guts of a sprite - * implementation. You may use this function if you wish to implement your own sprites. However, it's recommended to instead use - * `cf_make_png_cache_sprite` and `CF_Sprite`. - * @related CF_Png cf_png_cache_load cf_make_png_cache_animation cf_make_png_cache_animation_table cf_make_png_cache_sprite - */ -CF_API const htbl CF_Animation** CF_CALL cf_make_png_cache_animation_table(const char* sprite_name, const CF_Animation* const* animations, int animations_count); - -/** - * @function cf_png_cache_get_animation_table - * @category png_cache - * @brief Looks up an animation table within the png cache by name. - * @param sprite_name A unique name for the animation table. - * @return Returns a hashtable of unique sprite names to `CF_Animation`'s, see `cf_make_png_cache_sprite`. - * @related CF_Png cf_png_cache_load cf_make_png_cache_animation cf_make_png_cache_animation_table cf_make_png_cache_sprite - */ -CF_API const htbl CF_Animation** CF_CALL cf_png_cache_get_animation_table(const char* sprite_name); - -/** - * Makes a sprite. Each sprite must refer to an animation table previously constructed by `cf_make_png_cache_animation_table`. - * You can supply the pointer to the animation table yourself in `table`, or just leave it NULL. - * If table is `NULL` then `sprite_name` should be the path to your png file instead of the sprite's actual name. - */ -/** - * @function cf_make_png_cache_sprite - * @category png_cache - * @brief Constructs an `CF_Sprite` out of your own custom built animation table. - * @param sprite_name A unique name for the sprite. - * @param table An animation table. - * @return Returns a `CF_Sprite`, ready to use in e.g. `draw_sprite`. - * @related CF_Png cf_png_cache_load cf_make_png_cache_animation cf_make_png_cache_animation_table cf_make_png_cache_sprite - */ -CF_API CF_Sprite CF_CALL cf_make_png_cache_sprite(const char* sprite_name, const CF_Animation** table /*= NULL*/); - -#ifdef __cplusplus -} -#endif // __cplusplus - -//-------------------------------------------------------------------------------------------------- -// C++ API - -#ifdef CF_CPP - -#include "cute_array.h" - -namespace Cute -{ - -CF_INLINE CF_Result png_cache_load(const char* png_path, CF_Png* png = NULL) { return cf_png_cache_load(png_path, (CF_Png*)png); } -CF_INLINE CF_Result png_cache_load_mem(const char* png_path, const void* memory, size_t size, CF_Png* png = NULL) { return cf_png_cache_load_from_memory(png_path, memory, size, png); } -CF_INLINE void png_cache_unload(CF_Png png) { cf_png_cache_unload(png); } -CF_API const CF_Animation* CF_CALL make_png_cache_animation(const char* name, const Array& pngs, const Array& delays); -CF_INLINE const CF_Animation* png_cache_get_animation(const char* name) { return cf_png_cache_get_animation(name); } -CF_API const CF_Animation** CF_CALL make_png_cache_animation_table(const char* sprite_name, const Array& animations); -CF_INLINE const CF_Animation** png_cache_get_animation_table(const char* sprite_name) { return cf_png_cache_get_animation_table(sprite_name); } -CF_INLINE CF_Sprite make_png_cache_sprite(const char* sprite_name, const CF_Animation** table = NULL) { return cf_make_png_cache_sprite(sprite_name, table); } - -} - -#endif // CF_CPP - -#endif // CF_PNG_CACHE_H diff --git a/include/cute_rnd.h b/include/cute_rnd.h index 1f5c58305..7c8ba55f5 100644 --- a/include/cute_rnd.h +++ b/include/cute_rnd.h @@ -49,7 +49,7 @@ typedef struct CF_Rnd CF_INLINE CF_Rnd CF_CALL cf_rnd_seed(uint64_t seed); /** - * @function cf_rnd_uint + * @function cf_rnd_uint64 * @category random * @brief Returns a random `uint64_t`. * @param rnd The random number generator state. diff --git a/include/cute_shader_bytecode.h b/include/cute_shader_bytecode.h index 67ae76652..469146a05 100644 --- a/include/cute_shader_bytecode.h +++ b/include/cute_shader_bytecode.h @@ -8,7 +8,7 @@ #ifndef CF_SHADER_BYTECODE_H #define CF_SHADER_BYTECODE_H -#include +#include "cute_defines.h" #ifdef __cplusplus extern "C" { @@ -143,15 +143,21 @@ typedef struct CF_ShaderInfo { /* @member Number of samplers. */ int num_samplers; - /* @member Number of storage textures. */ + /* @member Number of readonly storage textures. */ int num_storage_textures; - /* @member Number of storage buffers. */ + /* @member Number of readonly storage buffers. */ int num_storage_buffers; + /* @member Number of readwrite storage textures. */ + int num_readwrite_storage_textures; + /* @member Number of readwrite storage buffers. */ + int num_readwrite_storage_buffers; /* @member Number of images. */ int num_images; /* @member Name of each images. */ const char** image_names; + /* @member Binding slot of each image. */ + int* image_binding_slots; /* @member Number of uniform blocks. */ int num_uniforms; diff --git a/include/cute_sprite.h b/include/cute_sprite.h index 925a0e690..d13a9b014 100644 --- a/include/cute_sprite.h +++ b/include/cute_sprite.h @@ -10,14 +10,12 @@ #define CF_SPRITE_H #include "cute_defines.h" -#include "cute_array.h" -#include "cute_hashtable.h" #include "cute_string.h" #include "cute_math.h" #include "cute_time.h" #include "cute_color.h" #include "cute_result.h" -#include "cute_math.h" +#include "cute_image.h" //-------------------------------------------------------------------------------------------------- // C API @@ -26,22 +24,6 @@ extern "C" { #endif // __cplusplus -/** - * @struct CF_Frame - * @category sprite - * @brief Represents one frame of animation within a sprite. - * @related CF_Frame CF_Animation CF_Sprite - */ -typedef struct CF_Frame -{ - /* @member Unique identifier for this frame's image. */ - uint64_t id; - - /* @member Number of seconds to display this frame. */ - float delay; -} CF_Frame; -// @end - /** * @enum CF_PlayDirection * @category sprite @@ -80,28 +62,6 @@ CF_INLINE const char* cf_play_direction_to_string(CF_PlayDirection dir) } } -/** - * @struct CF_Animation - * @category sprite - * @brief A named set of frames in sequence. - * @related CF_Frame CF_Animation CF_Sprite - */ -typedef struct CF_Animation -{ - /* The name of the animation. */ - const char* name; - - /* The direction to play the animation. See `CF_PlayDirection`. */ - CF_PlayDirection play_direction; - - /* The frames of the animation. See `dyna`. */ - dyna CF_Frame* frames; - - /* For internal use. */ - int frame_offset; -} CF_Animation; -// @end - /** * @struct CF_SpriteSlice * @category sprite @@ -132,6 +92,9 @@ typedef struct CF_SpriteSlice */ typedef struct CF_Sprite { + /* @member For internal use -- Unique sprite id */ + uint64_t id; + /* @member The name of the sprite. */ const char* name; @@ -141,21 +104,21 @@ typedef struct CF_Sprite /* @member Height of the sprite in pixels. */ int h; + /* @member For internal use -- Cached image ID of the current frame (set by cf_sprite_update / cf_sprite_play). */ + uint64_t _image_id; + + /* @member For internal use -- Cached pivot of the current frame (set by cf_sprite_update / cf_sprite_play). */ + CF_V2 _pivot; + + /* @member For internal use -- Cached center patch of the current frame for 9-slice (set by cf_sprite_update / cf_sprite_play). */ + CF_Aabb _center_patch; + /* @member Scale factor for the sprite when drawing. Default of `(1, 1)`. See `cf_draw_sprite`. */ CF_V2 scale; /* @member A local offset/origin for the sprite when drawing. Defaults to `(0, 0)`. */ CF_V2 offset; - /* @member A local pivot (offset) per-frame. This value comes from an aseprite slice on a particular frame if the `pivot` box is checked. Make sure to only have one pivot per frame when authoring your .ase file or they will overwrite each other upon loading. */ - dyna CF_V2* pivots; - - /* @member Center patch of a 9 slice, mainly to be used for UI*/ - dyna const CF_Aabb* center_patches; - - /* @member All the `CF_SpriteSlice`'s in the sprite. These get loaded from the .ase file. */ - dyna const CF_SpriteSlice* slices; - /* @member An opacity value for the entire sprite. Default of 1.0f. See `cf_draw_sprite`. */ float opacity; @@ -174,6 +137,9 @@ typedef struct CF_Sprite /* @member Whether or not to loop animations. */ bool loop; + /* @member Set to true when a non-looping animation reaches its last frame. Only meaningful when `loop` is false. Reset to false by `cf_sprite_play`. */ + bool finished; + /* @member The current elapsed time within a frame of animation. */ float t; @@ -183,11 +149,11 @@ typedef struct CF_Sprite /* @member Controls the animation play direction. This gets set each time `cf_sprite_play` is called to the animation's play direction. You may override this member yourself after calling `cf_sprite_play`. */ CF_PlayDirection play_direction; - /* @member A pointer to the current animation to display, from within the set `animations`. See `CF_Animation`. */ - const CF_Animation* animation; + /* @member Which blend to draw. 0 = default (all visible layers). Use cf_sprite_add_blend to register additional blends. */ + int blend_index; - /* @member The set of named animations for this sprite. See `CF_Animation` and `htbl`. */ - htbl const CF_Animation** animations; + /* @member Interned name of the current animation. */ + const char* animation_name; /* @member An optional transform for rendering within a particular space. See `CF_Transform`. */ CF_Transform transform; @@ -202,16 +168,7 @@ typedef struct CF_Sprite * you may be looking for `cf_make_sprite` or `cf_make_easy_sprite`. * @related CF_Sprite cf_sprite_defaults cf_make_easy_sprite cf_make_sprite */ -CF_INLINE CF_Sprite cf_sprite_defaults(void) -{ - CF_Sprite sprite = { 0 }; - sprite.scale = cf_v2(1, 1); - sprite.opacity = 1.0f; - sprite.play_speed_multiplier = 1.0f; - sprite.transform = cf_make_transform(); - sprite.loop = true; - return sprite; -} +CF_API CF_Sprite CF_CALL cf_sprite_defaults(); /** * @function cf_make_easy_sprite_from_png @@ -260,7 +217,7 @@ CF_API void CF_CALL cf_easy_sprite_unload(CF_Sprite *sprite); * @return Returns a `CF_Sprite` that can be drawn with `cf_sprite_draw`. * @remarks This function caches the sprite internally. Subsequent calls to load the same sprite will be very fast; you can use * this function directly to fetch sprites that were already loaded. If you want to load sprites with your own custom - * animation data, instead of using the .ase/.aseprite format, you can try out `cf_png_cache_load` for a more low-level option. + * animation data, instead of using the .ase/.aseprite format, you can try out `cf_custom_sprite_load_png` for a more low-level option. * See [Virtual File System](https://randygaul.github.io/cute_framework/topics/virtual_file_system). * @related CF_Sprite cf_make_easy_sprite_from_png cf_make_easy_sprite_from_pixels cf_easy_sprite_update_pixels cf_make_sprite_from_memory */ @@ -274,7 +231,7 @@ CF_API CF_Sprite CF_CALL cf_make_sprite(const char* aseprite_path); * @return Returns a `CF_Sprite` that can be drawn with `cf_sprite_draw`. * @remarks This function caches the sprite internally. Subsequent calls to load the same sprite will be very fast; you can use * this function directly to fetch sprites that were already loaded. If you want to load sprites with your own custom - * animation data, instead of using the .ase/.aseprite format, you can try out `cf_png_cache_load` for a more low-level option. + * animation data, instead of using the .ase/.aseprite format, you can try out `cf_custom_sprite_load_png` for a more low-level option. * See [Virtual File System](https://randygaul.github.io/cute_framework/topics/virtual_file_system). * @related CF_Sprite cf_make_easy_sprite_from_png cf_make_easy_sprite_from_pixels cf_easy_sprite_update_pixels */ @@ -302,15 +259,64 @@ CF_API void CF_CALL cf_sprite_unload(const char* aseprite_path); /** * @function cf_sprite_reload * @category sprite - * @brief Reloads the sprite's pixels from disk. + * @brief Reloads the sprite's pixels from disk in-place. * @param sprite The sprite to reload. - * @return The reloaded sprite. * @remarks This function is designed to help support asset or image hotloading/reloading during development. - * This function is *not* designed to be called once you ship your game. - * All old instances of the sprite are now invalid and should be reset to this return value. + * Frame IDs are reused so other sprites sharing the same asset will pick up changes on their next update. * @related CF_Sprite cf_make_sprite cf_sprite_unload cf_sprite_reload */ -CF_API CF_Sprite CF_CALL cf_sprite_reload(const CF_Sprite* sprite); +CF_API void CF_CALL cf_sprite_reload(CF_Sprite* sprite); + +/** + * @function cf_sprite_animation_count + * @category sprite + * @brief Returns the number of animations in the sprite's asset. + * @param sprite The sprite. + * @related CF_Sprite cf_sprite_animation_count cf_sprite_animation_name_at + */ +CF_API int CF_CALL cf_sprite_animation_count(const CF_Sprite* sprite); + +/** + * @function cf_sprite_animation_name_at + * @category sprite + * @brief Returns the name of the animation at the given index. + * @param sprite The sprite. + * @param index Zero-based index of the animation. + * @return An interned string, or NULL if out of range. + * @related CF_Sprite cf_sprite_animation_count cf_sprite_animation_name_at + */ +CF_API const char* CF_CALL cf_sprite_animation_name_at(const CF_Sprite* sprite, int index); + +/** + * @function cf_sprite_pivot + * @category sprite + * @brief Returns the cached pivot of the current frame. + * @param sprite The sprite. + * @related CF_Sprite cf_sprite_pivot + */ +CF_API CF_V2 CF_CALL cf_sprite_pivot(const CF_Sprite* sprite); + +/** + * @function cf_sprite_add_blend + * @category sprite + * @brief Register a layer blend for an aseprite sprite asset. + * @param path Virtual path used when loading the sprite. + * @param layer_names Array of layer name strings to include. + * @param layer_count Number of entries in layer_names. + * @return The blend index (1, 2, ...). Blend 0 = default (all visible layers). + * @remarks Ignores file visibility flags -- includes exactly the named layers. + * @related CF_Sprite cf_sprite_add_blend cf_sprite_blend_count + */ +CF_API int CF_CALL cf_sprite_add_blend(const char* path, const char** layer_names, int layer_count); + +/** + * @function cf_sprite_blend_count + * @category sprite + * @brief Returns the number of blends for this sprite's asset (>= 1). + * @param sprite The sprite. + * @related CF_Sprite cf_sprite_add_blend cf_sprite_blend_count + */ +CF_API int CF_CALL cf_sprite_blend_count(const CF_Sprite* sprite); //-------------------------------------------------------------------------------------------------- // In-line implementation of `CF_Sprite` functions. @@ -359,7 +365,7 @@ CF_INLINE float cf_sprite_get_scale_y(CF_Sprite* sprite) { CF_ASSERT(sprite); re /** * @function cf_sprite_set_scale * @category sprite - * @brief Returns the sprite's scale on the x-axis. + * @brief Sets the sprite's scale. * @related CF_Sprite cf_sprite_get_scale_x cf_sprite_get_scale_y cf_sprite_set_scale_x cf_sprite_set_scale_y cf_sprite_set_scale */ CF_INLINE void cf_sprite_set_scale(CF_Sprite* sprite, CF_V2 scale) { CF_ASSERT(sprite); sprite->scale = scale; } @@ -367,7 +373,7 @@ CF_INLINE void cf_sprite_set_scale(CF_Sprite* sprite, CF_V2 scale) { CF_ASSERT(s /** * @function cf_sprite_set_scale_x * @category sprite - * @brief Returns the sprite's scale on the x-axis. + * @brief Sets the sprite's scale on the x-axis. * @related CF_Sprite cf_sprite_get_scale_x cf_sprite_get_scale_y cf_sprite_set_scale_x cf_sprite_set_scale_y cf_sprite_set_scale */ CF_INLINE void cf_sprite_set_scale_x(CF_Sprite* sprite, float x) { CF_ASSERT(sprite); sprite->scale.x = x; } @@ -375,7 +381,7 @@ CF_INLINE void cf_sprite_set_scale_x(CF_Sprite* sprite, float x) { CF_ASSERT(spr /** * @function cf_sprite_set_scale_y * @category sprite - * @brief Returns the sprite's scale on the y-axis. + * @brief Sets the sprite's scale on the y-axis. * @related CF_Sprite cf_sprite_get_scale_x cf_sprite_get_scale_y cf_sprite_set_scale_x cf_sprite_set_scale_y cf_sprite_set_scale */ CF_INLINE void cf_sprite_set_scale_y(CF_Sprite* sprite, float y) { CF_ASSERT(sprite); sprite->scale.y = y; } @@ -399,7 +405,7 @@ CF_INLINE float cf_sprite_get_offset_y(CF_Sprite* sprite) { CF_ASSERT(sprite); r /** * @function cf_sprite_set_offset_x * @category sprite - * @brief Returns the sprite's local offset on the x-axis. + * @brief Sets the sprite's local offset on the x-axis. * @related CF_Sprite cf_sprite_get_offset_x cf_sprite_get_offset_y cf_sprite_set_offset_x cf_sprite_set_offset_y */ CF_INLINE void cf_sprite_set_offset_x(CF_Sprite* sprite, float offset) { CF_ASSERT(sprite); sprite->offset.x = offset; } @@ -407,7 +413,7 @@ CF_INLINE void cf_sprite_set_offset_x(CF_Sprite* sprite, float offset) { CF_ASSE /** * @function cf_sprite_set_offset_y * @category sprite - * @brief Returns the sprite's local offset on the y-axis. + * @brief Sets the sprite's local offset on the y-axis. * @related CF_Sprite cf_sprite_get_offset_x cf_sprite_get_offset_y cf_sprite_set_offset_x cf_sprite_set_offset_y */ CF_INLINE void cf_sprite_set_offset_y(CF_Sprite* sprite, float offset) { CF_ASSERT(sprite); sprite->offset.y = offset; } @@ -416,6 +422,7 @@ CF_INLINE void cf_sprite_set_offset_y(CF_Sprite* sprite, float offset) { CF_ASSE * @function cf_sprite_get_opacity * @category sprite * @brief Returns the sprite's opacity, a value from 0-1. + * @related CF_Sprite cf_sprite_get_opacity cf_sprite_set_opacity */ CF_INLINE float cf_sprite_get_opacity(CF_Sprite* sprite) { CF_ASSERT(sprite); return sprite->opacity; } @@ -423,6 +430,7 @@ CF_INLINE float cf_sprite_get_opacity(CF_Sprite* sprite) { CF_ASSERT(sprite); re * @function cf_sprite_set_opacity * @category sprite * @brief Sets the sprite's opacity, a value from 0-1. + * @related CF_Sprite cf_sprite_get_opacity cf_sprite_set_opacity */ CF_INLINE void cf_sprite_set_opacity(CF_Sprite* sprite, float opacity) { CF_ASSERT(sprite); sprite->opacity = opacity; } @@ -430,6 +438,7 @@ CF_INLINE void cf_sprite_set_opacity(CF_Sprite* sprite, float opacity) { CF_ASSE * @function cf_sprite_set_loop * @category sprite * @brief Sets whether or not the sprite can loop. True for looping. If false the animation will pause on the final frame. + * @related CF_Sprite cf_sprite_set_loop cf_sprite_get_loop cf_sprite_get_loop_count */ CF_INLINE void cf_sprite_set_loop(CF_Sprite* sprite, bool loop) { CF_ASSERT(sprite); sprite->loop = loop; } @@ -437,6 +446,7 @@ CF_INLINE void cf_sprite_set_loop(CF_Sprite* sprite, bool loop) { CF_ASSERT(spri * @function cf_sprite_get_loop * @category sprite * @brief Gets whether or not the sprite can loop. True for looping. If false the animation will pause on the final frame. + * @related CF_Sprite cf_sprite_set_loop cf_sprite_get_loop cf_sprite_get_loop_count */ CF_INLINE bool cf_sprite_get_loop(CF_Sprite* sprite) { CF_ASSERT(sprite); return sprite->loop; } @@ -445,26 +455,15 @@ CF_INLINE bool cf_sprite_get_loop(CF_Sprite* sprite) { CF_ASSERT(sprite); return * @category sprite * @brief Searches for and returns a particular slice. A zero'd out `CF_Aabb` is returned if no match was found. * @remarks Only fetches for slices within the current frame of the current animation. + * @related CF_Sprite CF_SpriteSlice cf_sprite_get_slice */ -CF_INLINE CF_Aabb cf_sprite_get_slice(CF_Sprite* sprite, const char* name) -{ - CF_ASSERT(sprite); - CF_Aabb not_found = { 0 }; - name = sintern(name); - int frame = sprite->frame_index + (sprite->animation ? sprite->animation->frame_offset : 0); - for (int i = 0; i < asize(sprite->slices); ++i) { - // >= here used according to Aseprite file spec, says they are valid for subsequent frames. - if (sprite->slices[i].name == name && sprite->slices[i].frame_index >= frame) { - return sprite->slices[i].box; - } - } - return not_found; -} +CF_API CF_Aabb CF_CALL cf_sprite_get_slice(CF_Sprite* sprite, const char* name); /** * @function cf_sprite_get_play_speed_multiplier * @category sprite * @brief Returns the sprite's playing speed multiplier. + * @related CF_Sprite cf_sprite_get_play_speed_multiplier cf_sprite_set_play_speed_multiplier */ CF_INLINE float cf_sprite_get_play_speed_multiplier(CF_Sprite* sprite) { CF_ASSERT(sprite); return sprite->play_speed_multiplier; } @@ -472,6 +471,7 @@ CF_INLINE float cf_sprite_get_play_speed_multiplier(CF_Sprite* sprite) { CF_ASSE * @function cf_sprite_set_play_speed_multiplier * @category sprite * @brief Sets the sprite's playing speed multiplier. + * @related CF_Sprite cf_sprite_get_play_speed_multiplier cf_sprite_set_play_speed_multiplier */ CF_INLINE void cf_sprite_set_play_speed_multiplier(CF_Sprite* sprite, float multiplier) { CF_ASSERT(sprite); sprite->play_speed_multiplier = multiplier; } @@ -479,12 +479,14 @@ CF_INLINE void cf_sprite_set_play_speed_multiplier(CF_Sprite* sprite, float mult * @function cf_sprite_get_loop_count * @category sprite * @brief Returns the sprite's loop count. + * @related CF_Sprite cf_sprite_get_loop_count cf_sprite_set_loop cf_sprite_get_loop */ CF_INLINE int cf_sprite_get_loop_count(CF_Sprite* sprite) { CF_ASSERT(sprite); return sprite->loop_count; } /** * @function cf_sprite_get_local_offset * @category sprite * @brief Returns the sprite's local offset, set by loading the .ase file if a slice named "origin" exists. + * @related CF_Sprite cf_sprite_get_local_offset cf_sprite_get_offset_x cf_sprite_get_offset_y */ CF_INLINE CF_V2 cf_sprite_get_local_offset(CF_Sprite* sprite) { CF_ASSERT(sprite); return sprite->offset; } @@ -496,64 +498,7 @@ CF_INLINE CF_V2 cf_sprite_get_local_offset(CF_Sprite* sprite) { CF_ASSERT(sprite * @remarks Call this once per frame. * @related CF_Sprite cf_make_sprite cf_sprite_update cf_sprite_play cf_sprite_pause */ -CF_INLINE void cf_sprite_update(CF_Sprite* sprite) -{ - CF_ASSERT(sprite); - if (sprite->paused) return; - if (!sprite->animation) return; - - sprite->t += CF_DELTA_TIME * sprite->play_speed_multiplier; - int frame_count = alen(sprite->animation->frames); - CF_PlayDirection direction = sprite->play_direction; - if (direction == CF_PLAY_DIRECTION_FORWARDS) { - if (sprite->t >= sprite->animation->frames[sprite->frame_index].delay) { - sprite->frame_index++; - sprite->t = 0; - if (sprite->frame_index == frame_count) { - if (sprite->loop) { - sprite->loop_count++; - sprite->frame_index = 0; - } else { - sprite->frame_index--; - } - } - } - } else if (direction == CF_PLAY_DIRECTION_BACKWARDS) { - if (sprite->t >= sprite->animation->frames[sprite->frame_index].delay) { - sprite->frame_index--; - sprite->t = 0; - if (sprite->frame_index < 0) { - if (sprite->loop) { - sprite->loop_count++; - sprite->frame_index = frame_count - 1; - } else { - sprite->frame_index++; - } - } - } - } else if (direction == CF_PLAY_DIRECTION_PINGPONG) { - if (sprite->t >= sprite->animation->frames[sprite->frame_index].delay) { - sprite->t = 0; - if (sprite->loop_count % 2) { - sprite->frame_index--; - if (sprite->frame_index < 0) { - if (sprite->loop) { - sprite->loop_count++; - sprite->frame_index++; - } else { - sprite->frame_index = 0; - } - } - } else { - sprite->frame_index++; - if (sprite->frame_index == frame_count) { - sprite->loop_count++; - sprite->frame_index--; - } - } - } - } -} +CF_API void CF_CALL cf_sprite_update(CF_Sprite* sprite); /** * @function cf_sprite_reset @@ -562,47 +507,27 @@ CF_INLINE void cf_sprite_update(CF_Sprite* sprite) * @param sprite The sprite. * @related CF_Sprite cf_sprite_update cf_sprite_play */ -CF_INLINE void cf_sprite_reset(CF_Sprite* sprite) -{ - CF_ASSERT(sprite); - sprite->paused = false; - sprite->frame_index = 0; - sprite->loop_count = 0; - sprite->t = 0; - if (sprite->animation) sprite->play_direction = sprite->animation->play_direction; -} +CF_API void CF_CALL cf_sprite_reset(CF_Sprite* sprite); /** * @function cf_sprite_play * @category sprite - * @brief Switches to a new aninmation and starts playing it from the beginning. + * @brief Switches to a new animation and starts playing it from the beginning. * @param sprite The sprite. * @param animation Name of the animation to switch to and start playing. * @related CF_Sprite cf_sprite_update cf_sprite_play cf_sprite_is_playing */ -CF_INLINE void cf_sprite_play(CF_Sprite* sprite, const char* animation) -{ - CF_ASSERT(sprite); - if (!sprite->animations) return; - sprite->animation = hfind(sprite->animations, sintern(animation)); - CF_ASSERT(sprite->animation); - cf_sprite_reset(sprite); -} +CF_API void CF_CALL cf_sprite_play(CF_Sprite* sprite, const char* animation); /** * @function cf_sprite_is_playing * @category sprite - * @brief Returns true if `animation` the is currently playing animation. + * @brief Returns true if the named animation is currently playing. * @param sprite The sprite. * @param animation Name of the animation. * @related CF_Sprite cf_sprite_update cf_sprite_play cf_sprite_is_playing */ -CF_INLINE bool cf_sprite_is_playing(CF_Sprite* sprite, const char* animation) -{ - CF_ASSERT(sprite); - if (!sprite->animation) return false; - return !CF_STRCMP(animation, sprite->animation->name); -} +CF_API bool CF_CALL cf_sprite_is_playing(CF_Sprite* sprite, const char* animation); /** * @function cf_sprite_pause @@ -646,7 +571,7 @@ CF_INLINE void cf_sprite_toggle_pause(CF_Sprite* sprite) /** * @function cf_sprite_flip_x * @category sprite - * @brief Flip's the sprite on the x-axis. + * @brief Flips the sprite on the x-axis. * @param sprite The sprite. * @remarks Works by flipping the sign of the sprite's scale on the x-axis. * @related CF_Sprite cf_sprite_flip_x cf_sprite_flip_y @@ -660,7 +585,7 @@ CF_INLINE void cf_sprite_flip_x(CF_Sprite* sprite) /** * @function cf_sprite_flip_y * @category sprite - * @brief Flip's the sprite on the y-axis. + * @brief Flips the sprite on the y-axis. * @param sprite The sprite. * @remarks Works by flipping the sign of the sprite's scale on the y-axis. * @related CF_Sprite cf_sprite_flip_x cf_sprite_flip_y @@ -678,12 +603,7 @@ CF_INLINE void cf_sprite_flip_y(CF_Sprite* sprite) * @param sprite The sprite. * @related CF_Sprite cf_sprite_frame_count cf_sprite_current_frame cf_sprite_frame_delay cf_sprite_animation_delay */ -CF_INLINE int cf_sprite_frame_count(const CF_Sprite* sprite) -{ - CF_ASSERT(sprite); - if (!sprite->animation) return 0; - return alen(sprite->animation->frames); -} +CF_API int CF_CALL cf_sprite_frame_count(const CF_Sprite* sprite); /** * @function cf_sprite_current_frame @@ -705,11 +625,7 @@ CF_INLINE int cf_sprite_current_frame(const CF_Sprite* sprite) * @param sprite The sprite. * @related CF_Sprite cf_sprite_frame_count cf_sprite_current_frame cf_sprite_frame_delay cf_sprite_animation_delay */ -CF_INLINE int cf_sprite_current_global_frame(const CF_Sprite* sprite) -{ - CF_ASSERT(sprite); - return sprite->frame_index + (sprite->animation ? sprite->animation->frame_offset : 0); -} +CF_API int CF_CALL cf_sprite_current_global_frame(const CF_Sprite* sprite); /** * @function cf_sprite_set_frame @@ -719,15 +635,7 @@ CF_INLINE int cf_sprite_current_global_frame(const CF_Sprite* sprite) * @param frame The frame number to set. * @related CF_Sprite cf_sprite_frame_count cf_sprite_current_frame cf_sprite_frame_delay cf_sprite_animation_delay */ -CF_INLINE void cf_sprite_set_frame(CF_Sprite* sprite, int frame) -{ - CF_ASSERT(sprite); - if (!sprite->animation) return; - int frame_count = alen(sprite->animation->frames); - CF_ASSERT(frame >= 0 && frame < frame_count); - sprite->frame_index = frame; - sprite->t = 0; -} +CF_API void CF_CALL cf_sprite_set_frame(CF_Sprite* sprite, int frame); /** * @function cf_sprite_frame_delay @@ -736,12 +644,7 @@ CF_INLINE void cf_sprite_set_frame(CF_Sprite* sprite, int frame) * @param sprite The sprite. * @related CF_Sprite CF_Frame cf_sprite_frame_count cf_sprite_current_frame cf_sprite_frame_delay cf_sprite_animation_delay cf_sprite_animation_interpolant */ -CF_INLINE float cf_sprite_frame_delay(CF_Sprite* sprite) -{ - CF_ASSERT(sprite); - if (!sprite->animation) return 0; - return sprite->animation->frames[sprite->frame_index].delay; -} +CF_API float CF_CALL cf_sprite_frame_delay(CF_Sprite* sprite); /** * @function cf_sprite_animation_delay @@ -750,17 +653,7 @@ CF_INLINE float cf_sprite_frame_delay(CF_Sprite* sprite) * @param sprite The sprite. * @related CF_Sprite CF_Frame CF_Animation cf_sprite_frame_count cf_sprite_current_frame cf_sprite_frame_delay cf_sprite_animation_delay cf_sprite_animation_interpolant */ -CF_INLINE float cf_sprite_animation_delay(CF_Sprite* sprite) -{ - CF_ASSERT(sprite); - if (!sprite->animation) return 0; - int count = cf_sprite_frame_count(sprite); - float delay = 0; - for (int i = 0; i < count; ++i) { - delay += sprite->animation->frames[i].delay; - } - return delay; -} +CF_API float CF_CALL cf_sprite_animation_delay(CF_Sprite* sprite); /** * @function cf_sprite_animation_interpolant @@ -770,15 +663,7 @@ CF_INLINE float cf_sprite_animation_delay(CF_Sprite* sprite) * @remarks 0 means just started, while 1 means finished. * @related CF_Sprite CF_Frame CF_Animation cf_sprite_frame_count cf_sprite_current_frame cf_sprite_frame_delay cf_sprite_animation_delay */ -CF_INLINE float cf_sprite_animation_interpolant(CF_Sprite* sprite) -{ - CF_ASSERT(sprite); - // TODO -- Backwards and pingpong. - if (!sprite->animation) return 0; - float delay = cf_sprite_animation_delay(sprite); - float t = sprite->t + sprite->animation->frames[sprite->frame_index].delay * sprite->frame_index; - return cf_clamp(t / delay, 0.0f, 1.0f); -} +CF_API float CF_CALL cf_sprite_animation_interpolant(CF_Sprite* sprite); /** * @function cf_sprite_will_finish @@ -788,17 +673,7 @@ CF_INLINE float cf_sprite_animation_interpolant(CF_Sprite* sprite) * @remarks This is useful to see if you're currently on the last frame of animation, and will finish in this particular update. * @related CF_Sprite cf_sprite_frame_count cf_sprite_current_frame cf_sprite_frame_delay cf_sprite_animation_delay cf_sprite_will_finish */ -CF_INLINE bool cf_sprite_will_finish(CF_Sprite* sprite) -{ - CF_ASSERT(sprite); - // TODO -- Backwards and pingpong. - if (!sprite->animation) return false; - if (sprite->frame_index == cf_sprite_frame_count(sprite) - 1) { - return sprite->t + CF_DELTA_TIME * sprite->play_speed_multiplier >= sprite->animation->frames[sprite->frame_index].delay; - } else { - return false; - } -} +CF_API bool CF_CALL cf_sprite_will_finish(CF_Sprite* sprite); /** * @function cf_sprite_on_loop @@ -819,16 +694,17 @@ CF_INLINE bool cf_sprite_on_loop(CF_Sprite* sprite) } /** - * @function cf_animation_add_frame + * @function cf_sprite_get_pixels * @category sprite - * @brief Adds a frame to an animation. - * @param animation The sprite. - * @param frame The frame. - * @remarks You can use this function to build your own animations in a custom manner. It's recommend to just use `cf_make_sprite`, which - * loads a full sprite out of a .ase file. But, this function provides another low-level option if desired. - * @related CF_Sprite CF_Animation CF_Frame dyna htbl + * @brief Returns pixel data for a specific frame of a named animation. + * @param sprite The sprite. + * @param blend_index Which blend to read (0 = default, 1+ from cf_sprite_add_blend). + * @param animation Name of the animation containing the frame. + * @param frame_index Zero-based index of the frame within the animation. + * @return A `CF_Image` with a copy of the frame's pixel data (premultiplied alpha). Returns a zero'd `CF_Image` if the animation or frame is not found. The caller must free the image with `cf_image_free`. + * @related CF_Sprite CF_Image CF_Frame cf_image_free */ -CF_INLINE void cf_animation_add_frame(CF_Animation* animation, CF_Frame frame) { CF_ASSERT(animation); apush(animation->frames, frame); } +CF_API CF_Image CF_CALL cf_sprite_get_pixels(CF_Sprite* sprite, int blend_index, const char* animation, int frame_index); #ifdef __cplusplus } @@ -880,7 +756,7 @@ CF_INLINE float sprite_animation_delay(CF_Sprite* sprite) { return cf_sprite_ani CF_INLINE float sprite_animation_interpolant(CF_Sprite* sprite) { return cf_sprite_animation_interpolant(sprite); } CF_INLINE bool sprite_will_finish(CF_Sprite* sprite) { return cf_sprite_will_finish(sprite); } CF_INLINE bool sprite_on_loop(CF_Sprite* sprite) { return cf_sprite_on_loop(sprite); } -CF_INLINE void animation_add_frame(CF_Animation* animation, CF_Frame frame) { cf_animation_add_frame(animation, frame); } +CF_INLINE CF_Image sprite_get_pixels(CF_Sprite* sprite, int blend_index, const char* animation, int frame_index) { return cf_sprite_get_pixels(sprite, blend_index, animation, frame_index); } CF_INLINE int sprite_width(CF_Sprite& sprite) { return cf_sprite_width(&sprite); } CF_INLINE int sprite_height(CF_Sprite& sprite) { return cf_sprite_height(&sprite); } @@ -920,15 +796,23 @@ CF_INLINE float sprite_animation_delay(CF_Sprite& sprite) { return cf_sprite_ani CF_INLINE float sprite_animation_interpolant(CF_Sprite& sprite) { return cf_sprite_animation_interpolant(&sprite); } CF_INLINE bool sprite_will_finish(CF_Sprite& sprite) { return cf_sprite_will_finish(&sprite); } CF_INLINE bool sprite_on_loop(CF_Sprite& sprite) { return cf_sprite_on_loop(&sprite); } -CF_INLINE void animation_add_frame(CF_Animation& animation, CF_Frame frame) { cf_animation_add_frame(&animation, frame); } +CF_INLINE CF_Image sprite_get_pixels(CF_Sprite& sprite, int blend_index, const char* animation, int frame_index) { return cf_sprite_get_pixels(&sprite, blend_index, animation, frame_index); } CF_INLINE CF_Sprite easy_make_sprite(const char* png_path, CF_Result* result) { return cf_make_easy_sprite_from_png(png_path, result); } CF_INLINE CF_Sprite easy_make_sprite(const CF_Pixel* pixels, int w, int h) { return cf_make_easy_sprite_from_pixels(pixels, w, h); } CF_INLINE CF_Sprite make_sprite(const char* aseprite_path) { return cf_make_sprite(aseprite_path); } CF_INLINE CF_Sprite make_demo_sprite() { return cf_make_demo_sprite(); } CF_INLINE void sprite_unload(const char* aseprite_path) { cf_sprite_unload(aseprite_path); } -CF_INLINE CF_Sprite sprite_reload(const CF_Sprite* sprite) { return cf_sprite_reload(sprite); } -CF_INLINE CF_Sprite sprite_reload(CF_Sprite& sprite) { return (sprite = cf_sprite_reload(&sprite)); } +CF_INLINE void sprite_reload(CF_Sprite* sprite) { cf_sprite_reload(sprite); } +CF_INLINE void sprite_reload(CF_Sprite& sprite) { cf_sprite_reload(&sprite); } +CF_INLINE int sprite_animation_count(const CF_Sprite* sprite) { return cf_sprite_animation_count(sprite); } +CF_INLINE const char* sprite_animation_name_at(const CF_Sprite* sprite, int index) { return cf_sprite_animation_name_at(sprite, index); } +CF_INLINE CF_V2 sprite_pivot(const CF_Sprite* sprite) { return cf_sprite_pivot(sprite); } +CF_INLINE void easy_sprite_update_pixels(CF_Sprite* sprite, const CF_Pixel* pixels) { cf_easy_sprite_update_pixels(sprite, pixels); } +CF_INLINE void easy_sprite_unload(CF_Sprite* sprite) { cf_easy_sprite_unload(sprite); } +CF_INLINE CF_Sprite make_sprite_from_memory(const char* unique_name, const void* aseprite_data, int size) { return cf_make_sprite_from_memory(unique_name, aseprite_data, size); } +CF_INLINE int sprite_add_blend(const char* path, const char** layer_names, int layer_count) { return cf_sprite_add_blend(path, layer_names, layer_count); } +CF_INLINE int sprite_blend_count(const CF_Sprite* sprite) { return cf_sprite_blend_count(sprite); } } diff --git a/include/cute_string.h b/include/cute_string.h index b79a3f3d6..6c2b8401d 100644 --- a/include/cute_string.h +++ b/include/cute_string.h @@ -11,13 +11,17 @@ #include "cute_defines.h" #include "cute_c_runtime.h" #include "cute_alloc.h" -#include "cute_hashtable.h" #include "cute_array.h" #include "cute_math.h" +#include + #include #include +// Shortform string macros (slen, sset, sfmt, sappend, sfree, etc.) are provided by ckit.h. +// You may use them directly if you wish, or use the cf_ longform prefixes below. + //-------------------------------------------------------------------------------------------------- // C API @@ -25,427 +29,654 @@ extern "C" { #endif // __cplusplus -// General purpose C-string API. 100% compatible with normal C-strings. -// Automatically grows on the heap as needed. -// Free it up with `sfree` when done. -// -// Example: -// -// sdyna char* s = NULL; -// sset(s, "Hello world!"); -// printf("%s", s); -// sfree(s); - /** * @function sdyna * @category string * @brief An empty macro used in the C API to markup dynamic strings. + * @example > Showcase of basic dynamic string features. + * // Create a new dynamic string. + * sdyna char* s = smake("Hello world!"); + * printf("%s\n", s); // Prints: Hello world! + * + * // Overwrite it with a new value. + * sset(s, "Goodbye!"); + * printf("%s\n", s); // Prints: Goodbye! + * + * // Append text to the string. + * sappend(s, " See you later."); + * printf("%s\n", s); // Prints: Goodbye! See you later. + * + * // Printf-style formatting. + * sfmt(s, "The answer is %d", 42); + * printf("%s\n", s); // Prints: The answer is 42 + * + * // String comparison. + * if (sequ(s, "The answer is 42")) { + * printf("Correct!\n"); + * } + * + * // Clean up. + * sfree(s); * @remarks This is an optional and _completely_ empty macro. It's only purpose is to provide a bit of visual indication a type is a - * dynamic string. - * @related sdyna sfmt sfmt_append svfmt svfmt_append sset sdup smake + * dynamic string. One downside of the C-macro API is the opaque nature of the pointer type. Since the macros use polymorphism + * on typed pointers (`char*`), there's no actual string struct type visible. `sdyna` helps visually indicate a pointer is a + * dynamic string, not just a plain `char*`. + * + * Dynamic strings are 100% compatible with normal C-strings -- pass them to `printf`, `strcmp`, etc. + * They automatically grow on the heap as needed. Free with `sfree` when done. + * + * To create a new string use `smake` or `sfmake`. To overwrite an existing string use `sset` or `sfmt`. + * Note: `sset`/`sfmt` require an l-value (a variable), not a literal like `NULL` -- use `smake`/`sfmake` to create from scratch. + * @related sdyna cf_string_make cf_string_fmt_make cf_string_set cf_string_fmt cf_string_append cf_string_free cf_string_len cf_string_equ + */ +#define sdyna CK_SDYNA + +//-------------------------------------------------------------------------------------------------- +// String Interning C API (global string table). + +/** + * @function cf_sintern + * @category string + * @brief Stores unique, static copy of a string in a global string interning table. + * @param s The string to insert into the global table. + * @return Returns a static, unique, stable, read-only copy of the string. The pointer is stable until `cf_sinuke` is called. + * @example > Using cf_sintern for fast string comparison and as hash table keys. + * // Intern some strings. + * const char* apple = cf_sintern("apple"); + * const char* banana = cf_sintern("banana"); + * const char* apple2 = cf_sintern("apple"); // Returns same pointer as first apple! + * + * // Fast pointer comparison (no strcmp needed). + * CF_ASSERT(apple == apple2); + * CF_ASSERT(apple != banana); + * + * // Use as hash table keys (cast pointer to uint64_t). + * CF_MAP(int) prices = NULL; + * map_set(prices, (uint64_t)apple, 100); + * map_set(prices, (uint64_t)banana, 50); + * int apple_price = map_get(prices, (uint64_t)cf_sintern("apple")); + * CF_ASSERT(apple_price == 100); + * map_free(prices); + * @remarks Only one copy of each unique string is stored. The purpose is primarily a memory optimization to reduce duplicate strings. + * You *can not* modify this string in any way. It is 100% immutable. Some major benefits come from placing strings into this + * table. + * + * - You can hash returned pointers directly into hash tables (instead of hashing the entire string). + * - You can simply compare pointers for equality, as opposed to comparing the string contents, as long as both strings came from this function. + * - You may optionally call `cf_sinuke` to free all resources used by the global string table. + * - This function is very fast if the string was already stored previously. + * @related cf_sintern cf_sintern_range cf_sivalid cf_silen cf_sinuke */ -#define sdyna +#define cf_sintern(s) sintern(s) /** - * @function slen + * @function cf_sintern_range * @category string - * @brief Returns the number of characters in the string, not counting the nul-terminator. - * @param s The string. Can be `NULL`. - * @related sdyna slen scount scap sempty + * @brief Stores unique, static copy of a string in a global string interning table. + * @param start A pointer to the start of the string to insert into the global table. + * @param end A pointer to the end of the string to insert into the global table. Should point just before the nul-byte (if there is a nul-byte). + * @return Returns a static, unique, stable, read-only copy of the string. The pointer is stable until `cf_sinuke` is called. + * @remarks Only one copy of each unique string is stored. The purpose is primarily a memory optimization to reduce duplicate strings. + * You *can not* modify this string in any way. It is 100% immutable. Some major benefits come from placing strings into this + * table. + * + * - You can hash returned pointers directly into hash tables (instead of hashing the entire string). + * - You can simply compare pointers for equality, as opposed to comparing the string contents, as long as both strings came from this function. + * - You may optionally call `cf_sinuke` to free all resources used by the global string table. + * - This function is very fast if the string was already stored previously. + * @related cf_sintern cf_sintern_range cf_sivalid cf_silen cf_sinuke */ -#define slen(s) cf_string_len(s) +#define cf_sintern_range(start, end) sintern_range(start, end) /** - * @function sempty + * @function cf_sivalid * @category string - * @brief Returns whether or not the string is empty. - * @param s The string. Can be `NULL`. - * @remarks Both "" and NULL count as empty. - * @related sdyna slen scount scap sempty + * @brief Returns true if the string is a static, stable, unique pointer from `cf_sintern`. + * @param s The string. + * @remarks This is *not* a secure method -- do not use it on any potentially dangerous strings. It's designed to be very simple and fast, nothing more. + * @related cf_sintern cf_sintern_range cf_sivalid cf_silen cf_sinuke */ -#define sempty(s) cf_string_empty(s) +#define cf_sivalid(s) sivalid(s) /** - * @function spush + * @function cf_silen * @category string - * @brief Pushes character `ch` onto the end of the string. - * @param s The string. Can be `NULL`. - * @param ch A character to push onto the end of the string. - * @remarks Does not overwite the nul-byte. If the string is empty a nul-byte is pushed afterwards. Can be NULL, will create a new string and assign `s` if so. - * @related sdyna spush spop sfit sfree sset + * @brief Returns the length of an intern'd string. + * @param s The string. + * @remarks This is *not* a secure method -- do not use it on any potentially dangerous strings. It's designed to be very simple and fast, nothing more. + * The return value is calculated in constant time, as opposed to calling `CF_STRLEN` (`strlen`). + * @related cf_sintern cf_sintern_range cf_sivalid cf_silen cf_sinuke + */ +#define cf_silen(s) silen(s) + +/** + * @function cf_sinuke + * @category string + * @brief Frees up all resources used by the global string table built by `cf_sintern`. + * @remarks All strings previously returned by `cf_sintern` are now invalid. + * @related cf_sintern cf_sintern_range cf_sivalid cf_silen cf_sinuke */ -#define spush(s, ch) cf_string_push(s, ch) +#define cf_sinuke() sinuke() + +//-------------------------------------------------------------------------------------------------- +// UTF8 and UTF16. /** - * @function sfree + * @function cf_string_append_UTF8 * @category string - * @brief Frees up all resources used by the string and sets it to `NULL`. + * @brief Appends a UTF32 codepoint (as `uint32_t`) encoded as UTF8 onto the string. * @param s The string. Can be `NULL`. - * @related sdyna spush spop sfit sfree sset + * @param codepoint An `int` codepoint in UTF32 form. + * @example > Appending UTF32 codepoints as UTF8 in a loop. + * sdyna char* s = NULL; + * while (has_codepoint()) { + * cf_string_append_UTF8(s, get_codepoint()); + * } + * sfree(s); + * @remarks The UTF8 bytes are appended onto the string. + * + * Each UTF32 codepoint represents a single character. Each character can be encoded from 1 to 4 + * bytes. Therefore, this function will push 1 to 4 bytes onto the string. + * + * If an invalid codepoint is found the "replacement character" 0xFFFD will be appended instead, which + * looks like a question mark inside a dark diamond. + * @related cf_string_append_UTF8 cf_string_decode_UTF8 cf_string_decode_UTF16 */ -#define sfree(s) cf_string_free(s) +#define cf_string_append_UTF8(s, codepoint) sappend_UTF8(s, codepoint) /** - * @function scount + * @function cf_string_decode_UTF8 * @category string - * @brief Returns the number of characters in the string, including the nul-terminator. + * @brief Decodes a single UTF8 character from the string as a UTF32 codepoint. * @param s The string. - * @related sdyna slen scount scap sempty + * @param codepoint An `int` pointer to receive the decoded codepoint. + * @return Returns pointer advanced past the decoded character (1 to 4 bytes). + * @example > Decoding a UTF8 string one codepoint at a time. + * int cp; + * const char* tmp = my_string; + * while (*tmp) { + * tmp = cf_string_decode_UTF8(tmp, &cp); + * DoSomethingWithCodepoint(cp); + * } + * @remarks You can use this function in a loop to decode one codepoint at a time, where each codepoint + * represents a single UTF8 character. If the decoded codepoint is invalid then the "replacement character" + * 0xFFFD will be recorded instead. + * @related cf_string_append_UTF8 cf_string_decode_UTF8 cf_string_decode_UTF16 */ -#define scount(s) cf_string_count(s) +#define cf_string_decode_UTF8(s, codepoint) decode_UTF8(s, codepoint) /** - * @function scap + * @function cf_string_decode_UTF16 * @category string - * @brief Gets the capacity of the string. - * @param s The string. Can be `NULL`. - * @remarks This is not the number of characters, but the size of the internal buffer. The capacity automatically grows as necessary, but - * you can use `sfit` to ensure a minimum capacity manually, as an optimization. - * @related sdyna slen scount scap sempty + * @brief Decodes a single UTF16 character from the string as a UTF32 codepoint. + * @param s The string. + * @param codepoint An `int` pointer to receive the decoded codepoint. + * @return Returns pointer advanced past the decoded character (1 to 2 uint16_t's). + * @example > Decoding a UTF16 string one codepoint at a time. + * int cp; + * const uint16_t* tmp = my_string; + * while (*tmp) { + * tmp = cf_string_decode_UTF16(tmp, &cp); + * DoSomethingWithCodepoint(cp); + * } + * @example > Converting a UTF16 string to UTF8. + * char* utf16_to_utf8(const uint16_t* text) + * { + * int cp; + * sdyna char* s = NULL; + * while (*text) { + * text = cf_string_decode_UTF16(text, &cp); + * cf_string_append_UTF8(s, cp); + * } + * return s; + * } + * @remarks You can use this function in a loop to decode one codepoint at a time, where each codepoint + * represents a single UTF16 character. You can convert a UTF16 string to UTF8 by calling `cf_string_append_UTF8` + * on another string instance inside a decode loop. + * @related cf_string_append_UTF8 cf_string_decode_UTF8 cf_string_decode_UTF16 */ -#define scap(s) cf_string_cap(s) +#define cf_string_decode_UTF16(s, codepoint) decode_UTF16(s, codepoint) + +//-------------------------------------------------------------------------------------------------- +// Longform C API. +// These map cf_string_* names to the shortform ckit macros. /** - * @function sfirst + * @function cf_string_len * @category string - * @brief Returns the first character in the string. - * @param s The string. Can be `NULL`. - * @return Returns '\0' if `s` is `NULL`. - * @related sdyna spush spop sfirst slast sclear + * @brief Returns the length of the string (not counting the nul-terminator), or 0 if NULL. + * @param s The string. + * @return Returns the length of `s`. + * @remarks Shortform: `slen(s)`. + * @related cf_string_size cf_string_count cf_string_empty cf_string_free */ -#define sfirst(s) cf_string_first(s) +#define cf_string_len(s) slen(s) /** - * @function slast + * @function cf_string_empty * @category string - * @brief Returns the last character in the string. Not the nul-byte. + * @brief Returns true if the string is NULL or has zero length. * @param s The string. Can be `NULL`. - * @return Returns '\0' if `s` is `NULL`. - * @related sdyna spush spop sfirst slast sclear + * @return Returns true if the string is empty. + * @remarks Shortform: `sempty(s)`. + * @related cf_string_len cf_string_free */ -#define slast(s) cf_string_last(s) +#define cf_string_empty(s) sempty(s) /** - * @function sclear + * @function cf_string_push * @category string - * @brief Sets the string size to zero. + * @brief Appends a single character to the end of the string. * @param s The string. Can be `NULL`. - * @remarks Does not free up any resources. - * @related sdyna spush spop sfirst slast sclear + * @param ch The character to append. + * @remarks Shortform: `spush(s, ch)`. + * @related cf_string_pop cf_string_append cf_string_set + */ +#define cf_string_push(s, ch) spush(s, ch) + +/** + * @function cf_string_free + * @category string + * @brief Frees the string and sets the pointer to NULL. + * @param s The string. Modified in-place. Safe to call on NULL. + * @remarks Shortform: `sfree(s)`. + * @related cf_string_set cf_string_clear cf_string_make */ -#define sclear(s) cf_string_clear(s) +#define cf_string_free(s) sfree(s) /** - * @function sfit + * @function cf_string_size * @category string - * @brief Ensures the capacity of the string is at least n+1 elements. + * @brief Returns the byte count including the nul-terminator, or 0 if NULL. * @param s The string. Can be `NULL`. - * @param n The number of elements for the new internal capacity. - * @remarks Does not change the size/count of the string, or the len. This function is just here for optimization purposes. - * @related sdyna sfit scap sclear + * @return Returns the byte count of `s` including the nul-terminator. + * @remarks Shortform: `scount(s)`. + * @related cf_string_len cf_string_count cf_string_cap */ -#define sfit(s, n) cf_string_fit(s, n) +#define cf_string_size(s) scount(s) /** - * @function sfmt + * @function cf_string_count * @category string - * @brief Printf's into the string using the format string `fmt`. + * @brief Returns the byte count including the nul-terminator, or 0 if NULL. Same as `cf_string_size`. * @param s The string. Can be `NULL`. - * @param fmt The format string. - * @param ... The parameters for the format string. - * @remarks The string will be overwritten from the beginning. Will automatically adjust capacity as needed. - * @related sdyna sfmt sfmt_append svfmt svfmt_append sset + * @return Returns the byte count of `s` including the nul-terminator. + * @remarks Shortform: `scount(s)`. + * @related cf_string_len cf_string_size cf_string_cap */ -#define sfmt(s, fmt, ...) cf_string_fmt(s, fmt, __VA_ARGS__) +#define cf_string_count(s) scount(s) /** - * @function sfmt_append + * @function cf_string_cap * @category string - * @brief Printf's into the *end* of the string, using the format string `fmt`. + * @brief Returns the capacity of the string's backing buffer, or 0 if NULL. * @param s The string. Can be `NULL`. - * @param fmt The format string. - * @param ... The parameters for the format string. - * @remarks All printed data is appended to the end of the string. Will automatically adjust it's capacity as needed. - * @related sdyna sfmt sfmt_append svfmt svfmt_append sset + * @return Returns the number of bytes allocated. + * @remarks Shortform: `scap(s)`. + * @related cf_string_len cf_string_fit */ -#define sfmt_append(s, fmt, ...) cf_string_fmt_append(s, fmt, __VA_ARGS__) +#define cf_string_cap(s) scap(s) /** - * @function svfmt + * @function cf_string_first * @category string - * @brief Printf's into the string using the format string `fmt`. + * @brief Returns the first character of the string, or `'\0'` if NULL/empty. * @param s The string. Can be `NULL`. - * @param fmt The format string. - * @param ... The parameters for the format string. - * @remarks You probably are looking for `sfmt` instead. The string will be overwritten from the beginning. Will automatically adjust it's - * capacity as needed. args must be a `va_list`. - * @related sdyna sfmt sfmt_append svfmt svfmt_append sset + * @return Returns the first character. + * @remarks Shortform: `sfirst(s)`. + * @related cf_string_last cf_string_len */ -#define svfmt(s, fmt, args) cf_string_vfmt(s, fmt, args) +#define cf_string_first(s) sfirst(s) /** - * @function svfmt_append + * @function cf_string_last * @category string - * @brief Printf's into the string using the format string `fmt`. + * @brief Returns the last character of the string, or `'\0'` if NULL/empty. * @param s The string. Can be `NULL`. - * @param fmt The format string. - * @param ... The parameters for the format string. - * @remarks You probably are looking for `sfmt_append` instead. The string will be overwritten from the beginning. Will automatically adjust it's - * capacity as needed. args must be a `va_list`. - * @related sdyna sfmt sfmt_append svfmt svfmt_append sset + * @return Returns the last character. + * @remarks Shortform: `slast(s)`. + * @related cf_string_first cf_string_len */ -#define svfmt_append(s, fmt, args) cf_string_vfmt_append(s, fmt, args) +#define cf_string_last(s) slast(s) /** - * @function sset + * @function cf_string_clear * @category string - * @brief Copies the string `b` into string `a`. - * @param a Destination for copying. Can be `NULL`. - * @param b Source for copying. - * @related sdyna sfmt sfmt_append svfmt svfmt_append sset sdup smake + * @brief Sets the string to empty without freeing memory. + * @param s The string. + * @remarks Shortform: `sclear(s)`. + * @related cf_string_free cf_string_len */ -#define sset(a, b) cf_string_set(a, b) +#define cf_string_clear(s) sclear(s) /** - * @function sdup + * @function cf_string_fit * @category string - * @brief Returns a completely new string copy. - * @param s The string to duplicate. - * @remarks You must free the copy with `sfree` when done. Does the same thing as `smake`. - * @related sdyna sset sdup smake + * @brief Ensures the string can hold at least `n` bytes, growing if necessary. + * @param s The string. Modified in-place. + * @param n The minimum number of bytes to reserve. + * @remarks Shortform: `sfit(s, n)`. + * @related cf_string_cap cf_string_len */ -#define sdup(s) cf_string_dup(s) +#define cf_string_fit(s, n) sfit(s, n) /** - * @function smake + * @function cf_string_fmt * @category string - * @brief Returns a completely new string copy. - * @param s The string to duplicate. - * @param b Source for copying. - * @remarks You must free the copy with `sfree` when done. Does the same thing as `sdup`. - * @related sdyna sset sdup smake + * @brief Sets the string to a printf-style formatted result. + * @param s The string. Modified in-place. + * @param fmt A printf-style format string. + * @param ... Format arguments. + * @remarks Shortform: `sfmt(s, fmt, ...)`. + * @related cf_string_fmt_append cf_string_vfmt cf_string_set */ -#define smake(s) cf_string_make(s) +#define cf_string_fmt(s, fmt, ...) sfmt(s, fmt, __VA_ARGS__) /** - * @function scmp + * @function cf_string_fmt_append * @category string - * @brief Compares two strings. - * @param a The first string. - * @param b The second string. - * @remarks Returns 0 if the two strings are equivalent. Otherwise returns 1 if a[i] > b[i], or -1 if a[i] < b[i]. - * @related sdyna scmp sicmp sequ siequ + * @brief Appends a printf-style formatted result to the string. + * @param s The string. Modified in-place. + * @param fmt A printf-style format string. + * @param ... Format arguments. + * @remarks Shortform: `sfmt_append(s, fmt, ...)`. + * @related cf_string_fmt cf_string_vfmt_append cf_string_append */ -#define scmp(a, b) cf_string_cmp(a, b) +#define cf_string_fmt_append(s, fmt, ...) sfmt_append(s, fmt, __VA_ARGS__) /** - * @function sicmp + * @function cf_string_vfmt * @category string - * @brief Compares two strings, ignoring case. - * @param a The first string. - * @param b The second string. - * @remarks Returns 0 if the two strings are equivalent. Otherwise returns 1 if a[i] > b[i], or -1 if a[i] < b[i]. - * @related sdyna scmp sicmp sequ siequ + * @brief Sets the string to a vprintf-style formatted result. + * @param s The string. Modified in-place. + * @param fmt A printf-style format string. + * @param args A `va_list` of format arguments. + * @remarks Shortform: `svfmt(s, fmt, args)`. + * @related cf_string_fmt cf_string_vfmt_append */ -#define sicmp(a, b) cf_string_icmp(a, b) +#define cf_string_vfmt(s, fmt, args) svfmt(s, fmt, args) /** - * @function sequ + * @function cf_string_vfmt_append * @category string - * @brief Returns true if the two strings are equivalent, false otherwise. - * @param a The first string. - * @param b The second string. - * @related sdyna scmp sicmp sequ siequ + * @brief Appends a vprintf-style formatted result to the string. + * @param s The string. Modified in-place. + * @param fmt A printf-style format string. + * @param args A `va_list` of format arguments. + * @remarks Shortform: `svfmt_append(s, fmt, args)`. + * @related cf_string_vfmt cf_string_fmt_append */ -#define sequ(a, b) cf_string_equ(a, b) +#define cf_string_vfmt_append(s, fmt, args) svfmt_append(s, fmt, args) /** - * @function siequ + * @function cf_string_set * @category string - * @brief Returns true if the two strings are equivalent, ignoring case, false otherwise. - * @param a The first string. - * @param b The second string. - * @related sdyna scmp sicmp sequ siequ + * @brief Sets the string to a copy of another string. + * @param a The destination string. Must be an l-value (a variable, not NULL). Modified in-place. + * @param b The source C string. + * @remarks Shortform: `sset(a, b)`. To create a new string from scratch use `smake` or `sfmake` instead. + * @related cf_string_dup cf_string_make cf_string_fmt_make cf_string_fmt cf_string_free */ -#define siequ(a, b) cf_string_iequ(a, b) +#define cf_string_set(a, b) sset(a, b) /** - * @function sprefix + * @function cf_string_dup * @category string - * @brief Check to see if the string's prefix matches. - * @param s The string. Can be `NULL`. - * @param prefix A string to compare against the beginning of `s`. - * @return Returns true if `prefix` is the prefix of `s`, false otherwise. - * @related sdyna sprefix ssuffix scontains sfirst_index_of slast_index_of sfind + * @brief Allocates and returns a new dynamic string copy. Free with `sfree`. + * @param s The source C string. + * @return Returns a new dynamic string. + * @remarks Shortform: `sdup(s)`. + * @related cf_string_make cf_string_set cf_string_free */ -#define sprefix(s, prefix) cf_string_prefix(s, prefix) +#define cf_string_dup(s) sdup(s) /** - * @function ssuffix + * @function cf_string_make * @category string - * @brief Check to see if the string's suffix matches. - * @param s The string. Can be `NULL`. - * @param prefix A string to compare against the end of `s`. - * @return Returns true if `suffix` is the suffix of `s`, false otherwise. - * @related sdyna sprefix ssuffix scontains sfirst_index_of slast_index_of sfind + * @brief Allocates and returns a new dynamic string copy. Same as `cf_string_dup`. Free with `sfree`. + * @param s The source C string. + * @return Returns a new dynamic string. + * @remarks Shortform: `smake(s)`. + * @related cf_string_dup cf_string_set cf_string_free */ -#define ssuffix(s, suffix) cf_string_suffix(s, suffix) +#define cf_string_make(s) smake(s) /** - * @function scontains + * @function cf_string_fmt_make * @category string - * @brief Returns true if s contains a certain substring. - * @param s The string. Can be `NULL`. - * @param contains_me A substring to search for. - * @related sdyna sprefix ssuffix scontains sfirst_index_of slast_index_of sfind + * @brief Allocates and returns a new formatted dynamic string. Free with `sfree`. + * @param fmt The printf-style format string. + * @return Returns a new dynamic string. + * @remarks Shortform: `sfmake(fmt, ...)`. Use this instead of `sfmt` when creating a string from scratch. + * @related cf_string_make cf_string_fmt cf_string_set cf_string_free */ -#define scontains(s, contains_me) cf_string_contains(s, contains_me) +#define cf_string_fmt_make(fmt, ...) sfmake(fmt, __VA_ARGS__) /** - * @function stoupper + * @function cf_string_cmp * @category string - * @brief Sets all characters in the string to upper case. - * @param s The string. Can be `NULL`. - * @related sdyna stoupper stolower siequ sicmp + * @brief Compares two strings lexicographically (case-sensitive). + * @param a First string. + * @param b Second string. + * @return Returns 0 if equal, negative if `a < b`, positive if `a > b`. + * @remarks Shortform: `scmp(a, b)`. + * @related cf_string_icmp cf_string_equ cf_string_iequ */ -#define stoupper(s) cf_string_toupper(s) +#define cf_string_cmp(a, b) scmp(a, b) /** - * @function stolower + * @function cf_string_icmp * @category string - * @brief Sets all characters in the string to lower case. - * @param s The string. Can be `NULL`. - * @related sdyna stoupper stolower siequ sicmp + * @brief Compares two strings lexicographically (case-insensitive). + * @param a First string. + * @param b Second string. + * @return Returns 0 if equal, negative if `a < b`, positive if `a > b`. + * @remarks Shortform: `sicmp(a, b)`. + * @related cf_string_cmp cf_string_equ cf_string_iequ */ -#define stolower(s) cf_string_tolower(s) +#define cf_string_icmp(a, b) sicmp(a, b) /** - * @function shash + * @function cf_string_equ * @category string - * @brief Returns a hash of the string as `uint64_t`. - * @param s The string. + * @brief Returns true if two strings are equal (case-sensitive). NULL-safe. + * @param a First string. Can be `NULL`. + * @param b Second string. Can be `NULL`. + * @return Returns true if the strings are equal. + * @remarks Shortform: `sequ(a, b)`. + * @related cf_string_iequ cf_string_cmp cf_string_icmp */ -#define shash(s) cf_string_hash(s) +#define cf_string_equ(a, b) sequ(a, b) /** - * @function sappend + * @function cf_string_iequ * @category string - * @brief Appends the string b onto the end of a. - * @param a The string to modify. Can be `NULL`. - * @param b Used to append onto `a`. - * @remarks You can technically do this with `sfmt`, but this function is optimized much faster. Does the same thing as `scat`. - * @related sdyna sappend scat sappend_range scat_range sfmt sfmt_append + * @brief Returns true if two strings are equal (case-insensitive). NULL-safe. + * @param a First string. Can be `NULL`. + * @param b Second string. Can be `NULL`. + * @return Returns true if the strings are equal (ignoring case). + * @remarks Shortform: `siequ(a, b)`. + * @related cf_string_equ cf_string_cmp cf_string_icmp */ -#define sappend(a, b) cf_string_append(a, b) +#define cf_string_iequ(a, b) siequ(a, b) /** - * @function scat + * @function cf_string_prefix * @category string - * @brief Appends the string b onto the end of a. - * @param a The string to modify. Can be `NULL`. - * @param b Used to append onto `a`. - * @remarks You can technically do this with `sfmt`, but this function is optimized much faster. Does the same thing as `sappend`. - * @related sdyna sappend scat sappend_range scat_range sfmt sfmt_append + * @brief Returns true if the string starts with the given prefix. + * @param s The string. + * @param prefix The prefix to test. + * @return Returns true if `s` starts with `prefix`. + * @remarks Shortform: `sprefix(s, prefix)`. + * @related cf_string_suffix cf_string_contains cf_string_find */ -#define scat(a, b) cf_string_append(a, b) +#define cf_string_prefix(s, prefix) sprefix(s, prefix) /** - * @function sappend_range + * @function cf_string_suffix * @category string - * @brief Appends a range of characters from string b onto the end of a. - * @param a The string to modify. Can be `NULL`. - * @param b Used to append onto `a`. - * @remarks You can technically do this with `sfmt`, but this function is optimized much faster. Does the same thing as `scat_range`. - * @related sdyna sappend scat sappend_range scat_range sfmt sfmt_append + * @brief Returns true if the string ends with the given suffix. + * @param s The string. + * @param suffix The suffix to test. + * @return Returns true if `s` ends with `suffix`. + * @remarks Shortform: `ssuffix(s, suffix)`. + * @related cf_string_prefix cf_string_contains cf_string_find */ -#define sappend_range(a, b, b_end) cf_string_append_range(a, b, b_end) +#define cf_string_suffix(s, suffix) ssuffix(s, suffix) /** - * @function scat_range + * @function cf_string_contains * @category string - * @brief Appends a range of characters from string b onto the end of a. - * @param a The string to modify. Can be `NULL`. - * @param b Used to append onto `a`. - * @remarks You can technically do this with `sfmt`, but this function is optimized much faster. Does the same thing as `sappend_range`. - * @related sdyna sappend scat sappend_range scat_range sfmt sfmt_append + * @brief Returns true if the string contains the given substring. + * @param s The string. + * @param contains_me The substring to search for. + * @return Returns true if `contains_me` is found within `s`. + * @remarks Shortform: `scontains(s, contains_me)`. + * @related cf_string_find cf_string_prefix cf_string_suffix */ -#define scat_range(a, b, b_end) cf_string_append_range(a, b, b_end) +#define cf_string_contains(s, contains_me) scontains(s, contains_me) /** - * @function strim + * @function cf_string_toupper * @category string - * @brief Removes all whitespace from the beginning and end of the string. + * @brief Converts the string to uppercase in-place. * @param s The string. - * @related sdyna strim sltrim srtrim slpad srpad sdedup sreplace serase + * @remarks Shortform: `stoupper(s)`. + * @related cf_string_tolower */ -#define strim(s) cf_string_trim(s) +#define cf_string_toupper(s) stoupper(s) /** - * @function sltrim + * @function cf_string_tolower * @category string - * @brief Removes all whitespace from the beginning of the string. + * @brief Converts the string to lowercase in-place. * @param s The string. - * @related sdyna strim sltrim srtrim slpad srpad sdedup sreplace serase + * @remarks Shortform: `stolower(s)`. + * @related cf_string_toupper */ -#define sltrim(s) cf_string_ltrim(s) +#define cf_string_tolower(s) stolower(s) /** - * @function srtrim + * @function cf_string_hash * @category string - * @brief Removes all whitespace from the end of the string. + * @brief Returns a FNV1a hash of the string. * @param s The string. - * @related sdyna strim sltrim srtrim slpad srpad sdedup sreplace serase + * @return Returns a `uint64_t` hash value. + * @remarks Shortform: `shash(s)`. + * @related cf_string_equ cf_string_cmp */ -#define srtrim(s) cf_string_rtrim(s) +#define cf_string_hash(s) shash(s) /** - * @function slpad + * @function cf_string_append * @category string - * @brief Places n characters `ch` onto the front of the string. - * @param s The string. Can be `NULL`. - * @param ch A character to insert. - * @param n Number of times to insert `ch`. - * @related sdyna strim sltrim srtrim slpad srpad sdedup sreplace serase + * @brief Appends string `b` onto the end of string `a`. + * @param a The destination string. Modified in-place. + * @param b The source C string to append. + * @remarks Shortform: `sappend(a, b)`. + * @related cf_string_append_range cf_string_fmt_append cf_string_set */ -#define slpad(s, ch, n) cf_string_lpad(s, ch, n) +#define cf_string_append(a, b) sappend(a, b) /** - * @function srpad + * @function cf_string_append_range * @category string - * @brief Appends n characters `ch` onto the end of the string. - * @param s The string. Can be `NULL`. - * @param ch A character to insert. - * @param n Number of times to insert `ch`. - * @related sdyna strim sltrim srtrim slpad srpad sdedup sreplace serase + * @brief Appends a range of characters `[b, b_end)` onto string `a`. + * @param a The destination string. Modified in-place. + * @param b Pointer to the start of the range. + * @param b_end Pointer one past the end of the range. + * @remarks Shortform: `sappend_range(a, b, b_end)`. + * @related cf_string_append cf_string_set */ -#define srpad(s, ch, n) cf_string_rpad(s, ch, n) +#define cf_string_append_range(a, b, b_end) sappend_range(a, b, b_end) /** - * @function ssplit_once + * @function cf_string_trim * @category string - * @brief Splits a string about the character `ch` one time, scanning from left-to-right. + * @brief Trims leading and trailing whitespace from the string in-place. + * @param s The string. Modified in-place. + * @remarks Shortform: `strim(s)`. + * @related cf_string_ltrim cf_string_rtrim + */ +#define cf_string_trim(s) strim(s) + +/** + * @function cf_string_ltrim + * @category string + * @brief Trims leading whitespace from the string in-place. + * @param s The string. Modified in-place. + * @remarks Shortform: `sltrim(s)`. + * @related cf_string_trim cf_string_rtrim + */ +#define cf_string_ltrim(s) sltrim(s) + +/** + * @function cf_string_rtrim + * @category string + * @brief Trims trailing whitespace from the string in-place. + * @param s The string. Modified in-place. + * @remarks Shortform: `srtrim(s)`. + * @related cf_string_trim cf_string_ltrim + */ +#define cf_string_rtrim(s) srtrim(s) + +/** + * @function cf_string_lpad + * @category string + * @brief Left-pads the string to at least `n` characters using `ch`. + * @param s The string. Modified in-place. + * @param ch The padding character. + * @param n The minimum total length after padding. + * @remarks Shortform: `slpad(s, ch, n)`. + * @related cf_string_rpad cf_string_trim + */ +#define cf_string_lpad(s, ch, n) slpad(s, ch, n) + +/** + * @function cf_string_rpad + * @category string + * @brief Right-pads the string to at least `n` characters using `ch`. + * @param s The string. Modified in-place. + * @param ch The padding character. + * @param n The minimum total length after padding. + * @remarks Shortform: `srpad(s, ch, n)`. + * @related cf_string_lpad cf_string_trim + */ +#define cf_string_rpad(s, ch, n) srpad(s, ch, n) + +/** + * @function cf_string_split_once + * @category string + * @brief Splits the string at the first occurrence of `ch`, returning the left half. * @param s The string. Must be a dynamically allocated string from this string API. Does *not* work on string literals. - * @param ch A character to split about. - * @return Returns the string to the left of `ch`. - * @remarks `s` will contain the string to the right of `ch`. - * Returns the string to the left of `ch`. - * If `ch` isn't found, simply returns `NULL` and does not modify `s`. + * @param ch The delimiter character. + * @return Returns the string to the left of `ch`. Returns `NULL` if `ch` isn't found (does not modify `s`). + * @example > Splitting a path into components using a loop. + * sdyna char* path = smake("/home/user/documents/file.txt"); + * sdyna char* part; + * while ((part = ssplit_once(path, '/'))) { + * printf("Part: %s\n", part); + * sfree(part); + * } + * printf("Remaining: %s\n", path); + * sfree(path); + * @remarks Shortform: `ssplit_once(s, ch)`. After the call, `s` contains the string to the right of `ch`. * You must call `sfree` on the returned string. - * + * * This function is intended to be used in a loop, successively chopping off pieces of `s`. * A much easier, but slightly slower, version of this function is `ssplit`, which returns - * an array of strings. - * @related sdyna ssplit_once ssplit + * an array of strings all at once. + * @related cf_string_split cf_string_find */ -#define ssplit_once(s, ch) cf_string_split_once(s, ch) +#define cf_string_split_once(s, ch) ssplit_once(s, ch) /** - * @function ssplit + * @function cf_string_split * @category string - * @brief Splits a string about the character `ch`, scanning from left-to-right. - * @param s The string. - * @param ch A character to split about. - * @return Returns a dynamic array of all delimited strings (see `dyna`). + * @brief Splits the string at every occurrence of `ch`, returning a dynamic array of strings. + * @param s The source C string (not modified). + * @param ch The delimiter character. + * @return Returns a `dyna char**` array of newly allocated strings. Free each string with `sfree`, then free the array with `afree`. * @example > Splitting a string about '.'. * sdyna char* s = NULL; * sset(s, "split.here.in.a.loop"); @@ -459,511 +690,397 @@ extern "C" { * dyna char** array_of_splits = ssplit(s, '.'); * for (int i = 0; i < alen(array_of_splits); ++i) { * const char* split = array_of_splits[i]; - * CF_TEST_ASSERT(sequ(split, splits_expected[i])); + * CF_ASSERT(sequ(split, splits_expected[i])); * sfree(split); * } * afree(array_of_splits); - * @remarks `s` is not modified. You must call `sfree` on the returned strings and `afree` on the returned array. - * @related sdyna ssplit_once ssplit + * sfree(s); + * @remarks Shortform: `ssplit(s, ch)`. The original string `s` is not modified. You must call `sfree` on each + * returned string and `afree` on the returned array. + * @related cf_string_split_once cf_string_find */ -#define ssplit(s, ch) cf_string_split(s, ch) +#define cf_string_split(s, ch) ssplit(s, ch) /** - * @function sfirst_index_of + * @function cf_string_first_index_of * @category string - * @brief Scanning from left-to-right, returns the first index of `ch` found. - * @param s The string. Can be `NULL`. - * @param ch A character to search for. - * @return Returns -1 if none are found. - * @related sdyna sfirst_index_of slast_index_of sfind + * @brief Returns the index of the first occurrence of `ch` in the string. + * @param s The string. + * @param ch The character to search for. + * @return Returns the index, or -1 if not found. + * @remarks Shortform: `sfirst_index_of(s, ch)`. + * @related cf_string_last_index_of cf_string_find cf_string_contains */ -#define sfirst_index_of(s, ch) cf_string_first_index_of(s, ch) +#define cf_string_first_index_of(s, ch) sfirst_index_of(s, ch) /** - * @function slast_index_of + * @function cf_string_last_index_of * @category string - * @brief Scanning from right-to-left, returns the first index of `ch` found. - * @param s The string. Can be `NULL`. - * @param ch A character to search for. - * @return Returns -1 if none are found. - * @related sdyna sfirst_index_of slast_index_of sfind + * @brief Returns the index of the last occurrence of `ch` in the string. + * @param s The string. + * @param ch The character to search for. + * @return Returns the index, or -1 if not found. + * @remarks Shortform: `slast_index_of(s, ch)`. + * @related cf_string_first_index_of cf_string_find cf_string_contains */ -#define slast_index_of(s, ch) cf_string_last_index_of(s, ch) +#define cf_string_last_index_of(s, ch) slast_index_of(s, ch) /** - * @function sfind + * @function cf_string_find * @category string - * @brief Scanning from left-to-right, returns a pointer to the substring `find`. - * @param s The string. - * @param find A substring to search for. - * @return Returns `NULL` if not found. - * @related sdyna sfirst_index_of slast_index_of sfind + * @brief Finds the first occurrence of a substring within the string. + * @param s The string to search in. + * @param find The substring to search for. + * @return Returns a pointer to the first match, or NULL if not found. + * @remarks Shortform: `sfind(s, find)`. + * @related cf_string_contains cf_string_first_index_of cf_string_replace */ -#define sfind(s, find) cf_string_find(s, find) +#define cf_string_find(s, find) sfind(s, find) /** - * @function sint + * @function cf_string_int * @category string - * @brief Converts an int64_t to a string and assigns `s` to it. - * @param s The string. - * @param i The value to convert. - * @related sdyna sint suint sfloat sdouble shex sbool stint stouint stofloat stodouble stohex stobool + * @brief Sets the string to the decimal representation of an integer. + * @param s The string. Modified in-place. + * @param i The integer value. + * @remarks Shortform: `sint(s, i)`. + * @related cf_string_uint cf_string_float cf_string_double cf_string_hex cf_string_bool cf_string_toint */ -#define sint(s, i) cf_string_int(s, i) +#define cf_string_int(s, i) sint(s, i) /** - * @function suint + * @function cf_string_uint * @category string - * @brief Converts a uint64_t to a string and assigns `s` to it. - * @param s The string. - * @param uint The value to convert. - * @related sdyna sint suint sfloat sdouble shex sbool stint stouint stofloat stodouble stohex stobool + * @brief Sets the string to the decimal representation of an unsigned integer. + * @param s The string. Modified in-place. + * @param uint The unsigned integer value. + * @remarks Shortform: `suint(s, uint)`. + * @related cf_string_int cf_string_float cf_string_double cf_string_hex cf_string_bool cf_string_touint */ -#define suint(s, uint) cf_string_uint(s, uint) +#define cf_string_uint(s, uint) suint(s, uint) /** - * @function sfloat + * @function cf_string_float * @category string - * @brief Converts a float to a string and assigns `s` to it. - * @param s The string. - * @param f The value to convert. - * @related sdyna sint suint sfloat sdouble shex sbool stint stouint stofloat stodouble stohex stobool + * @brief Sets the string to the decimal representation of a float. + * @param s The string. Modified in-place. + * @param f The float value. + * @remarks Shortform: `sfloat(s, f)`. + * @related cf_string_double cf_string_int cf_string_uint cf_string_tofloat */ -#define sfloat(s, f) cf_string_float(s, f) +#define cf_string_float(s, f) sfloat(s, f) /** - * @function sdouble + * @function cf_string_double * @category string - * @brief Converts a double to a string and assigns `s` to it. - * @param s The string. - * @param f The value to convert. - * @related sdyna sint suint sfloat sdouble shex sbool stint stouint stofloat stodouble stohex stobool + * @brief Sets the string to the decimal representation of a double. + * @param s The string. Modified in-place. + * @param f The double value. + * @remarks Shortform: `sdouble(s, f)`. + * @related cf_string_float cf_string_int cf_string_uint cf_string_todouble */ -#define sdouble(s, f) cf_string_double(s, f) +#define cf_string_double(s, f) sdouble(s, f) /** - * @function shex + * @function cf_string_hex * @category string - * @brief Converts a uint64_t to a hex-string and assigns `s` to it. - * @param s The string. - * @param uint The value to convert. - * @related sdyna sint suint sfloat sdouble shex sbool stint stouint stofloat stodouble stohex stobool + * @brief Sets the string to the hex representation of an unsigned integer. + * @param s The string. Modified in-place. + * @param uint The unsigned integer value. + * @remarks Shortform: `shex(s, uint)`. + * @related cf_string_int cf_string_uint cf_string_tohex */ -#define shex(s, uint) cf_string_hex(s, uint) +#define cf_string_hex(s, uint) sfmt(s, "0x%x", uint) /** - * @function sbool + * @function cf_string_ptr * @category string - * @brief Converts a bool to a string and assigns `s` to it. - * @param s The string. - * @param uint The value to convert. - * @related sdyna sint suint sfloat sdouble shex sbool stint stouint stofloat stodouble stohex stobool + * @brief Sets the string to the platform pointer representation of a pointer. + * @param s The string. Modified in-place. + * @param p The pointer value. + * @remarks Shortform: `sptr(s, p)`. + * @related cf_string_hex cf_string_int cf_string_uint + */ +#define cf_string_ptr(s, p) sptr(s, p) + +/** + * @function cf_string_bool + * @category string + * @brief Sets the string to `"true"` or `"false"`. + * @param s The string. Modified in-place. + * @param b The boolean value. + * @remarks Shortform: `sbool(s, b)`. + * @related cf_string_int cf_string_tobool */ -#define sbool(s, b) cf_string_bool(s, b) +#define cf_string_bool(s, b) sbool(s, b) /** - * @function stoint + * @function cf_string_toint * @category string - * @brief Converts a string to an int64_t and returns it. + * @brief Parses the string as a decimal integer. * @param s The string. - * @related sdyna sint suint sfloat sdouble shex sbool stint stouint stofloat stodouble stohex stobool + * @return Returns the parsed `int` value. + * @remarks Shortform: `stoint(s)`. + * @related cf_string_int cf_string_touint cf_string_tofloat cf_string_todouble cf_string_tohex cf_string_tobool */ -#define stoint(s) cf_string_toint(s) +#define cf_string_toint(s) stoint(s) /** - * @function stouint + * @function cf_string_touint * @category string - * @brief Converts a string to an uint64_t and returns it. + * @brief Parses the string as an unsigned integer. * @param s The string. - * @related sdyna sint suint sfloat sdouble shex sbool stint stouint stofloat stodouble stohex stobool + * @return Returns the parsed `uint64_t` value. + * @remarks Shortform: `stouint(s)`. + * @related cf_string_uint cf_string_toint cf_string_tofloat cf_string_todouble cf_string_tohex cf_string_tobool */ -#define stouint(s) cf_string_touint(s) +#define cf_string_touint(s) stouint(s) /** - * @function stofloat + * @function cf_string_tofloat * @category string - * @brief Converts a string to a float and returns it. + * @brief Parses the string as a float. * @param s The string. - * @related sdyna sint suint sfloat sdouble shex sbool stint stouint stofloat stodouble stohex stobool + * @return Returns the parsed `float` value. + * @remarks Shortform: `stofloat(s)`. + * @related cf_string_float cf_string_toint cf_string_todouble */ -#define stofloat(s) cf_string_tofloat(s) +#define cf_string_tofloat(s) stofloat(s) /** - * @function stodouble + * @function cf_string_todouble * @category string - * @brief Converts a string to a double and returns it. + * @brief Parses the string as a double. * @param s The string. - * @related sdyna sint suint sfloat sdouble shex sbool stint stouint stofloat stodouble stohex stobool + * @return Returns the parsed `double` value. + * @remarks Shortform: `stodouble(s)`. + * @related cf_string_double cf_string_toint cf_string_tofloat */ -#define stodouble(s) cf_string_todouble(s) +#define cf_string_todouble(s) stodouble(s) /** - * @function stohex + * @function cf_string_tohex * @category string - * @brief Converts a hex-string to a uint64_t and returns it. + * @brief Parses the string as a hexadecimal unsigned integer. * @param s The string. - * @remarks Supports srings that start with "0x", "#", or no prefix. - * @related sdyna sint suint sfloat sdouble shex sbool stint stouint stofloat stodouble stohex stobool + * @return Returns the parsed `uint64_t` value. + * @remarks Shortform: `stohex(s)`. + * @related cf_string_hex cf_string_toint cf_string_touint */ -#define stohex(s) cf_string_tohex(s) +#define cf_string_tohex(s) stohex(s) /** - * @function stobool + * @function cf_string_tobool * @category string - * @brief Converts a string to a bool and returns it. + * @brief Parses the string as a boolean. Returns true if the string is `"true"`. * @param s The string. - * @remarks Supports srings that start with "0x", "#", or no prefix. - * @related sdyna sint suint sfloat sdouble shex sbool stint stouint stofloat stodouble stohex stobool + * @return Returns the parsed `bool` value. + * @remarks Shortform: `stobool(s)`. + * @related cf_string_bool cf_string_toint */ -#define stobool(s) cf_string_tobool(s) +#define cf_string_tobool(s) stobool(s) /** - * @function sreplace + * @function cf_string_replace * @category string - * @brief Replaces all substrings `replace_me` with the substring `with_me`. - * @param s The string. Can be `NULL`. - * @param replace_me Substring to replace. - * @param with_me The replacement string. - * @remarks Supports srings that start with "0x", "#", or no prefix. - * @related sdyna strim sltrim srtrim slpad srpad sdedup sreplace serase + * @brief Replaces all occurrences of `replace_me` with `with_me` in the string. + * @param s The string. Modified in-place. + * @param replace_me The substring to find. + * @param with_me The replacement substring. + * @remarks Shortform: `sreplace(s, replace_me, with_me)`. + * @related cf_string_find cf_string_erase cf_string_dedup */ -#define sreplace(s, replace_me, with_me) cf_string_replace(s, replace_me, with_me) +#define cf_string_replace(s, replace_me, with_me) sreplace(s, replace_me, with_me) /** - * @function sdedup + * @function cf_string_dedup * @category string - * @brief Removes all consecutive occurances of `ch` from the string. - * @param s The string. Can be `NULL`. - * @param ch A character. - * @related sdyna strim sltrim srtrim slpad srpad sdedup sreplace serase + * @brief Collapses consecutive runs of `ch` down to a single occurrence. + * @param s The string. Modified in-place. + * @param ch The character to deduplicate. + * @remarks Shortform: `sdedup(s, ch)`. + * @related cf_string_replace cf_string_trim */ -#define sdedup(s, ch) cf_string_dedup(s, ch) +#define cf_string_dedup(s, ch) sdedup(s, ch) /** - * @function serase + * @function cf_string_erase * @category string - * @brief Deletes a number of characters from the string. - * @param s The string. Can be `NULL`. - * @param index Index in the string to start deleting from. - * @param count Number of character to delete. - * @related sdyna strim sltrim srtrim slpad srpad sdedup sreplace serase + * @brief Erases `count` characters starting at `index` from the string. + * @param s The string. Modified in-place. + * @param index The starting index. + * @param count The number of characters to erase. + * @remarks Shortform: `serase(s, index, count)`. + * @related cf_string_replace cf_string_pop cf_string_clear */ -#define serase(s, index, count) cf_string_erase(s, index, count) +#define cf_string_erase(s, index, count) serase(s, index, count) /** - * @function spop + * @function cf_string_pop * @category string - * @brief Removes a character from the end of the string. - * @param s The string. Can be `NULL`. - * @related sdyna spop spopn serase slast + * @brief Removes the last character from the string. + * @param s The string. Must be non-empty. + * @remarks Shortform: `spop(s)`. + * @related cf_string_pop_n cf_string_push cf_string_erase */ -#define spop(s) (s = cf_string_pop(s)) +#define cf_string_pop(s) spop(s) /** - * @function spopn + * @function cf_string_pop_n * @category string - * @brief Removes n characters from the back of a string. - * @param s The string. Can be `NULL`. - * @param n Number of characters to pop. - * @related sdyna spop spopn serase slast + * @brief Removes the last `n` characters from the string. + * @param s The string. + * @param n The number of characters to remove. + * @remarks Shortform: `spopn(s, n)`. + * @related cf_string_pop cf_string_erase */ -#define spopn(s, n) (s = cf_string_pop_n(s, n)) +#define cf_string_pop_n(s, n) spopn(s, n) /** - * @function sstatic + * @function cf_string_static * @category string * @brief Creates a string with an initial static storage backing. * @param s The string. Can be `NULL`. * @param buffer Pointer to a static memory buffer. * @param buffer_size The size of `buffer` in bytes. - * @remarks Will grow onto the heap if the size becomes too large. Call `sfree` when done. - * @related sdyna sstatic sisdyna spush sset + * @remarks Shortform: `sstatic(s, buffer, buffer_size)`. Will grow onto the heap if the size becomes too large. Call `sfree` when done. + * @related cf_string_is_dynamic cf_string_free cf_string_set */ -#define sstatic(s, buffer, buffer_size) cf_string_static(s, buffer, buffer_size) +#define cf_string_static(s, buffer, buffer_size) sstatic(s, buffer, buffer_size) /** - * @function sisdyna + * @function cf_string_is_dynamic * @category string - * @brief Checks to see if a C string is a dynamic string from Cute Framework's string API, or not. + * @brief Checks if a C string is a dynamic string from this API. * @param s The string. Can be `NULL`. - * @return Returns true if `s` is a dynamically alloced string from this C string API. - * @remarks This can be evaluated at compile time for string literals. - * @related sdyna sstatic sisdyna spush sset + * @return Returns true if `s` is a dynamically allocated string. + * @remarks Shortform: `sisdyna(s)`. + * @related cf_string_static cf_string_free */ -#define sisdyna(s) cf_string_is_dynamic(s) +#define cf_string_is_dynamic(s) sisdyna(s) //-------------------------------------------------------------------------------------------------- -// UTF8 and UTF16. +// Longform Path C API. +// These map cf_path_* names to the shortform ckit path macros. /** - * @function sappend_UTF8 - * @category string - * @brief Appends a UTF32 codepoint (as `uint32_t`) encoded as UTF8 onto the string. - * @param s The string. Can be `NULL`. - * @param codepoint An `int` codepoint in UTF32 form. - * @example > Example of suggested way to use this function within a loop. - * sdyna char* s = NULL; - * while (has_codepoint()) { - * sappend_UTF8(s, get_codepoint()); - * } - * sfree(s); - * @remarks The UTF8 bytes are appended onto the string. - * - * Each UTF32 codepoint represents a single character. Each character can be encoded from 1 to 4 - * bytes. Therefor, this function will push 1 to 4 bytes onto the string. - * - * If an invalid codepoint is found the "replacement character" 0xFFFD will be appended instead, which - * looks like question mark inside of a dark diamond. - * @related sappend_UTF8 cf_decode_UTF8 cf_decode_UTF16 + * @function cf_path_get_filename + * @category path + * @brief Returns the filename portion of a path. Returns a new string. + * @param path The path string. + * @return Returns a new dynamic string. Free with `sfree`. + * @example > Example fetching a filename from a path. + * const char* filename = spfname("/data/collections/rare/big_gem.txt"); + * printf("%s\n", filename); + * // Prints: big_gem.txt + * @remarks Shortform: `spfname(path)`. + * @related cf_path_get_filename_no_ext cf_path_get_ext cf_path_directory_of cf_path_normalize */ -#define sappend_UTF8(s, codepoint) cf_string_append_UTF8(s, codepoint) +#define cf_path_get_filename(path) spfname(path) /** - * @function cf_decode_UTF8 - * @category string - * @brief Decodes a single UTF8 character from the string as a UTF32 codepoint. - * @param s The string. Can be `NULL`. - * @param codepoint An `int` codepoint in UTF32 form. - * @return The return value is not a new string, but just s + bytes, where bytes is anywhere from 1 to 4. - * @example > Decoding a UTF8 string one codepoint at a time. - * int cp; - * const char* tmp = my_string; - * while (*tmp) { - * tmp = cf_decode_UTF8(tmp, &cp); - * DoSomethingWithCodepoint(cp); - * } - * @remarks You can use this function in a loop to decode one codepoint at a time, where each codepoint - * represents a single UTF8 character. If the decoded codepoint is invalid then the "replacement character" - * 0xFFFD will be recorded instead. - * @related sappend_UTF8 cf_decode_UTF8 cf_decode_UTF16 + * @function cf_path_get_filename_no_ext + * @category path + * @brief Returns the filename portion of a path without the file extension. Returns a new string. + * @param path The path string. + * @return Returns a new dynamic string. Free with `sfree`. + * @remarks Shortform: `spfname_no_ext(path)`. + * @related cf_path_get_filename cf_path_get_ext cf_path_ext_equ */ -CF_API const char* CF_CALL cf_decode_UTF8(const char* s, int* codepoint); +#define cf_path_get_filename_no_ext(path) spfname_no_ext(path) /** - * @function cf_decode_UTF16 - * @category string - * @brief Decodes a single UTF16 character from the string as a UTF32 codepoint. - * @param s The string. Can be `NULL`. - * @param codepoint An `int` codepoint in UTF32 form. - * @return The return value is not a new string, but just s + count, where count is anywhere from 1 to 2. - * @remarks You can use this function in a loop to decode one codepoint at a time, where each codepoint - * represents a single UTF8 character. - * - * ```cpp - * int cp; - * const uint16_t* tmp = my_string; - * while (tmp) { - * tmp = cf_decode_UTF16(tmp, &cp); - * DoSomethingWithCodepoint(cp); - * } - * ``` - * - * You can convert a UTF16 string to UTF8 by calling `sappend_UTF8` on another string - * instance inside the above example loop. Here's an example function to return a new string - * instance in UTF8 form given a UTF16 string. - * - * ```cpp - * char* utf8(uint16_t* text) - * { - * int cp; - * sdyna char* s = NULL; - * while (*text) { - * text = cf_decode_UTF16(text, &cp); - * s = sappend_UTF8(s, cp); - * } - * return s; - * } - * ``` - * @related sappend_UTF8 cf_decode_UTF8 cf_decode_UTF16 - */ -CF_API const uint16_t* CF_CALL cf_decode_UTF16(const uint16_t* s, int* codepoint); - -//-------------------------------------------------------------------------------------------------- -// String Intering C API (global string table). -// ^ ^ + * @function cf_path_get_ext + * @category path + * @brief Returns the extension of the file for the given path (including the dot). Returns a new string. + * @param path The path string. + * @return Returns a new dynamic string. Free with `sfree`. + * @remarks Shortform: `spext(path)`. + * @related cf_path_ext_equ cf_path_get_filename cf_path_get_filename_no_ext + */ +#define cf_path_get_ext(path) spext(path) /** - * @function sintern - * @category string - * @brief Stores unique, static copy of a string in a global string interning table. - * @param s The string to insert into the global table. - * @return Returns a static, unique, stable, read-only copy of the string. The pointer is stable until `sinuke` is called. - * @remarks Only one copy of each unique string is stored. The purpose is primarily a memory optimization to reduce duplicate strings. - * You *can not* modify this string in any way. It is 100% immutable. Some major benefits come from placing strings into this - * table. - * - * - You can hash returned pointers directly into hash tables (instead of hashing the entire string). - * - You can simply compare pointers for equality, as opposed to comparing the string contents, as long as both strings came from this function. - * - You may optionally call `sinuke` to free all resources used by the global string table. - * - This function is very fast if the string was already stored previously. - * @related sintern sintern_range sivalid silen sinuke + * @function cf_path_ext_equ + * @category path + * @brief Returns true if the file's extension matches, false otherwise. + * @param path The path string. + * @param ext The file extension to compare (e.g. `".png"`). + * @return Returns true if the extensions match. + * @remarks Shortform: `spext_equ(path, ext)`. + * @related cf_path_get_ext cf_path_get_filename */ -#define sintern(s) cf_sintern(s) +#define cf_path_ext_equ(path, ext) spext_equ(path, ext) /** - * @function sintern_range - * @category string - * @brief Stores unique, static copy of a string in a global string interning table. - * @param start A pointer to the start of the string to insert into the global table. - * @param end A pointer to the end of the string to insert into the global table. Should point just before the nul-byte (if there is a nul-byte). - * @return Returns a static, unique, stable, read-only copy of the string. The pointer is stable until `sinuke` is called. - * @remarks Only one copy of each unique string is stored. The purpose is primarily a memory optimization to reduce duplicate strings. - * You *can not* modify this string in any way. It is 100% immutable. Some major benefits come from placing strings into this - * table. - * - * - You can hash returned pointers directly into hash tables (instead of hashing the entire string). - * - You can simply compare pointers for equality, as opposed to comparing the string contents, as long as both strings came from this function. - * - You may optionally call `sinuke` to free all resources used by the global string table. - * - This function is very fast if the string was already stored previously. - * @related sintern sintern_range sivalid silen sinuke + * @function cf_path_pop + * @category path + * @brief Removes the rightmost file or directory from the path. Returns a new string. + * @param path The path string. + * @return Returns a new dynamic string. Free with `sfree`. + * @remarks Shortform: `sppop(path)`. + * @related cf_path_pop_n cf_path_directory_of cf_path_top_directory cf_path_normalize */ -#define sintern_range(start, end) cf_sintern_range(start, end) +#define cf_path_pop(path) sppop(path) /** - * @function sivalid - * @category string - * @brief Returns true if the string is a static, stable, unique pointer from `sintern`. - * @param s The string. - * @remarks This is *not* a secure method -- do not use it on any potentially dangerous strings. It's designed to be very simple and fast, nothing more. - * @related sintern sintern_range sivalid silen sinuke + * @function cf_path_pop_n + * @category path + * @brief Removes the rightmost `n` files or directories from the path. Returns a new string. + * @param path The path string. + * @param n The number of path components to pop. + * @return Returns a new dynamic string. Free with `sfree`. + * @remarks Shortform: `sppopn(path, n)`. + * @related cf_path_pop cf_path_directory_of */ -#define sivalid(s) (((cf_intern_t*)s - 1)->cookie == CF_INTERN_COOKIE) +#define cf_path_pop_n(path, n) sppopn(path, n) /** - * @function silen - * @category string - * @brief Returns the length of an intern'd string. - * @param s The string. - * @remarks This is *not* a secure method -- do not use it on any potentially dangerous strings. It's designed to be very simple and fast, nothing more. - * The return value is calculated in constant time, as opposed to calling `CF_STRLEN` (`strlen`). - * @related sintern sintern_range sivalid silen sinuke + * @function cf_path_compact + * @category path + * @brief Squishes the path to be less than or equal to `n` characters in length. Returns a new string. + * @param path The path string. + * @param n Max character count (including nul-byte). + * @return Returns a new dynamic string. Free with `sfree`. + * @remarks Shortform: `spcompact(path, n)`. Inserts ellipses "..." as necessary. Useful for displaying paths in small UI boxes. + * @related cf_path_pop cf_path_pop_n */ -#define silen(s) (((cf_intern_t*)s - 1)->len) +#define cf_path_compact(path, n) spcompact(path, n) /** - * @function sinuke - * @category string - * @brief Frees up all resources used by the global string table built by `sintern`. - * @remarks All strings previously returned by `sintern` are now invalid. - * @related sintern sintern_range sivalid silen sinuke + * @function cf_path_directory_of + * @category path + * @brief Returns the directory of a given file or directory. Returns a new string. + * @param path The path string. + * @return Returns a new dynamic string. Free with `sfree`. + * @remarks Shortform: `spdir_of(path)`. + * @related cf_path_get_filename cf_path_top_directory cf_path_pop cf_path_normalize */ -#define sinuke() cf_sinuke() - -//-------------------------------------------------------------------------------------------------- -// Longform C API. +#define cf_path_directory_of(path) spdir_of(path) -#define cf_string_len(s) (s ? (cf_array_count(s) ? cf_array_count(s)-1 : cf_array_count(s)) : 0) -#define cf_string_empty(s) (s ? cf_string_len(s) < 1 : 1) -#define cf_string_push(s, ch) do { if (!s) cf_array_push(s, ch); else s[cf_string_len(s)] = ch; cf_array_push(s, 0); } while (0) -#define cf_string_free(s) cf_array_free(s) -#define cf_string_size(s) cf_array_len(s) -#define cf_string_count(s) cf_array_count(s) -#define cf_string_cap(s) cf_array_capacity(s) -#define cf_string_first(s) (s ? s[0] : '\0') -#define cf_string_last(s) (s ? s[cf_string_len(s) - 1] : '\0') -#define cf_string_clear(s) (cf_array_clear(s), cf_array_push(s, 0)) -#define cf_string_fit(s, n) (s = cf_sfit(s, n)) -#define cf_string_fmt(s, fmt, ...) (s = cf_sfmt(s, fmt, __VA_ARGS__)) -#define cf_string_fmt_append(s, fmt, ...) (s = cf_sfmt_append(s, fmt, __VA_ARGS__)) -#define cf_string_vfmt(s, fmt, args) (s = cf_svfmt(s, fmt, args)) -#define cf_string_vfmt_append(s, fmt, args) (s = cf_svfmt_append(s, fmt, args)) -#define cf_string_set(a, b) (a = cf_sset(a, b)) -#define cf_string_set(a, b) (a = cf_sset(a, b)) -#define cf_string_dup(s) cf_sset(NULL, s) -#define cf_string_make(s) cf_sset(NULL, s) -#define cf_string_cmp(a, b) CF_STRCMP(a, b) -#define cf_string_icmp(a, b) CF_STRICMP(a, b) -#define cf_string_equ(a, b) ((a) == NULL && (b) == NULL ? true : ((a) == NULL || (b) == NULL ? false : !CF_STRCMP((a), (b)))) -#define cf_string_iequ(a, b) ((a) == NULL && (b) == NULL ? true : ((a) == NULL || (b) == NULL ? false : !CF_STRICMP((a), (b)))) -#define cf_string_prefix(s, prefix) cf_sprefix(s, prefix) -#define cf_string_suffix(s, suffix) cf_ssuffix(s, suffix) -#define cf_string_contains(s, contains_me) (cf_string_len(s) >= CF_STRLEN(contains_me) && !!CF_STRSTR(s, contains_me)) -#define cf_string_toupper(s) cf_stoupper(s) -#define cf_string_tolower(s) cf_stolower(s) -#define cf_string_hash(s) ahash(s) -#define cf_string_append(a, b) (a = cf_sappend(a, b)) -#define cf_string_append_range(a, b, b_end) (a = cf_sappend_range(a, b, b_end)) -#define cf_string_trim(s) (s = cf_strim(s)) -#define cf_string_ltrim(s) (s = cf_sltrim(s)) -#define cf_string_rtrim(s) (s = cf_srtrim(s)) -#define cf_string_lpad(s, ch, n) (s = cf_slpad(s, ch, n)) -#define cf_string_rpad(s, ch, n) (s = cf_srpad(s, ch, n)) -#define cf_string_split_once(s, ch) cf_ssplit_once(s, ch) -#define cf_string_split(s, ch) cf_ssplit(s, ch) -#define cf_string_first_index_of(s, ch) cf_sfirst_index_of(s, ch) -#define cf_string_last_index_of(s, ch) cf_slast_index_of(s, ch) -#define cf_string_find(s, find) CF_STRSTR(s, find) -#define cf_string_int(s, i) cf_string_fmt(s, "%d", i) -#define cf_string_uint(s, uint) cf_string_fmt(s, "%" PRIu64, uint) -#define cf_string_float(s, f) cf_string_fmt(s, "%f", f) -#define cf_string_double(s, f) cf_string_fmt(s, "%f", d) -#define cf_string_hex(s, uint) cf_string_fmt(s, "0x%x", uint) -#define cf_string_bool(s, b) cf_string_fmt(s, "%s", b ? "true" : "false") -#define cf_string_toint(s) cf_stoint(s) -#define cf_string_touint(s) cf_stouint(s) -#define cf_string_tofloat(s) cf_stofloat(s) -#define cf_string_todouble(s) cf_stodouble(s) -#define cf_string_tohex(s) cf_stohex(s) -#define cf_string_tobool(s) (!CF_STRCMP(s, "true")) -#define cf_string_replace(s, replace_me, with_me) (s = cf_sreplace(s, replace_me, with_me)) -#define cf_string_dedup(s, ch) (s = cf_sdedup(s, ch)) -#define cf_string_erase(s, index, count) (s = cf_serase(s, index, count)) -#define cf_string_pop(s) (s = cf_spop(s)) -#define cf_string_pop_n(s, n) (s = cf_spopn(s, n)) -#define cf_string_static(s, buffer, buffer_size) (cf_array_static(s, buffer, buffer_size), cf_array_push(s, 0)) -#define cf_string_is_dynamic(s) (s && !((#s)[0] == '"') && CF_AHDR(s)->cookie == CF_ACOOKIE) -#define cf_sinuke() cf_sinuke_intern_table() -#define cf_string_append_UTF8(s, codepoint) (s = cf_string_append_UTF8_impl(s, codepoint)) - -//-------------------------------------------------------------------------------------------------- -// Hidden API - Not intended for direct use. +/** + * @function cf_path_top_directory + * @category path + * @brief Returns the top-level directory of a given file or directory. Returns a new string. + * @param path The path string. + * @return Returns a new dynamic string. Free with `sfree`. + * @remarks Shortform: `sptop_of(path)`. + * @related cf_path_directory_of cf_path_get_filename cf_path_pop + */ +#define cf_path_top_directory(path) sptop_of(path) -#define CF_INTERN_COOKIE 0x75AFC82E // Used for sanity/type checking. +/** + * @function cf_path_normalize + * @category path + * @brief Normalizes a path as a new string. + * @param path The path string. + * @return Returns a new dynamic string. Free with `sfree`. + * @remarks Shortform: `spnorm(path)`. All '\\' are replaced with '/'. Duplicate '////' become single '/'. Trailing '/' removed. + * Dot folders resolved. The first character is always '/', unless a windows drive (e.g. "C:/Users/Randy/Documents"). + * @related cf_path_get_filename cf_path_directory_of cf_path_pop + */ +#define cf_path_normalize(path) spnorm(path) -typedef struct cf_intern_t -{ - uint32_t cookie; // Type check. - int len; - struct cf_intern_t* next; - const char* string; // For debugging convenience but allocated after this struct. -} cf_intern_t; - -CF_API char* CF_CALL cf_sfit(char* a, int n); -CF_API char* CF_CALL cf_sset(char* a, const char* b); -CF_API char* CF_CALL cf_sfmt(char* s, const char* fmt, ...); -CF_API char* CF_CALL cf_sfmt_append(char* s, const char* fmt, ...); -CF_API char* CF_CALL cf_svfmt(char* s, const char* fmt, va_list args); -CF_API char* CF_CALL cf_svfmt_append(char* s, const char* fmt, va_list args); -CF_API bool CF_CALL cf_sprefix(char* s, const char* prefix); -CF_API bool CF_CALL cf_ssuffix(char* s, const char* suffix); -CF_API void CF_CALL cf_stoupper(char* s); -CF_API void CF_CALL cf_stolower(char* s); -CF_API char* CF_CALL cf_sappend(char* a, const char* b); -CF_API char* CF_CALL cf_sappend_range(char* a, const char* b, const char* b_end); -CF_API char* CF_CALL cf_strim(char* s); -CF_API char* CF_CALL cf_sltrim(char* s); -CF_API char* CF_CALL cf_srtrim(char* s); -CF_API char* CF_CALL cf_slpad(char* s, char pad, int count); -CF_API char* CF_CALL cf_srpad(char* s, char pad, int count); -CF_API char* CF_CALL cf_ssplit_once(char* s, char split_c); -CF_API char** CF_CALL cf_ssplit(const char* s, char split_c); -CF_API int CF_CALL cf_sfirst_index_of(const char* s, char c); -CF_API int CF_CALL cf_slast_index_of(const char* s, char c); -CF_API int CF_CALL cf_stoint(const char* s); -CF_API uint64_t CF_CALL cf_stouint(const char* s); -CF_API float CF_CALL cf_stofloat(const char* s); -CF_API double CF_CALL cf_stodouble(const char* s); -CF_API uint64_t CF_CALL cf_stohex(const char* s); -CF_API char* CF_CALL cf_sreplace(char* s, const char* replace_me, const char* with_me); -CF_API char* CF_CALL cf_sdedup(char* s, int ch); -CF_API char* CF_CALL cf_serase(char* s, int index, int count); -CF_API char* CF_CALL cf_spop(char* s); -CF_API char* CF_CALL cf_spopn(char* s, int n); -CF_API char* CF_CALL cf_string_append_UTF8_impl(char *s, int codepoint); - -CF_API const char* CF_CALL cf_sintern(const char* s); -CF_API const char* CF_CALL cf_sintern_range(const char* start, const char* end); -CF_API void CF_CALL cf_sinuke_intern_table(void); #ifdef __cplusplus } @@ -997,7 +1114,7 @@ struct String { CF_INLINE String() { } CF_INLINE String(const char* s) { sset(m_str, s); } - CF_INLINE String(const char* start, const char* end) { int length = (int)(end - start); sfit(m_str, length); CF_STRNCPY(m_str, start, length); CF_AHDR(m_str)->size = length + 1; } + CF_INLINE String(const char* start, const char* end) { int length = (int)(end - start); sfit(m_str, length); strncpy(m_str, start, length); CK_AHDR(m_str)->size = length + 1; } CF_INLINE String(const String& s) { sset(m_str, s); } CF_INLINE String(String&& s) { m_str = s.m_str; s.m_str = NULL; } CF_INLINE String(int i) { sint(m_str, i); } @@ -1009,7 +1126,7 @@ struct String CF_INLINE String(bool b) { sbool(m_str, b); } CF_INLINE ~String() { sfree(m_str); m_str = NULL; } - CF_INLINE static String steal_from(char* cute_c_api_string) { CF_ACANARY(cute_c_api_string); String r; r.m_str = cute_c_api_string; return r; } + CF_INLINE static String steal_from(char* cute_c_api_string) { CK_ACANARY(cute_c_api_string); String r; r.m_str = cute_c_api_string; return r; } CF_INLINE char* steal() { char* result = m_str; m_str = NULL; return result; } CF_INLINE static String from_hex(uint64_t uint) { String r; shex(r.m_str, uint); return r; } @@ -1040,7 +1157,7 @@ struct String CF_INLINE int count() const { return scount(m_str); } CF_INLINE void ensure_capacity(int capacity) { sfit(m_str, capacity); } CF_INLINE void fit(int capacity) { sfit(m_str, capacity); } - CF_INLINE void set_len(int len) { sfit(m_str, len + 1); cf_array_len(m_str) = len + 1; m_str[len] = 0; } + CF_INLINE void set_len(int len) { sfit(m_str, len + 1); asetlen(m_str, len + 1); m_str[len] = 0; } CF_INLINE bool empty() const { return sempty(m_str); } CF_INLINE String& add(char ch) { spush(m_str, ch); return *this; } @@ -1059,8 +1176,8 @@ struct String CF_INLINE String& operator=(const char* s) { sset(m_str, s); return *this; } CF_INLINE String& operator=(const String& s) { sset(m_str, s); return *this; } CF_INLINE String& operator=(String&& s) { m_str = s.m_str; s.m_str = NULL; return *this; } - CF_INLINE Array split(char split_c) { Array r; char** s = ssplit(m_str, split_c); for (int i=0;i split(const char* split_me, char split_c) { Array r; char** s = ssplit(split_me, split_c); for (int i=0;i split(char split_c) { Array r; char** s = ssplit(m_str, split_c); for (int i=0;i split(const char* split_me, char split_c) { Array r; char** s = ssplit(split_me, split_c); for (int i=0;itext = text; } - CF_INLINE bool next() { if (*text) { text = cf_decode_UTF8(text, &codepoint); return true; } else return false; } + CF_INLINE bool next() { if (*text) { text = cf_string_decode_UTF8(text, &codepoint); return true; } else return false; } int codepoint; const char* text = NULL; @@ -1127,9 +1244,9 @@ struct UTF8 /** * UTF16 decoder. Load it up with a string and read `.codepoint`. Call `next` to fetch the * next codepoint. - * + * * Example: - * + * * UTF16 utf16 = UTF16(my_string_in_utf16_format); * while (utf16.next()) { * DoSomethingWithCodepoint(utf16.codepoint); @@ -1140,12 +1257,14 @@ struct UTF16 CF_INLINE UTF16() { } CF_INLINE UTF16(const uint16_t* text) { this->text = text; } - CF_INLINE bool next() { if (*text) { text = cf_decode_UTF16(text, &codepoint); return true; } else return false; } + CF_INLINE bool next() { if (*text) { text = cf_string_decode_UTF16(text, &codepoint); return true; } else return false; } int codepoint; const uint16_t* text = NULL; }; +// cf_sintern/cf_sintern_range route to sintern/sintern_range (ckit macros). + } #endif // CF_CPP diff --git a/include/cute_symbol.h b/include/cute_symbol.h index 480c7ecba..8b1a5919d 100644 --- a/include/cute_symbol.h +++ b/include/cute_symbol.h @@ -5,6 +5,9 @@ This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info */ +#ifndef CF_SYMBOL_H +#define CF_SYMBOL_H + #include "cute_defines.h" //-------------------------------------------------------------------------------------------------- @@ -21,9 +24,9 @@ typedef void CF_SharedLibrary; * @category utility * @brief Loads a shared library from disk and returns a pointer to the library. * @param path Path to the shared library in platform-dependent notation. - * @return Returns `NULL` in the case of errors, and can be unloaded by calling `unload_shared_library`. + * @return Returns `NULL` in the case of errors, and can be unloaded by calling `cf_unload_shared_library`. * @remarks Does not use the virtual file system. Once loaded, individual functions can be loaded from the shared - * library be called `cf_load_function`. See [Virtual File System](https://randygaul.github.io/cute_framework/topics/virtual_file_system). + * library by calling `cf_load_function`. See [Virtual File System](https://randygaul.github.io/cute_framework/topics/virtual_file_system). * @related cf_load_shared_library cf_unload_shared_library cf_load_function */ CF_API CF_SharedLibrary* CF_CALL cf_load_shared_library(const char* path); @@ -31,8 +34,8 @@ CF_API CF_SharedLibrary* CF_CALL cf_load_shared_library(const char* path); /** * @function cf_unload_shared_library * @category utility - * @brief Unloads a shared library previously loaded with `load_shared_library`. - * @param library A library of functions from `load_shared_library`. + * @brief Unloads a shared library previously loaded with `cf_load_shared_library`. + * @param library A library of functions from `cf_load_shared_library`. * @related cf_load_shared_library cf_unload_shared_library cf_load_function */ CF_API void cf_unload_shared_library(CF_SharedLibrary* library); @@ -41,9 +44,9 @@ CF_API void cf_unload_shared_library(CF_SharedLibrary* library); * @function cf_load_function * @category utility * @brief Loads a function out of a shared library. - * @param library A library of functions from `load_shared_library`. + * @param library A library of functions from `cf_load_shared_library`. * @param function_name The name of the function. - * @remarks The function pointer is not valid after calling `unload_shared_library`. After obtaining the function pointer with `load_function` + * @remarks The function pointer is not valid after calling `cf_unload_shared_library`. After obtaining the function pointer with `cf_load_function` * you must typecast it yourself. Returns `NULL` in the case of errors. * @related cf_load_shared_library cf_unload_shared_library cf_load_function */ @@ -63,8 +66,10 @@ namespace Cute CF_INLINE CF_SharedLibrary* load_shared_library(const char* path) { return cf_load_shared_library(path); } CF_INLINE void unload_shared_library(CF_SharedLibrary* library) { cf_unload_shared_library(library); } -CF_INLINE void* load_function(CF_SharedLibrary* library, const char* function_name) { return cf_load_function(library,function_name); } +CF_INLINE void* load_function(CF_SharedLibrary* library, const char* function_name) { return cf_load_function(library, function_name); } } #endif // CF_CPP + +#endif // CF_SYMBOL_H diff --git a/include/cute_time.h b/include/cute_time.h index 560e3c482..2cd6ce98a 100644 --- a/include/cute_time.h +++ b/include/cute_time.h @@ -194,7 +194,7 @@ CF_API void CF_CALL cf_pause_for_ticks(uint64_t pause_ticks); * @category time * @brief Returns true for one frame update each time an interval of seconds elapses. * @param interval Number of seconds between each interval. - * @param offset A starting offset in seconds for the interval. This gets mathematically modulo'd. Used to sync times for rythms or repeats. + * @param offset A starting offset in seconds for the interval. This gets mathematically modulo'd. Used to sync times for rhythms or repeats. * Simply place this within an if-statement! * @related cf_on_interval cf_between_interval cf_on_timestamp */ @@ -205,7 +205,7 @@ CF_API bool CF_CALL cf_on_interval(float interval, float offset); * @category time * @brief Returns true for one interval of seconds, every other interval. * @param interval Number of seconds between each interval. - * @param offset A starting offset in seconds for the interval. This gets mathematically modulo'd. Used to sync times for rythms or repeats. + * @param offset A starting offset in seconds for the interval. This gets mathematically modulo'd. Used to sync times for rhythms or repeats. * @remarks This function is super useful for general purpose gameplay implementation where you want an event to fire for N seconds, * and then _not_ fire for N seconds, flipping back and forth periodically. Simply place this within an if-statement. * @related cf_on_interval cf_between_interval cf_on_timestamp @@ -227,11 +227,29 @@ CF_API bool CF_CALL cf_on_timestamp(double timestamp); * @function cf_is_paused * @category time * @brief Returns true if the application is currently paused. - * @remarks Pause means from `cf_pause_for` or `cf_pause_for_ticks`. - * @related CF_PAUSE_TIME_LEFT cf_is_paused cf_pause_for cf_pause_for_ticks + * @remarks Pause means from `cf_pause_for`, `cf_pause_for_ticks`, or `cf_pause`. + * @related CF_PAUSE_TIME_LEFT cf_is_paused cf_pause_for cf_pause_for_ticks cf_pause cf_unpause */ CF_API bool CF_CALL cf_is_paused(void); +/** + * @function cf_pause + * @category time + * @brief Pauses the application indefinitely until `cf_unpause` is called. + * @remarks Use this when you need to pause without knowing how long in advance. + * @related CF_PAUSE_TIME_LEFT cf_is_paused cf_pause_for cf_pause_for_ticks cf_pause cf_unpause + */ +CF_API void CF_CALL cf_pause(void); + +/** + * @function cf_unpause + * @category time + * @brief Unpauses the application if it was paused. + * @remarks Clears any pause state from `cf_pause`, `cf_pause_for`, or `cf_pause_for_ticks`. + * @related CF_PAUSE_TIME_LEFT cf_is_paused cf_pause_for cf_pause_for_ticks cf_pause cf_unpause + */ +CF_API void CF_CALL cf_unpause(void); + /** * @function cf_get_ticks * @category time @@ -255,7 +273,7 @@ CF_API uint64_t CF_CALL cf_get_tick_frequency(void); * @category time * @brief Waits an estimated number of milliseconds before returning. * @remarks This function actually sleeps the application. If you want to instead pause updates without locking the entire - * applcation (so you can e.g. continue rendering and capturing user inputs) use `cf_pause_for` instead. + * application (so you can e.g. continue rendering and capturing user inputs) use `cf_pause_for` instead. * @related cf_get_ticks cf_get_tick_frequency cf_pause_for */ CF_API void CF_CALL cf_sleep(int milliseconds); @@ -266,7 +284,7 @@ CF_API void CF_CALL cf_sleep(int milliseconds); * @brief A stopwatch for general purpose precise timings. * @remarks Once created with `cf_make_stopwatch` the time elapsed can be fetched. To reset the stopwatch, simply call * `cf_make_stopwatch` again and overwrite your old stopwatch. - * @related CF_Stopwatch cf_make_stopwatch cf_seconds cf_milliseconds cf_microseconds + * @related CF_Stopwatch cf_make_stopwatch cf_stopwatch_seconds cf_stopwatch_milliseconds cf_stopwatch_microseconds */ typedef struct CF_Stopwatch { @@ -307,7 +325,7 @@ CF_API double CF_CALL cf_stopwatch_milliseconds(CF_Stopwatch stopwatch); * @function cf_stopwatch_microseconds * @category time * @brief Returns the number of microseconds elapsed since the last call to `cf_make_stopwatch` was made. - * @related CF_Stopwatch cf_make_stopwatch cf_seconds cf_stopwatch_milliseconds cf_stopwatch_microseconds + * @related CF_Stopwatch cf_make_stopwatch cf_stopwatch_seconds cf_stopwatch_milliseconds cf_stopwatch_microseconds */ CF_API double CF_CALL cf_stopwatch_microseconds(CF_Stopwatch stopwatch); @@ -326,9 +344,12 @@ namespace Cute CF_INLINE void set_fixed_timestep(int frames_per_second = 60) { cf_set_fixed_timestep(frames_per_second); } CF_INLINE void set_fixed_timestep_max_updates(int max_updates = 5) { cf_set_fixed_timestep_max_updates(max_updates); } CF_INLINE void set_target_framerate(int frames_per_second = -1) { cf_set_target_framerate(frames_per_second); } +CF_INLINE void set_update_udata(void* udata) { cf_set_update_udata(udata); } CF_INLINE void update_time(CF_OnUpdateFn* on_update = NULL) { cf_update_time(on_update); } CF_INLINE void pause_for(float seconds) { cf_pause_for(seconds); } CF_INLINE void pause_for_ticks(uint64_t pause_ticks) { cf_pause_for_ticks(pause_ticks); } +CF_INLINE void pause() { cf_pause(); } +CF_INLINE void unpause() { cf_unpause(); } CF_INLINE bool on_interval(float interval, float offset = 0) { return cf_on_interval(interval, offset); } CF_INLINE bool between_interval(float interval, float offset = 0) { return cf_between_interval(interval, offset); } diff --git a/include/cute_user_config.h b/include/cute_user_config.h index 6c3c638eb..28e04fe43 100644 --- a/include/cute_user_config.h +++ b/include/cute_user_config.h @@ -5,8 +5,8 @@ This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info */ -#ifndef CUTE_USER_CONFIG_H -#define CUTE_USER_CONFIG_H +#ifndef CF_USER_CONFIG_H +#define CF_USER_CONFIG_H /** * Feel free to override any macros in Cute Framework with your own. These are mainly to change the default @@ -50,4 +50,4 @@ * CF_FMODF */ -#endif // CUTE_USER_CONFIG_H +#endif // CF_USER_CONFIG_H diff --git a/include/cute_version.h.in b/include/cute_version.h.in index 05e8367a5..0db7ccc50 100644 --- a/include/cute_version.h.in +++ b/include/cute_version.h.in @@ -19,7 +19,7 @@ extern "C" { #endif // __cplusplus -CF_API const char* CF_CALL cf_version_string_linked(); +CF_API const char* CF_CALL cf_version_string_linked(void); #ifdef __cplusplus } diff --git a/libraries/cute/ckit.h b/libraries/cute/ckit.h new file mode 100644 index 000000000..16d63bd0b --- /dev/null +++ b/libraries/cute/ckit.h @@ -0,0 +1,1936 @@ +/* + ckit -- A single-header kit of high-performance C essentials. + + In exactly one .c/.cpp file: + #define CKIT_IMPLEMENTATION + #include "ckit.h" + In all other cases include as-expected: + #include "ckit.h" + + QUICK START GUIDE + + 1) Dynamic arrays: + int* a = NULL; + for (int i = 0; i < 10; ++i) { + apush(a, i); + } + for (int i = 0; i < 10; ++i) { + printf("%d\n", a[i]); + } + printf("len=%d cap=%d\n", acount(a), acap(a)); + afree(a); + + 2) Dynamic strings: + char* s = NULL; + sset(s, "Hello "); + sappend(s, "world!"); + printf("%s\n", s); + sfree(s); + + 3) Map (typed hashtable): + CK_MAP(int) m = NULL; + map_set(m, sintern("x"), 10); + map_set(m, sintern("y"), 20); + int x = map_get(m, sintern("x")); + int y = map_get(m, sintern("y")); + for (int i = 0; i < map_size(m); i++) { + printf("key=%llu val=%d\n", map_keys(m)[i], map_items(m)[i]); + } + map_free(m); + + 4) String interning: + const char* a = sintern("hello"); + char buf[64]; + strcpy(buf, "he"); + strcat(buf, "llo"); + const char *b = sintern(buf); + assert(a == b); + + Public domain - Do whatever you want with this code. +*/ + +#ifndef CKIT_H +#define CKIT_H + +#include +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------------------------------- +// Overrideable macros (define before including ckit.h to customize behavior): +// +// CK_ALLOC(sz) - Custom allocator. Default: malloc(sz) +// CK_REALLOC(p, sz) - Custom reallocator. Default: realloc(p, sz) +// CK_FREE(p) - Custom free. Default: free(p) + +#ifndef CK_ALLOC +# define CK_ALLOC(sz) malloc(sz) +#endif +#ifndef CK_REALLOC +# define CK_REALLOC(p, sz) realloc(p, sz) +#endif +#ifndef CK_FREE +# define CK_FREE(p) free(p) +#endif + +//-------------------------------------------------------------------------------------------------- +// Dynamic arrays (stretchy buffers). +// +// Declare as T* arr = NULL. Push elements with apush(). Access like a normal array. +// NULL is a valid empty array. +// +// Example: +// int* a = NULL; +// apush(a, 10); +// apush(a, 20); +// printf("%d %d\n", a[0], a[1]); // 10 20 +// afree(a); + +// Markup macros for documentation purposes. Expand to nothing. +#define CK_DYNA // Annotates a pointer as a dynamic array. +#define CK_SDYNA // Annotates a pointer as a dynamic string. + +// asize/acount: Number of elements. Returns 0 for NULL. +#define asize(a) ((a) ? CK_AHDR(a)->size : 0) +#define acount(a) asize(a) + +// asetlen: Set size directly (must be <= capacity). Does not allocate. +#define asetlen(a, n) (CK_AHDR(a)->size = (n)) + +// acap: Allocated capacity. Returns 0 for NULL. +#define acap(a) ((a) ? CK_AHDR(a)->capacity : 0) + +// afit: Ensure capacity for n elements. May reallocate. +#define afit(a, n) ((n) <= acap(a) ? 0 : (*(void**)&(a) = ck_agrow((a), (n), sizeof(*(a))))) + +// apush: Append element. May reallocate. +#define apush(a, ...) (CK_ACANARY(a), afit((a), 1 + asize(a)), (a)[CK_AHDR(a)->size++] = (__VA_ARGS__)) + +// apop: Remove and return last element. Array must not be empty. +#define apop(a) ((a)[--CK_AHDR(a)->size]) + +// aend: Pointer one past the last element. +#define aend(a) ((a) + asize(a)) + +// alast: Last element. Array must not be empty. +#define alast(a) ((a)[asize(a) - 1]) + +// aclear: Set size to 0 but keep allocated memory. +#define aclear(a) (CK_ACANARY(a), (a) ? CK_AHDR(a)->size = 0 : 0) + +// adel: Remove element at index i by swapping with last (O(1), unordered). +#define adel(a, i) ((a)[i] = (a)[--CK_AHDR(a)->size]) + +// astatic: Use a stack/static buffer instead of heap allocation. +// char buffer[1024]; +// int* a = NULL; +// astatic(a, buffer, sizeof(buffer)); +#define astatic(a, buffer, buffer_size) (*(void**)&(a) = ck_astatic(buffer, buffer_size, sizeof(*(a)))) + +// aset: Copy array b into a. +#define aset(a, b) (*(void**)&(a) = ck_aset((void*)(a), (void*)(b), sizeof(*(a)))) + +// arev: Reverse array in-place. +#define arev(a) ((a) ? ck_arev(a, sizeof(*(a))) : (void*)0) + +// ahash: Hash all bytes in the array using FNV1a. +#define ahash(a) ((a) ? ck_hash_fnv1a(a, sizeof(*(a)) * asize(a)) : 0) + +// afree: Free array memory and set pointer to NULL. +#define afree(a) do { CK_ACANARY(a); if (a && !CK_AHDR(a)->is_static) CK_FREE(CK_AHDR(a)); (a) = NULL; } while (0) + +// Check if array is a valid dynamic array. +#define avalid(a) ((a) && CK_AHDR(a)->cookie.val == CK_ACOOKIE) + +//-------------------------------------------------------------------------------------------------- +// Dynamic strings (built on dynamic arrays). +// +// 100% compatible with normal C-strings. Free with sfree when done. +// +// To create a new string use smake or sfmake. To overwrite an existing string +// use sset or sfmt. The first argument to sset/sfmt must be an l-value (a +// variable), not a literal like NULL -- use smake/sfmake to create from scratch. +// +// Example: +// +// char* s = smake("Hello world!"); +// printf("%s\n", s); +// sset(s, "Goodbye!"); +// printf("%s\n", s); +// sfree(s); + +// slen: String length (not including null terminator). Returns 0 for NULL. +#define slen(s) ((s) ? (asize(s) ? asize(s) - 1 : asize(s)) : 0) + +// scount: Internal array size (length + null terminator). +#define scount(s) asize(s) + +// scap: Allocated capacity. +#define scap(s) acap(s) + +// sempty: True if string is NULL or has length 0. +#define sempty(s) ((s) ? slen(s) < 1 : 1) + +// sfirst/slast: First or last character. Returns '\0' for empty/NULL. +#define sfirst(s) ((s) ? (s)[0] : '\0') +#define slast(s) ((s) ? (s)[slen(s) - 1] : '\0') + +// sclear: Clear string to empty but keep allocated memory. +#define sclear(s) (aclear(s), apush(s, 0)) + +// sstatic: Create a string backed by a static buffer. Grows to heap if needed. +#define sstatic(s, buf, sz) (astatic(s, buf, sz), apush(s, 0)) + +// sisdyna: True if s is a dynamic string from this API (not a literal or static buffer). +#define sisdyna(s) (!((#s)[0] == '"') && ck_avalid(s)) + +// sfree: Free string memory and set pointer to NULL. +#define sfree(s) afree(s) + +// sfit: Ensure capacity for n characters. +#define sfit(s, n) (s = ck_sfit(s, n)) + +// spush/spop: Append or remove a single character. +#define spush(s, ch) do { if (!(s)) apush(s, ch); else (s)[slen(s)] = (ch); apush(s, 0); } while (0) +#define spop(s) (s = ck_spop(s)) +#define spopn(s, n) (s = ck_spopn(s, n)) + +// sset: Overwrite a with contents of b. +#define sset(a, b) (a = ck_sset(a, b)) + +// sdup/smake: Create a new string (copies from source). +// char* s = smake("hello"); +#define sdup(s) ck_sset(NULL, s) +#define smake(s) ck_sset(NULL, s) + +// sfmake/sfmt: Printf-style string creation/formatting. +// char* s = sfmake("x=%d", 42); // Create new +// sfmt(s, "x=%d", 99); // Overwrite existing +#define sfmake(fmt, ...) ck_sfmt(NULL, fmt, __VA_ARGS__) +#define sfmt(s, fmt, ...) (s = ck_sfmt(s, fmt, __VA_ARGS__)) +#define sfmt_append(s, fmt, ...) (s = ck_sfmt_append(s, fmt, __VA_ARGS__)) +#define svfmt(s, fmt, args) (s = ck_svfmt(s, fmt, args)) +#define svfmt_append(s, fmt, args) (s = ck_svfmt_append(s, fmt, args)) + +// sappend/scat: Concatenate b onto a. +#define sappend(a, b) (a = ck_sappend(a, b)) +#define scat(a, b) sappend(a, b) +#define sappend_range(a, b, e) (a = ck_sappend_range(a, b, e)) +#define scat_range(a, b, e) sappend_range(a, b, e) + +// Comparison. sequ/siequ are NULL-safe (NULL == NULL is true). +#define scmp(a, b) strcmp(a, b) +#define sicmp(a, b) ck_stricmp(a, b) +#define sequ(a, b) ((a) == NULL && (b) == NULL ? 1 : ((a) == NULL || (b) == NULL ? 0 : !strcmp((a), (b)))) +#define siequ(a, b) ((a) == NULL && (b) == NULL ? 1 : ((a) == NULL || (b) == NULL ? 0 : !ck_stricmp((a), (b)))) + +// sprefix/ssuffix: Check if string starts/ends with given prefix/suffix. +#define sprefix(s, p) ck_sprefix(s, p) +#define ssuffix(s, p) ck_ssuffix(s, p) +#define scontains(s, sub) (slen(s) >= (int)strlen(sub) && !!strstr(s, sub)) + +// sfirst_index_of/slast_index_of: Find character. Returns -1 if not found. +#define sfirst_index_of(s, ch) ck_sfirst_index_of(s, ch) +#define slast_index_of(s, ch) ck_slast_index_of(s, ch) + +// sfind: Find substring. Returns pointer to match or NULL. +#define sfind(s, sub) strstr(s, sub) + +// stoupper/stolower: Convert case in-place. +#define stoupper(s) ck_stoupper(s) +#define stolower(s) ck_stolower(s) + +// shash: FNV-1a hash of string. +#define shash(s) ck_hash_fnv1a(s, slen(s)) + +// strim/sltrim/srtrim: Trim whitespace from both ends, left only, or right only. +#define strim(s) (s = ck_strim(s)) +#define sltrim(s) (s = ck_sltrim(s)) +#define srtrim(s) (s = ck_srtrim(s)) + +// slpad/srpad: Pad string with n copies of ch on left or right. +#define slpad(s, ch, n) (s = ck_slpad(s, ch, n)) +#define srpad(s, ch, n) (s = ck_srpad(s, ch, n)) + +// sreplace: Replace all occurrences of old with new. +#define sreplace(s, old, new) (s = ck_sreplace(s, old, new)) + +// sdedup: Collapse consecutive runs of ch into single ch. +#define sdedup(s, ch) (s = ck_sdedup(s, ch)) + +// serase: Remove n characters starting at idx. +#define serase(s, idx, n) (s = ck_serase(s, idx, n)) + +// ssplit_once: Split at first occurrence of ch. +// Returns left part (new string). Modifies s to contain right part. +// char* s = smake("a:b:c"); +// char* left = ssplit_once(s, ':'); // left="a", s="b:c" +#define ssplit_once(s, ch) ck_ssplit_once(s, ch) + +// ssplit: Split into array of strings. Caller must afree the array and each string. +// char** parts = ssplit("a:b:c", ':'); +// for (int i = 0; i < acount(parts); ++i) printf("%s\n", parts[i]); +// for (int i = 0; i < acount(parts); ++i) sfree(parts[i]); +// afree(parts); +#define ssplit(s, ch) ck_ssplit(s, ch) + +// Parsing functions. +#define stoint(s) ck_stoint(s) +#define stouint(s) ck_stouint(s) +#define stofloat(s) ck_stofloat(s) +#define stodouble(s) ck_stodouble(s) +#define stohex(s) ck_stohex(s) // Accepts 0x or # prefix. +#define stobool(s) (!strcmp(s, "true")) + +// sappend_UTF8: Append a Unicode codepoint encoded as UTF-8. +#define sappend_UTF8(s, cp) (s = ck_sappend_UTF8(s, cp)) + +// decode_UTF8: Decode one UTF-8 codepoint from s, store it in *codepoint. +// Returns a pointer past the consumed bytes. Invalid sequences yield 0xFFFD. +#define decode_UTF8(s, cp) ck_decode_UTF8(s, cp) + +// decode_UTF16: Decode one UTF-16 codepoint (with surrogate pair support) from s, store it in *codepoint. +// Returns a pointer past the consumed uint16_t(s). Invalid surrogates yield 0xFFFD. +#define decode_UTF16(s, cp) ck_decode_UTF16(s, cp) + +// String formatting from primitives. +#define sint(s, i) sfmt(s, "%d", (int)(i)) +#define suint(s, u) sfmt(s, "%" PRIu64, (uint64_t)(u)) +#define sfloat(s, f) sfmt(s, "%f", (double)(f)) +#define sdouble(s, f) sfmt(s, "%f", (double)(f)) +#define shex(s, u) sfmt(s, "0x%x", (unsigned)(u)) +#define sptr(s, p) sfmt(s, "%p", (void*)(p)) +#define sbool(s, b) sfmt(s, "%s", (b) ? "true" : "false") + +//-------------------------------------------------------------------------------------------------- +// String path utilities. +// +// All return newly allocated strings (caller must sfree). +// +// Example: +// char* ext = spext("file.txt"); // -> ".txt" +// char* norm = spnorm("a/../b"); // -> "/b" +// sfree(ext); sfree(norm); + +// spfname: Get filename from path. "/usr/bin/app" -> "app" +#define spfname(s) ck_spfname(s) + +// spfname_no_ext: Get filename without extension. "/usr/bin/app.exe" -> "app" +#define spfname_no_ext(s) ck_spfname_no_ext(s) + +// spext: Get file extension including dot. "file.txt" -> ".txt" +#define spext(s) ck_spext(s) + +// spext_equ: Check if path has given extension. spext_equ("file.txt", ".txt") -> 1 +#define spext_equ(s, ext) ck_spext_equ(s, ext) + +// sppop: Remove last path component. "/a/b/c" -> "/a/b" +#define sppop(s) ck_sppop(s) + +// sppopn: Remove last n path components. +#define sppopn(s, n) ck_sppopn(s, n) + +// spcompact: Shorten path to fit n characters using "...". +#define spcompact(s, n) ck_spcompact(s, n) + +// spdir_of: Get parent directory. "/a/b/c" -> "/b" +#define spdir_of(s) ck_spdir_of(s) + +// sptop_of: Get top-level directory after root. +#define sptop_of(s) ck_sptop_of(s) + +// spnorm: Normalize path. Resolves "..", converts \ to /. +#define spnorm(s) ck_spnorm(s) + +//-------------------------------------------------------------------------------------------------- +// Map (typed hashtable) - Stretchy-buffer style. +// +// CK_MAP(T) resolves to T*. NULL is a valid empty map. Keys are always uint64_t. +// For string keys, use sintern() to get a unique pointer and cast to uint64_t. +// Keys and values are stored in parallel dense arrays for fast iteration. +// +// Example: +// CK_MAP(int) scores = NULL; +// map_set(scores, sintern("player1"), 100); +// map_set(scores, sintern("player2"), 200); +// int p1 = map_get(scores, sintern("player1")); // 100 +// +// // Iteration: +// for (int i = 0; i < map_size(scores); i++) { +// uint64_t key = map_keys(scores)[i]; +// int val = scores[i]; // or map_items(scores)[i] +// } +// map_free(scores); + +// CK_MAP(T): Declares a map type. Just T* under the hood. +#define CK_MAP(T) T* + +// map_size: Number of {key, value} pairs. Returns 0 for NULL. +#define map_size(m) (map_validate(m), (m) ? CK_MHDR(m)->size : 0) + +// map_capacity: Allocated capacity. +#define map_capacity(m) (map_validate(m), (m) ? CK_MHDR(m)->capacity : 0) + +// map_keys: Pointer to keys array (uint64_t*). Parallel to items array. +#define map_keys(m) (map_validate(m), (m) ? ck_map_keys_ptr(CK_MHDR(m)) : NULL) + +// map_items: Pointer to items array. Same as the map pointer itself. +#define map_items(m) (map_validate(m), (m)) + +// map_has: Check if key exists. +#define map_has(m, k) ( \ + map_validate(m), \ + (m) && ck_map_find_impl(CK_MHDR(m), (uint64_t)(k)) >= 0) + +// map_del: Remove entry by key. Returns 1 if deleted, 0 if not found. +#define map_del(m, k) ( \ + map_validate(m), \ + (m) ? ck_map_del_impl(CK_MHDR(m), (uint64_t)(k)) : 0) + +// map_clear: Remove all entries but keep allocated memory. +#define map_clear(m) do { \ + map_validate(m); \ + if (m) ck_map_clear_impl(CK_MHDR(m)); \ +} while(0) + +// map_free: Free all memory and set pointer to NULL. +#define map_free(m) do { \ + if (m) { map_validate(m); ck_map_free_impl(CK_MHDR(m)); } \ + (m) = NULL; \ +} while(0) + +// map_find: Alias for map_get. +#define map_find(m, k) map_get(m, k) + +// map_key/map_val: Access key or value at index i. +#define map_key(m, i) (map_keys(m)[i]) +#define map_val(m, i) (map_items(m)[i]) + +// map_sort: Sort entries by values. Comparator receives pointers to values. +// int cmp(const void* a, const void* b) { return *(int*)a - *(int*)b; } +// map_sort(m, cmp); +#define map_sort(m, cmp) do { \ + map_validate(m); \ + if (m) ck_map_sort_impl(CK_MHDR(m), cmp); \ +} while(0) + +// map_ssort: Sort entries by keys (treating keys as interned string pointers). +// map_ssort(m, 0); // case-sensitive +// map_ssort(m, 1); // case-insensitive +#define map_ssort(m, ignore_case) do { \ + map_validate(m); \ + if (m) ck_map_ssort_impl(CK_MHDR(m), ignore_case); \ +} while(0) + +// map_swap: Swap entries at indices i and j. +#define map_swap(m, i, j) do { \ + map_validate(m); \ + if (m) ck_map_swap_impl(CK_MHDR(m), i, j); \ +} while(0) + +//-------------------------------------------------------------------------------------------------- +// C/C++ specific map macros (type inference differs). +// +// map_get: Get value by key. Returns zero-initialized value if not found. +// map_get_ptr: Get pointer to value. Returns NULL if not found. +// map_set: Set value for key. Creates entry if not exists. +// map_add: Alias for map_set with uint64_t value (backwards compat). + +#ifdef __cplusplus +# include +# define map_get(m, k) (map_validate(m), (m) && ck_map_find_impl(CK_MHDR(m), (uint64_t)(k)) >= 0 ? (m)[ck_map_find_impl(CK_MHDR(m), (uint64_t)(k))] : std::remove_pointer_t>{}) +# define map_get_ptr(m, k) (map_validate(m), (m) ? (std::remove_reference_t)ck_map_get_ptr_impl(CK_MHDR(m), (uint64_t)(k)) : nullptr) +# define map_set(m, k, v) do { std::remove_pointer_t ck_v_ = (v); ck_map_set_stretchy((void**)&(m), (uint64_t)(k), &ck_v_, sizeof(ck_v_)); map_validate(m); } while(0) +# define map_add(m, k, v) do { uint64_t ck_v_ = (uint64_t)(v); ck_map_set_stretchy((void**)&(m), (uint64_t)(k), &ck_v_, sizeof(ck_v_)); map_validate(m); } while(0) +#else + +// map_get: Get value by key. Returns zero-initialized value if not found. +// int x = map_get(m, sintern("x")); +#define map_get(m, k) ( \ + map_validate(m), \ + (m) && ck_map_find_impl(CK_MHDR(m), (uint64_t)(k)) >= 0 \ + ? (m)[ck_map_find_impl(CK_MHDR(m), (uint64_t)(k))] \ + : (typeof(*(m))){ 0 }) + +// map_get_ptr: Get pointer to value. Returns NULL if not found. +// int* px = map_get_ptr(m, sintern("x")); +// if (px) *px = 999; +#define map_get_ptr(m, k) ( \ + map_validate(m), \ + (m) ? (typeof(m))ck_map_get_ptr_impl(CK_MHDR(m), (uint64_t)(k)) : NULL) + +// map_set: Set value for key. Creates entry if not exists. May reallocate. +// map_set(m, sintern("x"), 42); +#define map_set(m, k, v) do { \ + typeof(*(m)) ck_v_ = (v); \ + ck_map_set_stretchy((void**)&(m), (uint64_t)(k), &ck_v_, sizeof(ck_v_)); \ + map_validate(m); \ +} while(0) + +// map_add: Alias for map_set with uint64_t value (backwards compat). +#define map_add(m, k, v) do { \ + uint64_t ck_v_ = (uint64_t)(v); \ + ck_map_set_stretchy((void**)&(m), (uint64_t)(k), &ck_v_, sizeof(ck_v_)); \ + map_validate(m); \ +} while(0) + +#endif + +//-------------------------------------------------------------------------------------------------- +// String interning. +// +// Returns a unique, stable pointer for each unique string. +// Interned strings can be compared by pointer (==) instead of strcmp. +// Great as map keys: cast the pointer to uint64_t. +// Also reduces memory by deduplicating identical strings. +// +// Example: +// const char* a = sintern("hello"); +// char buf[64]; strcpy(buf, "hel"); strcat(buf, "lo"); +// const char* b = sintern(buf); +// assert(a == b); // Same pointer! +// +// // Use as map key: +// CK_MAP(int) m = NULL; +// map_set(m, sintern("x"), 10); +// int x = map_get(m, sintern("x")); // 10 + +// sintern: Return interned string. Same contents always returns same pointer. +#define sintern(s) ck_sintern(s) + +// sintern_range: Intern a substring [start, end). +#define sintern_range(start, end) ck_sintern_range(start, end) + +// sivalid: True if s is an interned string (from sintern). +#define sivalid(s) (((CK_UniqueString*)(s) - 1)->cookie.val == CK_INTERN_COOKIE) + +// silen: Length of an interned string (constant-time). +#define silen(s) (((CK_UniqueString*)(s) - 1)->len) + +// sinuke: Free all interned strings. All previous pointers become invalid. +#define sinuke() sintern_nuke() + +//-------------------------------------------------------------------------------------------------- +// Private implementation details. + +// Cookie union for debugger-friendly viewing of 4-char magic values. +typedef union CK_Cookie +{ + uint32_t val; + char c[4]; +} CK_Cookie; + +// Compile-time cookie value (uint32_t literal, no function call). +#define CK_COOKIE_VAL(a, b, c, d) \ + ((uint32_t)(unsigned char)(a) | ((uint32_t)(unsigned char)(b) << 8) | \ + ((uint32_t)(unsigned char)(c) << 16) | ((uint32_t)(unsigned char)(d) << 24)) + +// Portable case-insensitive string compare. +#ifdef _WIN32 +# define ck_stricmp _stricmp +#else +# define ck_stricmp strcasecmp +#endif + +// Intern structure for validation and length access. +#define CK_INTERN_COOKIE CK_COOKIE_VAL('I','N','T','R') +typedef struct CK_UniqueString +{ + CK_Cookie cookie; + int len; + struct CK_UniqueString* next; + char* str; +} CK_UniqueString; + +// Hidden array header behind the user pointer. +typedef struct CK_ArrayHeader +{ + int size; + int capacity; + int is_static; + char* data; + CK_Cookie cookie; +} CK_ArrayHeader; + +#define CK_AHDR(a) ((CK_ArrayHeader*)(a) - 1) +#define CK_ACOOKIE CK_COOKIE_VAL('A','R','R','Y') +#define CK_ACANARY(a) ((a) ? assert(CK_AHDR(a)->cookie.val == CK_ACOOKIE) : (void)0) + +#ifndef CK_API +#define CK_API +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +CK_API void sintern_nuke(); +CK_API int ck_sintern_gen(); + +CK_API void* ck_agrow(const void* a, int new_size, size_t element_size); +CK_API void* ck_astatic(const void* a, int buffer_size, size_t element_size); +CK_API void* ck_aset(const void* a, const void* b, size_t element_size); +CK_API void* ck_arev(const void* a, size_t element_size); + +typedef struct CK_MapSlot +{ + uint64_t h; + int item_index; + int base_count; +} CK_MapSlot; + +// Safety cookie for map validation. +#define CK_MAP_COOKIE CK_COOKIE_VAL('M','A','P','!') + +// Compute pointers to arrays within the single allocation. +// items: right after header (header is 24 bytes, 8-byte aligned) +#define ck_map_items_ptr(hdr) ((void*)((hdr) + 1)) + +// keys: after items (aligned to 8) +#define ck_map_keys_offset(cap, vsz) (sizeof(CK_MapHeader) + CK_ALIGN8((size_t)(cap) * (vsz))) +#define ck_map_keys_ptr(hdr) ((uint64_t*)((char*)(hdr) + ck_map_keys_offset((hdr)->capacity, (hdr)->val_size))) + +// islot: after keys (already 8-byte aligned since keys are uint64_t) +#define ck_map_islot_ptr(hdr) ((int*)(ck_map_keys_ptr(hdr) + (hdr)->capacity)) + +// slots: after islot (aligned to 8 for CK_MapSlot) +#define ck_map_slots_offset(hdr) CK_ALIGN8((size_t)((char*)(ck_map_islot_ptr(hdr) + (hdr)->capacity) - (char*)(hdr))) +#define ck_map_slots_ptr(hdr) ((CK_MapSlot*)((char*)(hdr) + ck_map_slots_offset(hdr))) + +// Internal: Get header pointer from map pointer. +#define CK_MHDR(m) ((m) ? (CK_MapHeader*)((char*)(m) - sizeof(CK_MapHeader)) : NULL) + +// Internal: Validate map cookie (catches use-after-free, etc). +#define map_validate(m) ((void)(!(m) || (assert(CK_MHDR(m)->cookie.val == CK_MAP_COOKIE), 1))) + +// Map header stored just before the values array. +// All arrays (items, keys, islot, slots) are in a single allocation following the header. +// Layout: [Header][items...][keys...][islot...][slots...] +typedef struct CK_MapHeader +{ + CK_Cookie cookie; // Safety cookie for validation + int val_size; // Size of each value in bytes + int size; // Number of items + int capacity; // Capacity for items/keys/islot + int slot_count; // Number of used hash slots + int slot_capacity; // Capacity for hash slots (power of 2) +} CK_MapHeader; + +// Alignment helper. +#define CK_ALIGN8(x) (((x) + 7) & ~(size_t)7) + +// Map implementation functions. +CK_API void ck_map_set_stretchy(void** m_ptr, uint64_t key, const void* val, int val_size); +CK_API CK_MapHeader* ck_map_ensure_capacity(void** m_ptr, int want_items, int val_size); +CK_API int ck_map_find_impl(CK_MapHeader* hdr, uint64_t key); +CK_API void* ck_map_get_ptr_impl(CK_MapHeader* hdr, uint64_t key); +CK_API int ck_map_del_impl(CK_MapHeader* hdr, uint64_t key); +CK_API void ck_map_clear_impl(CK_MapHeader* hdr); +CK_API void ck_map_free_impl(CK_MapHeader* hdr); +CK_API void ck_map_swap_impl(CK_MapHeader* hdr, int i, int j); +CK_API void ck_map_sort_impl(CK_MapHeader* hdr, int (*cmp)(const void* a, const void* b)); +CK_API void ck_map_ssort_impl(CK_MapHeader* hdr, int ignore_case); + +// String implementation functions. +CK_API char* ck_sfit(char* a, int n); +CK_API char* ck_sset(char* a, const char* b); +CK_API char* ck_sfmt(char* s, const char* fmt, ...); +CK_API char* ck_sfmt_append(char* s, const char* fmt, ...); +CK_API char* ck_svfmt(char* s, const char* fmt, va_list args); +CK_API char* ck_svfmt_append(char* s, const char* fmt, va_list args); +CK_API int ck_sprefix(const char* s, const char* prefix); +CK_API int ck_ssuffix(const char* s, const char* suffix); +CK_API void ck_stoupper(char* s); +CK_API void ck_stolower(char* s); +CK_API char* ck_sappend(char* a, const char* b); +CK_API char* ck_sappend_range(char* a, const char* b, const char* b_end); +CK_API char* ck_strim(char* s); +CK_API char* ck_sltrim(char* s); +CK_API char* ck_srtrim(char* s); +CK_API char* ck_slpad(char* s, char pad, int count); +CK_API char* ck_srpad(char* s, char pad, int count); +CK_API char* ck_ssplit_once(char* s, char split_c); +CK_API char** ck_ssplit(const char* s, char split_c); +CK_API int ck_sfirst_index_of(const char* s, char c); +CK_API int ck_slast_index_of(const char* s, char c); +CK_API int ck_stoint(const char* s); +CK_API uint64_t ck_stouint(const char* s); +CK_API float ck_stofloat(const char* s); +CK_API double ck_stodouble(const char* s); +CK_API uint64_t ck_stohex(const char* s); +CK_API char* ck_sreplace(char* s, const char* replace_me, const char* with_me); +CK_API char* ck_sdedup(char* s, int ch); +CK_API char* ck_serase(char* s, int index, int count); +CK_API char* ck_spop(char* s); +CK_API char* ck_spopn(char* s, int n); +CK_API char* ck_sappend_UTF8(char* s, int codepoint); +CK_API const char* ck_decode_UTF8(const char* s, int* codepoint); + +// Path implementation functions. +CK_API char* ck_spfname(const char* path); +CK_API char* ck_spfname_no_ext(const char* path); +CK_API char* ck_spext(const char* path); +CK_API int ck_spext_equ(const char* path, const char* ext); +CK_API char* ck_sppop(const char* path); +CK_API char* ck_sppopn(const char* path, int n); +CK_API char* ck_spcompact(const char* path, int n); +CK_API char* ck_spdir_of(const char* path); +CK_API char* ck_sptop_of(const char* path); +CK_API char* ck_spnorm(const char* path); + +// UTF16 decode. +#include +CK_API const uint16_t* ck_decode_UTF16(const uint16_t* s, int* codepoint); + +// Intern implementation. +CK_API const char* ck_sintern_range(const char* start, const char* end); +CK_API uint64_t ck_hash_fnv1a(const void* ptr, size_t sz); + +static inline const char* ck_sintern(const char* s) +{ + // Direct-mapped pointer cache. String literals have stable addresses, + // so the same call site always hits after the first miss. The strcmp + // handles reused buffers (e.g. sprintf into the same char[]). The + // generation counter invalidates the cache after sintern_nuke(). + struct ck_sintern_cache_entry { const char* key; const char* val; }; +#ifdef __cplusplus + static thread_local struct ck_sintern_cache_entry ck_sintern_cache[64]; + static thread_local int ck_sintern_cache_gen; +#else + static _Thread_local struct ck_sintern_cache_entry ck_sintern_cache[64]; + static _Thread_local int ck_sintern_cache_gen; +#endif + int gen = ck_sintern_gen(); + if (ck_sintern_cache_gen != gen) { + memset(ck_sintern_cache, 0, sizeof(ck_sintern_cache)); + ck_sintern_cache_gen = gen; + } + unsigned ck_idx = (unsigned)((uintptr_t)s >> 3) & 63; + if (ck_sintern_cache[ck_idx].key == s && strcmp(ck_sintern_cache[ck_idx].val, s) == 0) + return ck_sintern_cache[ck_idx].val; + const char* result = ck_sintern_range(s, s + strlen(s)); + ck_sintern_cache[ck_idx].key = s; + ck_sintern_cache[ck_idx].val = result; + return result; +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CKIT_H + +//-------------------------------------------------------------------------------------------------- + +#ifdef CKIT_IMPLEMENTATION + +#ifndef CKIT_IMPLEMENTATION_GUARD +#define CKIT_IMPLEMENTATION_GUARD + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//-------------------------------------------------------------------------------------------------- +// Array. + +void* ck_agrow(const void* a, int new_size, size_t element_size) +{ + CK_ACANARY(a); + assert(acap(a) <= (SIZE_MAX - 1) / 2); + int new_capacity = 2 * acap(a); + int min_cap = new_size > 16 ? new_size : 16; + if (new_capacity < min_cap) new_capacity = min_cap; + assert(new_size <= new_capacity); + assert((size_t)new_capacity <= (SIZE_MAX - sizeof(CK_ArrayHeader)) / element_size); + size_t total_size = sizeof(CK_ArrayHeader) + (size_t)new_capacity * element_size; + CK_ArrayHeader* hdr; + if (a) { + if (!CK_AHDR(a)->is_static) { + hdr = (CK_ArrayHeader*)CK_REALLOC(CK_AHDR(a), total_size); + } else { + hdr = (CK_ArrayHeader*)CK_ALLOC(total_size); + memcpy(hdr + 1, a, (size_t)asize(a) * element_size); + hdr->size = asize(a); + hdr->cookie.val = CK_ACOOKIE; + } + } else { + hdr = (CK_ArrayHeader*)CK_ALLOC(total_size); + hdr->size = 0; + hdr->cookie.val = CK_ACOOKIE; + } + hdr->capacity = new_capacity; + hdr->is_static = 0; + hdr->data = (char*)(hdr + 1); + return (void*)(hdr + 1); +} + +void* ck_astatic(const void* a, int buffer_size, size_t element_size) +{ + CK_ArrayHeader* hdr = (CK_ArrayHeader*)a; + hdr->size = 0; + hdr->cookie.val = CK_ACOOKIE; + if (sizeof(CK_ArrayHeader) <= element_size) { + hdr->capacity = buffer_size / (int)element_size - 1; + } else { + int elements_taken = (int)sizeof(CK_ArrayHeader) / (int)element_size + ((int)sizeof(CK_ArrayHeader) % (int)element_size > 0); + hdr->capacity = buffer_size / (int)element_size - elements_taken; + } + hdr->data = (char*)(hdr + 1); + hdr->is_static = 1; + return (void*)(hdr + 1); +} + +void* ck_aset(const void* a, const void* b, size_t element_size) +{ + CK_ACANARY(a); + if (!b) { + aclear(a); + return (void*)a; + } + CK_ACANARY(b); + if (acap(a) < asize(b)) { + a = ck_agrow(a, asize(b), element_size); + } + memcpy((void*)a, b, (size_t)asize(b) * element_size); + if (a) asetlen(a, asize(b)); + else assert(!asize(b)); + return (void*)a; +} + +void* ck_arev(const void* a_ptr, size_t element_size) +{ + CK_ACANARY(a_ptr); + char* a = (char*)a_ptr; + int n = acount(a_ptr); + if (n <= 1) return (void*)a_ptr; + char* b = a + element_size * (n - 1); + char tmp[256]; + char* t = element_size <= sizeof(tmp) ? tmp : (char*)CK_ALLOC(element_size); + while (a < b) { + memcpy(t, a, element_size); + memcpy(a, b, element_size); + memcpy(b, t, element_size); + a += element_size; + b -= element_size; + } + if (t != tmp) CK_FREE(t); + return (void*)a_ptr; +} + +//-------------------------------------------------------------------------------------------------- +// Strings. + +char* ck_sfit(char* a, int n) +{ + afit(a, n + 1); + if (scount(a) == 0) apush(a, 0); + return a; +} + +char* ck_sset(char* a, const char* b) +{ + CK_ACANARY(a); + if (!b) return NULL; + int bsize = (int)strlen(b) + 1; + if (acap(a) < bsize) { + a = (char*)ck_agrow(a, bsize, 1); + } + memcpy(a, b, (size_t)bsize); + asetlen(a, bsize); + return a; +} + +char* ck_sfmt(char* s, const char* fmt, ...) +{ + CK_ACANARY(s); + va_list args; + va_start(args, fmt); + int n = 1 + vsnprintf(s, (size_t)scap(s), fmt, args); + va_end(args); + if (n > scap(s)) { + sfit(s, n); + va_start(args, fmt); + n = 1 + vsnprintf(s, (size_t)scap(s), fmt, args); + va_end(args); + } + asetlen(s, n); + return s; +} + +char* ck_sfmt_append(char* s, const char* fmt, ...) +{ + CK_ACANARY(s); + va_list args; + va_start(args, fmt); + int nul = !!s; + int capacity = scap(s) - scount(s); + int n = 1 + vsnprintf(s + slen(s), (size_t)(capacity > 0 ? capacity : 0), fmt, args); + va_end(args); + if (n > capacity) { + afit(s, n + scount(s)); + va_start(args, fmt); + int new_capacity = scap(s) - scount(s); + n = 1 + vsnprintf(s + slen(s), (size_t)new_capacity, fmt, args); + assert(n <= new_capacity); + va_end(args); + } + asetlen(s, asize(s) + n - nul); + return s; +} + +char* ck_svfmt(char* s, const char* fmt, va_list args) +{ + CK_ACANARY(s); + va_list copy_args; + va_copy(copy_args, args); + int n = 1 + vsnprintf(s, (size_t)scap(s), fmt, args); + if (n > scap(s)) { + sfit(s, n); + n = 1 + vsnprintf(s, (size_t)scap(s), fmt, copy_args); + } + va_end(copy_args); + asetlen(s, n); + return s; +} + +char* ck_svfmt_append(char* s, const char* fmt, va_list args) +{ + CK_ACANARY(s); + va_list copy_args; + va_copy(copy_args, args); + int nul = !!s; + int capacity = scap(s) - scount(s); + int n = 1 + vsnprintf(s + slen(s), (size_t)(capacity > 0 ? capacity : 0), fmt, copy_args); + va_end(copy_args); + if (n > capacity) { + afit(s, n + scount(s)); + int new_capacity = scap(s) - scount(s); + n = 1 + vsnprintf(s + slen(s), (size_t)new_capacity, fmt, args); + assert(n <= new_capacity); + } + asetlen(s, asize(s) + n - nul); + return s; +} + +int ck_sprefix(const char* s, const char* prefix) +{ + if (!s) return 0; + int s_len = (int)strlen(s); + int prefix_len = (int)strlen(prefix); + return s_len >= prefix_len && !memcmp(s, prefix, (size_t)prefix_len); +} + +int ck_ssuffix(const char* s, const char* suffix) +{ + if (!s) return 0; + int s_len = (int)strlen(s); + int suffix_len = (int)strlen(suffix); + return s_len >= suffix_len && !memcmp(s + s_len - suffix_len, suffix, (size_t)suffix_len); +} + +void ck_stoupper(char* s) +{ + if (!s) return; + for (int i = 0; i < slen(s); ++i) s[i] = (char)toupper((unsigned char)s[i]); +} + +void ck_stolower(char* s) +{ + if (!s) return; + for (int i = 0; i < slen(s); ++i) s[i] = (char)tolower((unsigned char)s[i]); +} + +char* ck_sappend(char* a, const char* b) +{ + CK_ACANARY(a); + int blen = (int)strlen(b); + if (blen <= 0) return a; + sfit(a, slen(a) + blen + 1); + memcpy(a + slen(a), b, (size_t)blen); + asetlen(a, asize(a) + blen); + a[slen(a)] = 0; + return a; +} + +char* ck_sappend_range(char* a, const char* b, const char* b_end) +{ + CK_ACANARY(a); + int blen = (int)(b_end - b); + if (blen <= 0) return a; + sfit(a, slen(a) + blen + 1); + memcpy(a + slen(a), b, (size_t)blen); + asetlen(a, asize(a) + blen); + a[slen(a)] = 0; + return a; +} + +char* ck_strim(char* s) +{ + CK_ACANARY(s); + int len = slen(s); + if (len <= 0) return s; + char* start = s; + char* end = s + len - 1; + while (start <= end && isspace((unsigned char)*start)) start++; + while (end >= start && isspace((unsigned char)*end)) end--; + int new_len = (int)(end >= start ? (end - start + 1) : 0); + if (new_len > 0) memmove(s, start, (size_t)new_len); + s[new_len] = 0; + asetlen(s, new_len + 1); + return s; +} + +char* ck_sltrim(char* s) +{ + CK_ACANARY(s); + int len = slen(s); + if (len <= 0) return s; + char* start = s; + char* end = s + len - 1; + while (start <= end && isspace((unsigned char)*start)) start++; + int new_len = (int)(end >= start ? (end - start + 1) : 0); + if (new_len > 0) memmove(s, start, (size_t)new_len); + s[new_len] = 0; + asetlen(s, new_len + 1); + return s; +} + +char* ck_srtrim(char* s) +{ + CK_ACANARY(s); + int len = slen(s); + if (len <= 0) return s; + char* end = s + len - 1; + while (end >= s && isspace((unsigned char)*end)) --end; + int new_len = (int)(end - s + 1); + if (new_len < 0) new_len = 0; + s[new_len] = 0; + asetlen(s, new_len + 1); + return s; +} + +char* ck_slpad(char* s, char pad, int count) +{ + CK_ACANARY(s); + sfit(s, scount(s) + count); + memmove(s + count, s, (size_t)scount(s)); + memset(s, pad, (size_t)count); + asetlen(s, asize(s) + count); + return s; +} + +char* ck_srpad(char* s, char pad, int count) +{ + CK_ACANARY(s); + sfit(s, scount(s) + count); + memset(s + slen(s), pad, (size_t)count); + asetlen(s, asize(s) + count); + s[slen(s)] = 0; + return s; +} + +char* ck_ssplit_once(char* s, char split_c) +{ + CK_ACANARY(s); + char* start = s; + char* end = s + slen(s); + while (start < end) { + if (*start == split_c) break; + ++start; + } + if (start == end) return NULL; // Delimiter not found. + int len = (int)(start - s); + char* split = NULL; + sfit(split, len + 1); + asetlen(split, len + 1); + memcpy(split, s, (size_t)len); + split[len] = 0; + int new_len = slen(s) - len - 1; + memmove(s, s + len + 1, (size_t)new_len); + asetlen(s, new_len + 1); + s[new_len] = 0; + return split; +} + +char** ck_ssplit(const char* s, char split_c) +{ + char* copy = NULL; + char** result = NULL; + char* split = NULL; + sset(copy, s); + while ((split = ssplit_once(copy, split_c))) { + apush(result, split); + } + apush(result, copy); + return result; +} + +int ck_sfirst_index_of(const char* s, char c) +{ + if (!s) return -1; + const char* p = strchr(s, c); + if (!p) return -1; + return (int)(p - s); +} + +int ck_slast_index_of(const char* s, char c) +{ + if (!s) return -1; + const char* p = strrchr(s, c); + if (!p) return -1; + return (int)(p - s); +} + +int ck_stoint(const char* s) +{ + char* end; + long long result = strtoll(s, &end, 10); + return (int)result; +} + +uint64_t ck_stouint(const char* s) +{ + char* end; + uint64_t result = (uint64_t)strtoll(s, &end, 10); + return result; +} + +float ck_stofloat(const char* s) +{ + char* end; + double result = strtod(s, &end); + return (float)result; +} + +double ck_stodouble(const char* s) +{ + char* end; + double result = strtod(s, &end); + return result; +} + +uint64_t ck_stohex(const char* s) +{ + if (!strncmp(s, "#", 1)) s += 1; + if (!strncmp(s, "0x", 2)) s += 2; + int len = (int)strlen(s); + if (len != 6 && len != 8) return 0; + char* end; + uint64_t result = (uint64_t)strtoll(s, &end, 16); + return len == 6 ? ((result << 8) | 0xFF) : result; +} + +char* ck_sreplace(char* s, const char* replace_me, const char* with_me) +{ + CK_ACANARY(s); + if (!s) return NULL; + size_t replace_len = strlen(replace_me); + if (replace_len == 0) return s; // Empty pattern: nothing to replace. + size_t with_len = strlen(with_me); + char* find; + char* search = s; + while ((find = strstr(search, replace_me))) { + int find_offset = (int)(find - s); + if (replace_len > with_len) { + int remaining = scount(s) - find_offset - (int)replace_len; + int diff = (int)(replace_len - with_len); + memcpy(find, with_me, with_len); + memmove(find + with_len, find + replace_len, (size_t)remaining); + asetlen(s, asize(s) - diff); + } else { + int remaining = scount(s) - find_offset - (int)replace_len; + int diff = (int)(with_len - replace_len); + sfit(s, scount(s) + diff); + find = s + find_offset; + memmove(find + with_len, find + replace_len, (size_t)remaining); + memcpy(find, with_me, with_len); + asetlen(s, asize(s) + diff); + } + search = find + with_len; + } + return s; +} + +char* ck_sdedup(char* s, int ch) +{ + CK_ACANARY(s); + if (!s) return NULL; + int len = (int)strlen(s); + if (len <= 1) return s; // Empty or single char: nothing to dedup. + int i = 0, j = 1; + int dup = 0; + while (j < len) { + if (s[i] == ch && s[j] == ch) { + dup = 1; + ++j; + } else { + ++i; + if (dup) s[i] = s[j]; + ++j; + } + } + s[i + 1] = 0; + asetlen(s, i + 2); + return s; +} + +char* ck_serase(char* s, int index, int count) +{ + CK_ACANARY(s); + if (!s) return NULL; + if (index < 0) { + count += index; + index = 0; + if (count <= 0) return s; + } + if (index >= slen(s)) return s; + if (index + count >= slen(s)) { + asetlen(s, index + 1); + s[index] = 0; + return s; + } else { + int remaining = scount(s) - (count + index); + memmove(s + index, s + count + index, (size_t)remaining); + asetlen(s, asize(s) - count); + } + return s; +} + +char* ck_spop(char* s) +{ + CK_ACANARY(s); + if (s && slen(s)) { asetlen(s, asize(s) - 1); s[slen(s)] = 0; } + return s; +} + +char* ck_spopn(char* s, int n) +{ + CK_ACANARY(s); + if (!s || n < 0) return s; + while (scount(s) > 1 && n--) asetlen(s, asize(s) - 1); + s[slen(s)] = 0; + return s; +} + +char* ck_sappend_UTF8(char* s, int codepoint) +{ + CK_ACANARY(s); + if (codepoint > 0x10FFFF) codepoint = 0xFFFD; +#define CK_EMIT(X, Y, Z) spush(s, (char)(X | ((codepoint >> Y) & Z))) + if (codepoint < 0x80) { CK_EMIT(0x00,0,0x7F); } + else if (codepoint < 0x800) { CK_EMIT(0xC0,6,0x1F); CK_EMIT(0x80, 0, 0x3F); } + else if (codepoint < 0x10000) { CK_EMIT(0xE0,12,0xF); CK_EMIT(0x80, 6, 0x3F); CK_EMIT(0x80, 0, 0x3F); } + else { CK_EMIT(0xF0,18,0x7); CK_EMIT(0x80, 12, 0x3F); CK_EMIT(0x80, 6, 0x3F); CK_EMIT(0x80, 0, 0x3F); } +#undef CK_EMIT + return s; +} + +const char* ck_decode_UTF8(const char* s, int* codepoint) +{ + unsigned char c = (unsigned char)*s++; + int extra = 0, min = 0; + *codepoint = 0; + if (c >= 0xF0) { *codepoint = c & 0x07; extra = 3; min = 0x10000; } + else if (c >= 0xE0) { *codepoint = c & 0x0F; extra = 2; min = 0x800; } + else if (c >= 0xC0) { *codepoint = c & 0x1F; extra = 1; min = 0x80; } + else if (c >= 0x80) { *codepoint = 0xFFFD; } + else *codepoint = c; + while (extra--) { + c = (unsigned char)*s++; + if ((c & 0xC0) != 0x80) { *codepoint = 0xFFFD; } + if (*codepoint != 0xFFFD) { *codepoint = ((*codepoint) << 6) | (c & 0x3F); } + } + if (*codepoint < min) *codepoint = 0xFFFD; + return s; +} + +//-------------------------------------------------------------------------------------------------- +// String path utilities. + +char* ck_spfname(const char* path) +{ + if (!path || path[0] == '\0') return NULL; + int at = slast_index_of(path, '/'); + const char* f = path + at + 1; + if (f[0] != '\0') return smake(f); + return NULL; +} + +char* ck_spfname_no_ext(const char* path) +{ + char* s = ck_spfname(path); + if (!s) return NULL; + int at = slast_index_of(s, '.'); + if (at == 0) { sfree(s); return NULL; } + if (at == -1) return s; + serase(s, at, slen(s) - at); + return s; +} + +char* ck_spext(const char* path) +{ + int at = slast_index_of(path, '.'); + if (at == -1 || path[at + 1] == 0 || path[at + 1] == '/') return NULL; + return smake(path + at); +} + +int ck_spext_equ(const char* path, const char* ext) +{ + int at = slast_index_of(path, '.'); + if (at == -1 || path[at + 1] == 0 || path[at + 1] == '/') return 0; + return sequ(path + at, ext); +} + +char* ck_sppop(const char* path) +{ + char* s = sdup(path); + if (slast(s) == '/') spop(s); + int at = slast_index_of(s, '/'); + if (at == -1 || at == 0) return (sset(s, "/"), s); + serase(s, at, slen(s) - at); + return s; +} + +char* ck_sppopn(const char* path, int n) +{ + char* s = sdup(path); + while (n--) { + if (slast(s) == '/') spop(s); + int at = slast_index_of(s, '/'); + if (at == -1 || at == 0) { sset(s, "/"); continue; } + serase(s, at, slen(s) - at); + } + return s; +} + +char* ck_spcompact(const char* path, int n) +{ + int len = (int)strlen(path); + if (n <= 6) return NULL; + if (len < n) return sdup(path); + int at = slast_index_of(path, '/'); + if (at == -1 || at == 0) { + char* s = sdup(path); + serase(s, n, slen(s) - n); + serase(s, n - 3, 3); + sappend(s, "..."); + return s; + } + int remaining = len - at - 1; + if (remaining >= n - 3) { + char* s = smake("..."); + sappend_range(s, path, path + at - 6); + sappend(s, "..."); + return s; + } else { + char* s = sdup(path); + int len_s = slen(s); + int to_erase = len_s - (remaining - 3); + serase(s, remaining - 3, to_erase); + sappend(s, "..."); + sappend(s, path + at); + return s; + } +} + +char* ck_spdir_of(const char* path) +{ + if (!*path || (*path == '.' && (int)strlen(path) < 3)) return NULL; + if (sequ(path, "../")) return NULL; + if (sequ(path, "/")) return NULL; + int at = slast_index_of(path, '/'); + if (at == -1) return NULL; + if (at == 0) return smake("/"); + char* s = smake(path); + serase(s, at, slen(s) - at); + at = slast_index_of(s, '/'); + if (at == -1) { + if (slen(s) == 2) { + return s; + } else { + s[0] = '/'; + return s; + } + } + serase(s, 0, at); + return s; +} + +char* ck_sptop_of(const char* path) +{ + int at = sfirst_index_of(path, '/'); + if (at == -1) return NULL; + int next = sfirst_index_of(path + at + 1, '/'); + if (next == -1) return smake("/"); + char* s = sdup(path + at); + serase(s, next + 1, slen(s) - (next + 1)); + return s; +} + +char* ck_spnorm(const char* path) +{ + char* result = NULL; + int len = (int)strlen(path); + if (*path != '\\' && *path != '/') { + int windows_drive = len >= 2 && path[1] == ':'; + if (!windows_drive) { + spush(result, '/'); + } + } + int prev_was_dot = 0; + int prev_was_dotdot = 0; + for (int i = 0; i < len; ++i) { + char c = path[i]; + if (c == '\\' || c == '/') { + if (!prev_was_dot) { + spush(result, '/'); + } else if (prev_was_dotdot) { + char* tmp = ck_sppop(result); + sfree(result); + result = tmp; + spush(result, '/'); + } + prev_was_dot = 0; + prev_was_dotdot = 0; + } else if (c == '.') { + if (prev_was_dot) prev_was_dotdot = 1; + prev_was_dot = 1; + } else { + if (prev_was_dot) spush(result, '.'); + spush(result, c); + prev_was_dot = 0; + prev_was_dotdot = 0; + } + } + sreplace(result, "//", "/"); + if (slen(result) > 1 && slast(result) == '/') spop(result); + return result; +} + +//-------------------------------------------------------------------------------------------------- +// Map implementation (originally by Mattias Gustavsson) - Stretchy buffer style. + +uint64_t ck_map_hash(uint64_t x) +{ + x += 0x9e3779b97f4a7c15ull; + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ull; + x = (x ^ (x >> 27)) * 0x94d049bb133111ebull; + x ^= (x >> 31); + return x ? x : 1ull; +} + +void ck_map_zero_slots(CK_MapHeader* hdr) +{ + CK_MapSlot* slots = ck_map_slots_ptr(hdr); + for (int i = 0; i < hdr->slot_capacity; ++i) { + slots[i].h = 0; + slots[i].item_index = -1; + slots[i].base_count = 0; + } + hdr->slot_count = 0; +} + +// Total allocation size for map with given capacities. +size_t ck_map_alloc_size(int capacity, int val_size, int slot_capacity) +{ + size_t items_end = sizeof(CK_MapHeader) + CK_ALIGN8((size_t)capacity * val_size); + size_t keys_end = items_end + (size_t)capacity * sizeof(uint64_t); + size_t islot_end = keys_end + (size_t)capacity * sizeof(int); + size_t slots_start = CK_ALIGN8(islot_end); + return slots_start + (size_t)slot_capacity * sizeof(CK_MapSlot); +} + +int ck_map_find_insertion_slot(CK_MapHeader* hdr, uint64_t h) +{ + CK_MapSlot* slots = ck_map_slots_ptr(hdr); + int mask = hdr->slot_capacity - 1; + int base = (int)(h & (uint64_t)mask); + int slot = base, first_free = base; + int remaining = slots[base].base_count; + + while (remaining > 0) { + if (slots[slot].item_index < 0 && slots[first_free].item_index >= 0) + first_free = slot; + uint64_t sh = slots[slot].h; + if (sh) { + if (((int)(sh & (uint64_t)mask)) == base) --remaining; + } + slot = (slot + 1) & mask; + } + + slot = first_free; + while (slots[slot].item_index >= 0) { + slot = (slot + 1) & mask; + } + return slot; +} + +int ck_map_find_slot(const CK_MapHeader* hdr, uint64_t key, uint64_t h) +{ + if (hdr->slot_capacity == 0) return -1; + CK_MapSlot* slots = ck_map_slots_ptr((CK_MapHeader*)hdr); + uint64_t* keys = ck_map_keys_ptr((CK_MapHeader*)hdr); + int mask = hdr->slot_capacity - 1; + int base = (int)(h & (uint64_t)mask); + int slot = base; + int remaining = slots[base].base_count; + while (remaining > 0) { + int item = slots[slot].item_index; + if (item >= 0) { + uint64_t sh = slots[slot].h; + if (((int)(sh & (uint64_t)mask)) == base) { + --remaining; + if (sh == h && keys[item] == key) return slot; + } + } + slot = (slot + 1) & mask; + } + return -1; +} + +// Rebuild hash table after reallocation. Assumes slots are already zeroed. +void ck_map_rebuild_slots(CK_MapHeader* hdr) +{ + if (hdr->slot_capacity == 0) return; + CK_MapSlot* slots = ck_map_slots_ptr(hdr); + uint64_t* keys = ck_map_keys_ptr(hdr); + int* islot = ck_map_islot_ptr(hdr); + for (int idx = 0; idx < hdr->size; ++idx) { + uint64_t h = ck_map_hash(keys[idx]); + int slot = ck_map_find_insertion_slot(hdr, h); + int base = (int)(h & (uint64_t)(hdr->slot_capacity - 1)); + slots[slot].h = h; + slots[slot].item_index = idx; + ++slots[base].base_count; + islot[idx] = slot; + ++hdr->slot_count; + } +} + +// Ensure we have capacity for 'want' items. May reallocate and update m_ptr. +// Returns the possibly-updated header pointer. +// Single allocation: [Header][items][keys][islot][slots] +CK_MapHeader* ck_map_ensure_capacity(void** m_ptr, int want_items, int val_size) +{ + CK_MapHeader* hdr = *m_ptr ? (CK_MapHeader*)((char*)*m_ptr - sizeof(CK_MapHeader)) : NULL; + int old_item_cap = hdr ? hdr->capacity : 0; + int old_slot_cap = hdr ? hdr->slot_capacity : 0; + int old_size = hdr ? hdr->size : 0; + + // Compute new item capacity. + int new_item_cap = old_item_cap; + if (want_items > new_item_cap) { + new_item_cap = old_item_cap ? old_item_cap * 2 : 16; + while (new_item_cap < want_items) new_item_cap *= 2; + } + + // Compute new slot capacity. Slots should be at least 2x items for good hash distribution. + int new_slot_cap = old_slot_cap; + int min_slot_cap = new_item_cap * 2; + if (min_slot_cap < 16) min_slot_cap = 16; + if (new_slot_cap < min_slot_cap) { + new_slot_cap = 16; + while (new_slot_cap < min_slot_cap) new_slot_cap *= 2; + } + + // Also check load factor on existing slots. + if (hdr && hdr->slot_capacity > 0) { + int thresh = hdr->slot_capacity - (hdr->slot_capacity >> 2); // 75% + if (hdr->slot_count >= thresh) { + int grown = hdr->slot_capacity * 2; + if (grown > new_slot_cap) new_slot_cap = grown; + } + } + + // If no change needed, return current. + if (new_item_cap == old_item_cap && new_slot_cap == old_slot_cap) { + return hdr; + } + + // Allocate new block. + size_t new_size = ck_map_alloc_size(new_item_cap, val_size, new_slot_cap); + CK_MapHeader* new_hdr = (CK_MapHeader*)CK_ALLOC(new_size); + memset(new_hdr, 0, new_size); + + // Initialize header. + new_hdr->cookie.val = CK_MAP_COOKIE; + new_hdr->val_size = val_size; + new_hdr->size = old_size; + new_hdr->capacity = new_item_cap; + new_hdr->slot_count = 0; + new_hdr->slot_capacity = new_slot_cap; + + // Copy existing data if any. + if (hdr && old_size > 0) { + memcpy(ck_map_items_ptr(new_hdr), ck_map_items_ptr(hdr), (size_t)old_size * val_size); + memcpy(ck_map_keys_ptr(new_hdr), ck_map_keys_ptr(hdr), (size_t)old_size * sizeof(uint64_t)); + } + + // Initialize islot to -1. + int* new_islot = ck_map_islot_ptr(new_hdr); + for (int i = 0; i < new_item_cap; i++) new_islot[i] = -1; + + // Initialize slots item_index to -1 (0 would appear occupied). + CK_MapSlot* new_slots = ck_map_slots_ptr(new_hdr); + for (int i = 0; i < new_slot_cap; i++) new_slots[i].item_index = -1; + + // Rebuild hash table. + ck_map_rebuild_slots(new_hdr); + + // Free old allocation. + if (hdr) CK_FREE(hdr); + + // Update user pointer. + *m_ptr = ck_map_items_ptr(new_hdr); + return new_hdr; +} + +int ck_map_find_impl(CK_MapHeader* hdr, uint64_t key) +{ + if (!hdr || hdr->size == 0 || hdr->slot_capacity == 0) return -1; + uint64_t h = ck_map_hash(key); + int s = ck_map_find_slot(hdr, key, h); + if (s < 0) return -1; + CK_MapSlot* slots = ck_map_slots_ptr(hdr); + return slots[s].item_index; +} + +void ck_map_set_stretchy(void** m_ptr, uint64_t key, const void* val, int val_size) +{ + CK_MapHeader* hdr = *m_ptr ? (CK_MapHeader*)((char*)*m_ptr - sizeof(CK_MapHeader)) : NULL; + uint64_t h = ck_map_hash(key); + + // Update if entry already exists. + if (hdr && hdr->slot_capacity) { + int s = ck_map_find_slot(hdr, key, h); + if (s >= 0) { + CK_MapSlot* slots = ck_map_slots_ptr(hdr); + int idx = slots[s].item_index; + char* items = (char*)ck_map_items_ptr(hdr); + memcpy(items + idx * hdr->val_size, val, (size_t)hdr->val_size); + return; + } + } + + // Ensure capacity (may reallocate and rebuild hash table). + int want = (hdr ? hdr->size : 0) + 1; + hdr = ck_map_ensure_capacity(m_ptr, want, val_size); + assert(hdr->val_size == val_size); + + // Get array pointers (may have changed after realloc). + char* items = (char*)ck_map_items_ptr(hdr); + uint64_t* keys = ck_map_keys_ptr(hdr); + int* islot = ck_map_islot_ptr(hdr); + CK_MapSlot* slots = ck_map_slots_ptr(hdr); + + // Append to dense set. + int idx = hdr->size++; + keys[idx] = key; + memcpy(items + idx * hdr->val_size, val, (size_t)hdr->val_size); + + // Insert into hash slots. + int slot = ck_map_find_insertion_slot(hdr, h); + int base = (int)(h & (uint64_t)(hdr->slot_capacity - 1)); + slots[slot].h = h; + slots[slot].item_index = idx; + ++slots[base].base_count; + islot[idx] = slot; + ++hdr->slot_count; +} + +void* ck_map_get_ptr_impl(CK_MapHeader* hdr, uint64_t key) +{ + if (!hdr || hdr->size == 0 || hdr->slot_capacity == 0) return NULL; + uint64_t h = ck_map_hash(key); + int s = ck_map_find_slot(hdr, key, h); + if (s < 0) return NULL; + CK_MapSlot* slots = ck_map_slots_ptr(hdr); + return (char*)ck_map_items_ptr(hdr) + slots[s].item_index * hdr->val_size; +} + +int ck_map_del_impl(CK_MapHeader* hdr, uint64_t key) +{ + if (!hdr || hdr->size == 0 || hdr->slot_capacity == 0) return 0; + uint64_t h = ck_map_hash(key); + int s = ck_map_find_slot(hdr, key, h); + if (s < 0) return 0; + + CK_MapSlot* slots = ck_map_slots_ptr(hdr); + uint64_t* keys = ck_map_keys_ptr(hdr); + int* islot = ck_map_islot_ptr(hdr); + char* items = (char*)ck_map_items_ptr(hdr); + + int mask = hdr->slot_capacity - 1; + int base = (int)(h & (uint64_t)mask); + int idx = slots[s].item_index; + int last = hdr->size - 1; + + --slots[base].base_count; + slots[s].item_index = -1; + --hdr->slot_count; + + if (idx != last) { + keys[idx] = keys[last]; + memcpy(items + idx * hdr->val_size, items + last * hdr->val_size, (size_t)hdr->val_size); + int ms = islot[last]; + slots[ms].item_index = idx; + islot[idx] = ms; + } + islot[last] = -1; + --hdr->size; + return 1; +} + +void ck_map_swap_impl(CK_MapHeader* hdr, int i, int j) +{ + if (i == j) return; + assert(i >= 0 && i < hdr->size); + assert(j >= 0 && j < hdr->size); + + uint64_t* keys = ck_map_keys_ptr(hdr); + int* islot = ck_map_islot_ptr(hdr); + CK_MapSlot* slots = ck_map_slots_ptr(hdr); + char* items = (char*)ck_map_items_ptr(hdr); + + // Swap keys. + uint64_t tk = keys[i]; + keys[i] = keys[j]; + keys[j] = tk; + + // Swap values. + char tmp[256]; + char* t = hdr->val_size <= (int)sizeof(tmp) ? tmp : (char*)CK_ALLOC((size_t)hdr->val_size); + memcpy(t, items + i * hdr->val_size, (size_t)hdr->val_size); + memcpy(items + i * hdr->val_size, items + j * hdr->val_size, (size_t)hdr->val_size); + memcpy(items + j * hdr->val_size, t, (size_t)hdr->val_size); + if (t != tmp) CK_FREE(t); + + // Swap islot backpointers. + int si = islot[i]; + int sj = islot[j]; + if (si >= 0) slots[si].item_index = j; + if (sj >= 0) slots[sj].item_index = i; + islot[i] = sj; + islot[j] = si; +} + +void ck_map_sort_range(CK_MapHeader* hdr, int offset, int count, int (*cmp)(const void* a, const void* b)) +{ + if (count <= 1) return; + char* items = (char*)ck_map_items_ptr(hdr); + char* pivot = items + (offset + count - 1) * hdr->val_size; + int lo = 0; + for (int hi = 0; hi < count - 1; ++hi) { + char* hi_val = items + (offset + hi) * hdr->val_size; + if (cmp(hi_val, pivot) < 0) { + ck_map_swap_impl(hdr, offset + lo, offset + hi); + ++lo; + } + } + ck_map_swap_impl(hdr, offset + (count - 1), offset + lo); + ck_map_sort_range(hdr, offset, lo, cmp); + ck_map_sort_range(hdr, offset + lo + 1, count - 1 - lo, cmp); +} + +void ck_map_sort_impl(CK_MapHeader* hdr, int (*cmp)(const void* a, const void* b)) +{ + ck_map_sort_range(hdr, 0, hdr->size, cmp); +} + +void ck_map_ssort_range(CK_MapHeader* hdr, int offset, int count, int ignore_case) +{ + if (count <= 1) return; + uint64_t* keys = ck_map_keys_ptr(hdr); + + #define CK_SSORT_KEY(i) (keys[(offset) + (i)]) + + uint64_t pivot = CK_SSORT_KEY(count - 1); + int lo = 0; + for (int hi = 0; hi < count - 1; ++hi) { + const char* hi_key = (const char*)CK_SSORT_KEY(hi); + const char* pivot_key = (const char*)pivot; + int cmp = ignore_case ? ck_stricmp(hi_key, pivot_key) : strcmp(hi_key, pivot_key); + if (cmp < 0) { + ck_map_swap_impl(hdr, offset + lo, offset + hi); + ++lo; + } + } + ck_map_swap_impl(hdr, offset + (count - 1), offset + lo); + ck_map_ssort_range(hdr, offset, lo, ignore_case); + ck_map_ssort_range(hdr, offset + lo + 1, count - 1 - lo, ignore_case); + + #undef CK_SSORT_KEY +} + +void ck_map_ssort_impl(CK_MapHeader* hdr, int ignore_case) +{ + ck_map_ssort_range(hdr, 0, hdr->size, ignore_case); +} + +void ck_map_free_impl(CK_MapHeader* hdr) +{ + CK_FREE(hdr); // Single allocation. +} + +void ck_map_clear_impl(CK_MapHeader* hdr) +{ + hdr->size = 0; + if (hdr->slot_capacity) ck_map_zero_slots(hdr); +} + +//-------------------------------------------------------------------------------------------------- +// String interning (originally from Per Vognsen). + +uint64_t ck_hash_fnv1a(const void* ptr, size_t sz) +{ + uint64_t x = 0xcbf29ce484222325ull; + const char* buf = (const char*)ptr; + for (size_t i = 0; i < sz; i++) { + x ^= (uint64_t)(unsigned char)buf[i]; + x *= 0x100000001b3ull; + x ^= x >> 32; + } + return x; +} + +// C11 atomics for C, std::atomic for C++ +// Must close extern "C" before including since it contains C++ templates. +#ifdef __cplusplus +} // extern "C" +#include +extern "C" { +#define CK_ATOMIC(T) std::atomic +#define ck_atomic_load(p) (p)->load() +#define ck_atomic_store(p, v) (p)->store(v) +#define ck_atomic_compare_exchange_strong(p, expected, desired) (p)->compare_exchange_strong(*(expected), (desired)) +#define ck_atomic_compare_exchange_weak(p, expected, desired) (p)->compare_exchange_weak(*(expected), (desired)) +#else +#include +#define CK_ATOMIC(T) _Atomic(T) +#define ck_atomic_load(p) atomic_load(p) +#define ck_atomic_store(p, v) atomic_store(p, v) +#define ck_atomic_compare_exchange_strong(p, expected, desired) atomic_compare_exchange_strong(p, expected, desired) +#define ck_atomic_compare_exchange_weak(p, expected, desired) atomic_compare_exchange_weak(p, expected, desired) +#endif + +typedef struct CK_InternTable +{ + CK_MAP(CK_UniqueString*) interns; + CK_ATOMIC(int) lock; +} CK_InternTable; + +static CK_ATOMIC(CK_InternTable*) g_intern_table; +static int g_sintern_gen; + +int ck_sintern_gen() { return g_sintern_gen; } + +static CK_InternTable* ck_sintern_get_table() +{ + CK_InternTable* table = ck_atomic_load(&g_intern_table); + if (!table) { + CK_InternTable* new_table = (CK_InternTable*)CK_ALLOC(sizeof(CK_InternTable)); + memset(new_table, 0, sizeof(CK_InternTable)); + CK_InternTable* expected = NULL; + if (ck_atomic_compare_exchange_strong(&g_intern_table, &expected, new_table)) { + table = new_table; + } else { + CK_FREE(new_table); + table = expected; + } + } + return table; +} + +static void ck_sintern_lock(CK_InternTable* table) +{ + int expected = 0; + while (!ck_atomic_compare_exchange_weak(&table->lock, &expected, 1)) { + expected = 0; + } +} + +static void ck_sintern_unlock(CK_InternTable* table) +{ + ck_atomic_store(&table->lock, 0); +} + +const char* ck_sintern_range(const char* start, const char* end) +{ + CK_InternTable* table = ck_sintern_get_table(); + size_t len = (size_t)(end - start); + uint64_t key = ck_hash_fnv1a((void*)start, len); + + ck_sintern_lock(table); + + CK_UniqueString* head = map_get(table->interns, key); + for (CK_UniqueString* it = head; it; it = it->next) { + if ((size_t)it->len == len && memcmp(it->str, start, len) == 0) { + ck_sintern_unlock(table); + return it->str; + } + } + + size_t bytes = sizeof(CK_UniqueString) + len + 1; + CK_UniqueString* node = (CK_UniqueString*)CK_ALLOC(bytes); + node->cookie.val = CK_INTERN_COOKIE; + node->len = (int)len; + node->next = head; + node->str = (char*)(node + 1); + memcpy(node->str, start, len); + node->str[len] = '\0'; + map_set(table->interns, key, node); + + ck_sintern_unlock(table); + return node->str; +} + +void sintern_nuke() +{ + g_sintern_gen++; + CK_InternTable* table = ck_atomic_load(&g_intern_table); + if (!table) return; + + ck_sintern_lock(table); + ck_atomic_store(&g_intern_table, (CK_InternTable*)NULL); + + for (int i = 0; i < map_size(table->interns); ++i) { + CK_UniqueString* it = map_items(table->interns)[i]; + while (it) { + CK_UniqueString* next = it->next; + CK_FREE(it); + it = next; + } + } + map_free(table->interns); + ck_sintern_unlock(table); + CK_FREE(table); +} + +const uint16_t* ck_decode_UTF16(const uint16_t* s, int* codepoint) +{ + int W1 = *s++; + if (W1 < 0xD800 || W1 > 0xDFFF) { + *codepoint = W1; + } else if (W1 > 0xD800 && W1 < 0xDBFF) { + int W2 = *s++; + if (W2 > 0xDC00 && W2 < 0xDFFF) *codepoint = 0x10000 + (((W1 & 0x03FF) << 10) | (W2 & 0x03FF)); + else *codepoint = 0xFFFD; + } else *codepoint = 0xFFFD; + return s; +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CKIT_IMPLEMENTATION_GUARD +#endif // CKIT_IMPLEMENTATION diff --git a/libraries/cute/cute_aseprite.h b/libraries/cute/cute_aseprite.h index 216f38088..910091a9d 100644 --- a/libraries/cute/cute_aseprite.h +++ b/libraries/cute/cute_aseprite.h @@ -19,12 +19,8 @@ LIMITATIONS - Only the "normal" blend mode for layers is supported. As a workaround try - using the "merge down" function in Aseprite to create a normal layer. - Supporting all blend modes would take too much code to be worth it. - Does not support very old versions of Aseprite (with old palette chunks - 0x0004 or 0x0011). Also does not support deprecrated mask chunk. + 0x0011). Also does not support deprecrated mask chunk. sRGB and ICC profiles are parsed but completely ignored when blending frames together. If you want these to be used when composing frames you @@ -80,10 +76,9 @@ animation, formed by blending all the cels of an animation together. There is one cel per layer per frame. Each cel contains its own pixel data. - The frame's pixels are automatically assumed to have been blended by the `normal` - blend mode. A warning is emit if any other blend mode is encountered. Feel free - to update the pixels of each frame with your own implementation of blending - functions. The frame's pixels are merely provided like this for convenience. + The frame's pixels are composited using each layer's blend mode, including + all 19 Aseprite blend modes. Group layers with valid blend mode/opacity + (header flag bit 2) are composited separately then merged into their parent. BUGS AND CRASHES @@ -98,19 +93,21 @@ #ifndef CUTE_ASEPRITE_H #define CUTE_ASEPRITE_H +#include + typedef struct ase_t ase_t; +typedef struct ase_color_t ase_color_t; ase_t* cute_aseprite_load_from_file(const char* path, void* mem_ctx); ase_t* cute_aseprite_load_from_memory(const void* memory, int size, void* mem_ctx); void cute_aseprite_free(ase_t* aseprite); +void cute_aseprite_blend_layers(ase_t* ase, uint64_t layer_mask, ase_color_t** out_pixels); #define CUTE_ASEPRITE_MAX_LAYERS (64) #define CUTE_ASEPRITE_MAX_SLICES (128) #define CUTE_ASEPRITE_MAX_PALETTE_ENTRIES (1024) #define CUTE_ASEPRITE_MAX_TAGS (256) -#include - typedef struct ase_color_t ase_color_t; typedef struct ase_frame_t ase_frame_t; typedef struct ase_layer_t ase_layer_t; @@ -154,6 +151,29 @@ typedef enum ase_layer_flags_t ASE_LAYER_FLAGS_REFERENCE = 0x40, } ase_layer_flags_t; +typedef enum ase_blend_mode_t +{ + ASE_BLEND_MODE_NORMAL, + ASE_BLEND_MODE_MULTIPLY, + ASE_BLEND_MODE_SCREEN, + ASE_BLEND_MODE_OVERLAY, + ASE_BLEND_MODE_DARKEN, + ASE_BLEND_MODE_LIGHTEN, + ASE_BLEND_MODE_COLOR_DODGE, + ASE_BLEND_MODE_COLOR_BURN, + ASE_BLEND_MODE_HARD_LIGHT, + ASE_BLEND_MODE_SOFT_LIGHT, + ASE_BLEND_MODE_DIFFERENCE, + ASE_BLEND_MODE_EXCLUSION, + ASE_BLEND_MODE_HSL_HUE, + ASE_BLEND_MODE_HSL_SATURATION, + ASE_BLEND_MODE_HSL_COLOR, + ASE_BLEND_MODE_HSL_LUMINOSITY, + ASE_BLEND_MODE_ADDITION, + ASE_BLEND_MODE_SUBTRACT, + ASE_BLEND_MODE_DIVIDE, +} ase_blend_mode_t; + typedef enum ase_layer_type_t { ASE_LAYER_TYPE_NORMAL, @@ -164,6 +184,7 @@ struct ase_layer_t { ase_layer_flags_t flags; ase_layer_type_t type; + ase_blend_mode_t blend_mode; const char* name; ase_layer_t* parent; float opacity; @@ -288,6 +309,7 @@ struct ase_t int grid_w; int grid_h; int has_color_profile; + int valid_group_blend; ase_color_profile_t color_profile; ase_palette_t palette; @@ -344,6 +366,8 @@ struct ase_t #define CUTE_ASEPRITE_MEMSET memset #endif +#include // sqrt (used by soft_light blend mode) + #if !defined(CUTE_ASEPRITE_ASSERT) #include #define CUTE_ASEPRITE_ASSERT assert @@ -866,38 +890,234 @@ ase_t* cute_aseprite_load_from_file(const char* path, void* mem_ctx) return aseprite; } +static int s_min(int a, int b) +{ + return a < b ? a : b; +} + +static int s_max(int a, int b) +{ + return a < b ? b : a; +} + static int s_mul_un8(int a, int b) { int t = (a * b) + 0x80; return (((t >> 8) + t) >> 8); } -static ase_color_t s_blend(ase_color_t src, ase_color_t dst, uint8_t opacity) +// Per-channel blend helpers. Reference: Aseprite blend_funcs.cpp + +static int s_blend_multiply(int b, int s) { return s_mul_un8(b, s); } +static int s_blend_screen(int b, int s) { return b + s - s_mul_un8(b, s); } +static int s_blend_overlay(int b, int s) { return b < 128 ? s_mul_un8(b, s << 1) : s_blend_screen(b, (s << 1) - 255); } +static int s_blend_darken(int b, int s) { return s_min(b, s); } +static int s_blend_lighten(int b, int s) { return s_max(b, s); } + +static int s_blend_color_dodge(int b, int s) +{ + if (b == 0) return 0; + s = 255 - s; + if (b >= s) return 255; + return (b * 255) / s; +} + +static int s_blend_color_burn(int b, int s) +{ + if (b == 255) return 255; + b = 255 - b; + if (b >= s) return 0; + return 255 - (b * 255) / s; +} + +static int s_blend_hard_light(int b, int s) { return s < 128 ? s_mul_un8(b, s << 1) : s_blend_screen(b, (s << 1) - 255); } + +static int s_blend_soft_light(int b, int s) +{ + double t = (double)s / 255.0; + double r; + if (t < 0.5) { + r = (double)b / 255.0; + r = r - (1.0 - 2.0 * t) * r * (1.0 - r); + } else { + double d; + r = (double)b / 255.0; + if (r < 0.25) + d = ((16.0 * r - 12.0) * r + 4.0) * r; + else + d = sqrt(r); + r = r + (2.0 * t - 1.0) * (d - r); + } + return (int)(r * 255.0 + 0.5); +} + +static int s_blend_difference(int b, int s) { return b > s ? b - s : s - b; } +static int s_blend_exclusion(int b, int s) { return b + s - 2 * s_mul_un8(b, s); } +static int s_blend_addition(int b, int s) { return s_min(b + s, 255); } +static int s_blend_subtract(int b, int s) { return s_max(b - s, 0); } + +static int s_blend_divide(int b, int s) +{ + if (b == 0) return 0; + if (b >= s) return 255; + return (b * 255) / s; +} + +// HSL helpers (double precision, matching Aseprite). + +static double s_lum(double r, double g, double b) { return 0.3 * r + 0.59 * g + 0.11 * b; } + +static double s_sat(double r, double g, double b) +{ + double mx = r > g ? (r > b ? r : b) : (g > b ? g : b); + double mn = r < g ? (r < b ? r : b) : (g < b ? g : b); + return mx - mn; +} + +static void s_clip_color(double* r, double* g, double* b) +{ + double l = s_lum(*r, *g, *b); + double mn = *r < *g ? (*r < *b ? *r : *b) : (*g < *b ? *g : *b); + double mx = *r > *g ? (*r > *b ? *r : *b) : (*g > *b ? *g : *b); + if (mn < 0.0) { + double d = l - mn; + if (d > 0.0001) { + *r = l + (*r - l) * l / d; + *g = l + (*g - l) * l / d; + *b = l + (*b - l) * l / d; + } else { + *r = *g = *b = l; + } + } + if (mx > 1.0) { + double d = mx - l; + if (d > 0.0001) { + *r = l + (*r - l) * (1.0 - l) / d; + *g = l + (*g - l) * (1.0 - l) / d; + *b = l + (*b - l) * (1.0 - l) / d; + } else { + *r = *g = *b = l; + } + } +} + +static void s_set_lum(double* r, double* g, double* b, double l) +{ + double d = l - s_lum(*r, *g, *b); + *r += d; + *g += d; + *b += d; + s_clip_color(r, g, b); +} + +static void s_set_sat(double* r, double* g, double* b, double s) +{ + // Identify min/mid/max pointers. + double *mn, *mid, *mx; + if (*r <= *g) { + if (*g <= *b) { mn = r; mid = g; mx = b; } + else if (*r <= *b) { mn = r; mid = b; mx = g; } + else { mn = b; mid = r; mx = g; } + } else { + if (*r <= *b) { mn = g; mid = r; mx = b; } + else if (*g <= *b) { mn = g; mid = b; mx = r; } + else { mn = b; mid = g; mx = r; } + } + if (*mx > *mn) { + *mid = ((*mid - *mn) * s) / (*mx - *mn); + *mx = s; + } else { + *mid = *mx = 0.0; + } + *mn = 0.0; +} + +// Apply per-channel blend mode to src/dst RGB, then composite with Porter-Duff over. +static ase_color_t s_blend(ase_color_t src, ase_color_t dst, uint8_t opacity, ase_blend_mode_t mode) { src.a = (uint8_t)s_mul_un8(src.a, opacity); + + if (mode == ASE_BLEND_MODE_NORMAL || dst.a == 0) { + int a = src.a + dst.a - s_mul_un8(src.a, dst.a); + int r, g, b; + if (a == 0) { + r = g = b = 0; + } else { + r = dst.r + (src.r - dst.r) * src.a / a; + g = dst.g + (src.g - dst.g) * src.a / a; + b = dst.b + (src.b - dst.b) * src.a / a; + } + ase_color_t ret = { (uint8_t)r, (uint8_t)g, (uint8_t)b, (uint8_t)a }; + return ret; + } + + // Compute blended RGB. + int br, bg, bb; + + switch (mode) { + case ASE_BLEND_MODE_MULTIPLY: br = s_blend_multiply(dst.r, src.r); bg = s_blend_multiply(dst.g, src.g); bb = s_blend_multiply(dst.b, src.b); break; + case ASE_BLEND_MODE_SCREEN: br = s_blend_screen(dst.r, src.r); bg = s_blend_screen(dst.g, src.g); bb = s_blend_screen(dst.b, src.b); break; + case ASE_BLEND_MODE_OVERLAY: br = s_blend_overlay(dst.r, src.r); bg = s_blend_overlay(dst.g, src.g); bb = s_blend_overlay(dst.b, src.b); break; + case ASE_BLEND_MODE_DARKEN: br = s_blend_darken(dst.r, src.r); bg = s_blend_darken(dst.g, src.g); bb = s_blend_darken(dst.b, src.b); break; + case ASE_BLEND_MODE_LIGHTEN: br = s_blend_lighten(dst.r, src.r); bg = s_blend_lighten(dst.g, src.g); bb = s_blend_lighten(dst.b, src.b); break; + case ASE_BLEND_MODE_COLOR_DODGE: br = s_blend_color_dodge(dst.r, src.r); bg = s_blend_color_dodge(dst.g, src.g); bb = s_blend_color_dodge(dst.b, src.b); break; + case ASE_BLEND_MODE_COLOR_BURN: br = s_blend_color_burn(dst.r, src.r); bg = s_blend_color_burn(dst.g, src.g); bb = s_blend_color_burn(dst.b, src.b); break; + case ASE_BLEND_MODE_HARD_LIGHT: br = s_blend_hard_light(dst.r, src.r); bg = s_blend_hard_light(dst.g, src.g); bb = s_blend_hard_light(dst.b, src.b); break; + case ASE_BLEND_MODE_SOFT_LIGHT: br = s_blend_soft_light(dst.r, src.r); bg = s_blend_soft_light(dst.g, src.g); bb = s_blend_soft_light(dst.b, src.b); break; + case ASE_BLEND_MODE_DIFFERENCE: br = s_blend_difference(dst.r, src.r); bg = s_blend_difference(dst.g, src.g); bb = s_blend_difference(dst.b, src.b); break; + case ASE_BLEND_MODE_EXCLUSION: br = s_blend_exclusion(dst.r, src.r); bg = s_blend_exclusion(dst.g, src.g); bb = s_blend_exclusion(dst.b, src.b); break; + case ASE_BLEND_MODE_ADDITION: br = s_blend_addition(dst.r, src.r); bg = s_blend_addition(dst.g, src.g); bb = s_blend_addition(dst.b, src.b); break; + case ASE_BLEND_MODE_SUBTRACT: br = s_blend_subtract(dst.r, src.r); bg = s_blend_subtract(dst.g, src.g); bb = s_blend_subtract(dst.b, src.b); break; + case ASE_BLEND_MODE_DIVIDE: br = s_blend_divide(dst.r, src.r); bg = s_blend_divide(dst.g, src.g); bb = s_blend_divide(dst.b, src.b); break; + case ASE_BLEND_MODE_HSL_HUE: + case ASE_BLEND_MODE_HSL_SATURATION: + case ASE_BLEND_MODE_HSL_COLOR: + case ASE_BLEND_MODE_HSL_LUMINOSITY: { + double sr = src.r / 255.0, sg = src.g / 255.0, sb = src.b / 255.0; + double dr = dst.r / 255.0, dg = dst.g / 255.0, db = dst.b / 255.0; + double rr, rg, rb; + switch (mode) { + case ASE_BLEND_MODE_HSL_HUE: + rr = sr; rg = sg; rb = sb; + s_set_sat(&rr, &rg, &rb, s_sat(dr, dg, db)); + s_set_lum(&rr, &rg, &rb, s_lum(dr, dg, db)); + break; + case ASE_BLEND_MODE_HSL_SATURATION: + rr = dr; rg = dg; rb = db; + s_set_sat(&rr, &rg, &rb, s_sat(sr, sg, sb)); + s_set_lum(&rr, &rg, &rb, s_lum(dr, dg, db)); + break; + case ASE_BLEND_MODE_HSL_COLOR: + rr = sr; rg = sg; rb = sb; + s_set_lum(&rr, &rg, &rb, s_lum(dr, dg, db)); + break; + case ASE_BLEND_MODE_HSL_LUMINOSITY: + rr = dr; rg = dg; rb = db; + s_set_lum(&rr, &rg, &rb, s_lum(sr, sg, sb)); + break; + default: rr = sr; rg = sg; rb = sb; break; + } + br = (int)(rr * 255.0 + 0.5); + bg = (int)(rg * 255.0 + 0.5); + bb = (int)(rb * 255.0 + 0.5); + } break; + default: br = src.r; bg = src.g; bb = src.b; break; + } + + // Alpha composite with blended RGB. int a = src.a + dst.a - s_mul_un8(src.a, dst.a); int r, g, b; if (a == 0) { r = g = b = 0; } else { - r = dst.r + (src.r - dst.r) * src.a / a; - g = dst.g + (src.g - dst.g) * src.a / a; - b = dst.b + (src.b - dst.b) * src.a / a; + r = dst.r + (br - dst.r) * src.a / a; + g = dst.g + (bg - dst.g) * src.a / a; + b = dst.b + (bb - dst.b) * src.a / a; } ase_color_t ret = { (uint8_t)r, (uint8_t)g, (uint8_t)b, (uint8_t)a }; return ret; } -static int s_min(int a, int b) -{ - return a < b ? a : b; -} - -static int s_max(int a, int b) -{ - return a < b ? b : a; -} - static ase_color_t s_color(ase_t* ase, void* src, int index) { ase_color_t result; @@ -948,7 +1168,9 @@ ase_t* cute_aseprite_load_from_memory(const void* memory, int size, void* mem_ct CUTE_ASEPRITE_ASSERT(bpp == 1); ase->mode = ASE_MODE_INDEXED; } - uint32_t valid_layer_opacity = s_read_uint32(s) & 1; + uint32_t header_flags = s_read_uint32(s); + uint32_t valid_layer_opacity = header_flags & 1; + ase->valid_group_blend = (header_flags >> 1) & 1; int speed = s_read_uint16(s); s_skip(s, sizeof(uint32_t) * 2); // Spec says skip these bytes, as they're zero'd. ase->transparent_palette_entry_index = s_read_uint8(s); @@ -1030,8 +1252,7 @@ ase_t* cute_aseprite_load_from_memory(const void* memory, int size, void* mem_ct } s_skip(s, sizeof(uint16_t)); // Default layer width in pixels (ignored). s_skip(s, sizeof(uint16_t)); // Default layer height in pixels (ignored). - int blend_mode = (int)s_read_uint16(s); - if (blend_mode) CUTE_ASEPRITE_WARNING("Unknown blend mode encountered."); + layer->blend_mode = (ase_blend_mode_t)s_read_uint16(s); layer->opacity = s_read_uint8(s) / 255.0f; if (!valid_layer_opacity) layer->opacity = 1.0f; s_skip(s, 3); // For future use (set to zero). @@ -1049,7 +1270,11 @@ ase_t* cute_aseprite_load_from_memory(const void* memory, int size, void* mem_ct cel->y = s_read_int16(s); cel->opacity = s_read_uint8(s) / 255.0f; int cel_type = (int)s_read_uint16(s); - s_skip(s, 7); // For future (set to zero). + // z-index (int16) + 5 reserved bytes. The z-index lets individual cels + // override their layer stacking order per-frame, useful for hand-animated + // tricks like an arm swapping in front of / behind the body. Not parsed -- + // we composite in flat layer-index order. + s_skip(s, 7); switch (cel_type) { case 0: // Raw cel. cel->w = s_read_uint16(s); @@ -1224,34 +1449,77 @@ ase_t* cute_aseprite_load_from_memory(const void* memory, int size, void* mem_ct } } + // Helper: find layer index given layer pointer. + #define s_layer_index(ase, layer) ((int)((layer) - (ase)->layers)) + // Blend all cel pixels into each of their respective frames, for convenience. + // Supports group compositing when valid_group_blend is set. + int pixel_count = ase->w * ase->h; + int pixel_bytes = pixel_count * (int)sizeof(ase_color_t); + + // Allocate group buffers if needed. + ase_color_t* group_buffers[CUTE_ASEPRITE_MAX_LAYERS]; + CUTE_ASEPRITE_MEMSET(group_buffers, 0, sizeof(group_buffers)); + if (ase->valid_group_blend) { + for (int li = 0; li < ase->layer_count; ++li) { + if (ase->layers[li].type == ASE_LAYER_TYPE_GROUP) { + group_buffers[li] = (ase_color_t*)CUTE_ASEPRITE_ALLOC(pixel_bytes, mem_ctx); + } + } + } + for (int i = 0; i < ase->frame_count; ++i) { ase_frame_t* frame = ase->frames + i; - frame->pixels = (ase_color_t*)CUTE_ASEPRITE_ALLOC((int)(sizeof(ase_color_t)) * ase->w * ase->h, mem_ctx); - CUTE_ASEPRITE_MEMSET(frame->pixels, 0, sizeof(ase_color_t) * (size_t)ase->w * (size_t)ase->h); - ase_color_t* dst = frame->pixels; + frame->pixels = (ase_color_t*)CUTE_ASEPRITE_ALLOC(pixel_bytes, mem_ctx); + CUTE_ASEPRITE_MEMSET(frame->pixels, 0, (size_t)pixel_bytes); + + // Clear group buffers for this frame. + if (ase->valid_group_blend) { + for (int li = 0; li < ase->layer_count; ++li) { + if (group_buffers[li]) { + CUTE_ASEPRITE_MEMSET(group_buffers[li], 0, (size_t)pixel_bytes); + } + } + } + for (int j = 0; j < frame->cel_count; ++j) { ase_cel_t* cel = frame->cels + j; - if (!(cel->layer->flags & ASE_LAYER_FLAGS_VISIBLE)) { - continue; - } - if (cel->layer->parent && !(cel->layer->parent->flags & ASE_LAYER_FLAGS_VISIBLE)) { - continue; + + // Walk full parent chain for visibility. + int visible = (cel->layer->flags & ASE_LAYER_FLAGS_VISIBLE) ? 1 : 0; + if (visible) { + ase_layer_t* p = cel->layer->parent; + while (p) { + if (!(p->flags & ASE_LAYER_FLAGS_VISIBLE)) { visible = 0; break; } + p = p->parent; + } } + if (!visible) continue; + while (cel->is_linked) { - ase_frame_t* frame = ase->frames + cel->linked_frame_index; + ase_frame_t* linked_frame = ase->frames + cel->linked_frame_index; int found = 0; - for (int k = 0; k < frame->cel_count; ++k) { - if (frame->cels[k].layer == cel->layer) { - cel = frame->cels + k; + for (int k = 0; k < linked_frame->cel_count; ++k) { + if (linked_frame->cels[k].layer == cel->layer) { + cel = linked_frame->cels + k; found = 1; break; } } CUTE_ASEPRITE_ASSERT(found); } + + // Target buffer: group parent's buffer (if group compositing) or frame. + ase_color_t* dst; + if (ase->valid_group_blend && cel->layer->parent && cel->layer->parent->type == ASE_LAYER_TYPE_GROUP) { + dst = group_buffers[s_layer_index(ase, cel->layer->parent)]; + } else { + dst = frame->pixels; + } + void* src = cel->pixels; uint8_t opacity = (uint8_t)(cel->opacity * cel->layer->opacity * 255.0f); + ase_blend_mode_t blend_mode = cel->layer->blend_mode; int cx = cel->x; int cy = cel->y; int cw = cel->w; @@ -1268,17 +1536,115 @@ ase_t* cute_aseprite_load_from_memory(const void* memory, int size, void* mem_ct int dst_index = aw * dy + dx; ase_color_t src_color = s_color(ase, src, cw * sy + sx); ase_color_t dst_color = dst[dst_index]; - ase_color_t result = s_blend(src_color, dst_color, opacity); + ase_color_t result = s_blend(src_color, dst_color, opacity, blend_mode); dst[dst_index] = result; } } } + + // Flush group buffers bottom-up (highest layer index = innermost groups first). + if (ase->valid_group_blend) { + for (int li = ase->layer_count - 1; li >= 0; --li) { + if (!group_buffers[li]) continue; + ase_layer_t* group = ase->layers + li; + + // Target: parent group's buffer, or the frame. + ase_color_t* dst; + if (group->parent && group->parent->type == ASE_LAYER_TYPE_GROUP) { + dst = group_buffers[s_layer_index(ase, group->parent)]; + } else { + dst = frame->pixels; + } + + uint8_t group_opacity = (uint8_t)(group->opacity * 255.0f); + ase_blend_mode_t group_mode = group->blend_mode; + ase_color_t* src_buf = group_buffers[li]; + for (int p = 0; p < pixel_count; ++p) { + if (src_buf[p].a == 0) continue; + dst[p] = s_blend(src_buf[p], dst[p], group_opacity, group_mode); + } + + // Clear for next frame. + CUTE_ASEPRITE_MEMSET(src_buf, 0, (size_t)pixel_bytes); + } + } } + // Free group buffers. + if (ase->valid_group_blend) { + for (int li = 0; li < ase->layer_count; ++li) { + if (group_buffers[li]) { + CUTE_ASEPRITE_FREE(group_buffers[li], mem_ctx); + } + } + } + + #undef s_layer_index + ase->mem_ctx = mem_ctx; return ase; } +void cute_aseprite_blend_layers(ase_t* ase, uint64_t layer_mask, ase_color_t** out_pixels) +{ + for (int i = 0; i < ase->frame_count; ++i) { + ase_frame_t* frame = ase->frames + i; + ase_color_t* dst = out_pixels[i]; + for (int j = 0; j < frame->cel_count; ++j) { + ase_cel_t* cel = frame->cels + j; + int li = (int)(cel->layer - ase->layers); + if (!(layer_mask & (1ULL << li))) continue; + + // Walk parent chain: skip if any ancestor is not in the mask. + int include = 1; + ase_layer_t* p = cel->layer->parent; + while (p) { + int pi = (int)(p - ase->layers); + if (!(layer_mask & (1ULL << pi))) { include = 0; break; } + p = p->parent; + } + if (!include) continue; + + while (cel->is_linked) { + ase_frame_t* linked_frame = ase->frames + cel->linked_frame_index; + int found = 0; + for (int k = 0; k < linked_frame->cel_count; ++k) { + if (linked_frame->cels[k].layer == cel->layer) { + cel = linked_frame->cels + k; + found = 1; + break; + } + } + CUTE_ASEPRITE_ASSERT(found); + } + + void* src = cel->pixels; + uint8_t opacity = (uint8_t)(cel->opacity * cel->layer->opacity * 255.0f); + ase_blend_mode_t blend_mode = cel->layer->blend_mode; + int cx = cel->x; + int cy = cel->y; + int cw = cel->w; + int ch = cel->h; + int cl = -s_min(cx, 0); + int ct = -s_min(cy, 0); + int dl = s_max(cx, 0); + int dt = s_max(cy, 0); + int dr = s_min(ase->w, cw + cx); + int db = s_min(ase->h, ch + cy); + int aw = ase->w; + for (int dx = dl, sx = cl; dx < dr; dx++, sx++) { + for (int dy = dt, sy = ct; dy < db; dy++, sy++) { + int dst_index = aw * dy + dx; + ase_color_t src_color = s_color(ase, src, cw * sy + sx); + ase_color_t dst_color = dst[dst_index]; + ase_color_t result = s_blend(src_color, dst_color, opacity, blend_mode); + dst[dst_index] = result; + } + } + } + } +} + void cute_aseprite_free(ase_t* ase) { for (int i = 0; i < ase->frame_count; ++i) { diff --git a/libraries/cute/cute_c2.h b/libraries/cute/cute_c2.h index de9a5b296..73639def0 100644 --- a/libraries/cute/cute_c2.h +++ b/libraries/cute/cute_c2.h @@ -289,10 +289,10 @@ CUTE_C2_API int c2CircletoCapsule(c2Circle A, c2Capsule B); CUTE_C2_API int c2AABBtoAABB(c2AABB A, c2AABB B); CUTE_C2_API int c2AABBtoCapsule(c2AABB A, c2Capsule B); CUTE_C2_API int c2CapsuletoCapsule(c2Capsule A, c2Capsule B); -CUTE_C2_API int c2CircletoPoly(c2Circle A, const c2Poly* B, const c2x* bx); -CUTE_C2_API int c2AABBtoPoly(c2AABB A, const c2Poly* B, const c2x* bx); -CUTE_C2_API int c2CapsuletoPoly(c2Capsule A, const c2Poly* B, const c2x* bx); -CUTE_C2_API int c2PolytoPoly(const c2Poly* A, const c2x* ax, const c2Poly* B, const c2x* bx); +CUTE_C2_API int c2CircletoPoly(c2Circle A, const c2Poly* B); +CUTE_C2_API int c2AABBtoPoly(c2AABB A, const c2Poly* B); +CUTE_C2_API int c2CapsuletoPoly(c2Capsule A, const c2Poly* B); +CUTE_C2_API int c2PolytoPoly(const c2Poly* A, const c2Poly* B); // ray operations // output is placed into the c2Raycast struct, which represents the hit location @@ -301,7 +301,7 @@ CUTE_C2_API int c2PolytoPoly(const c2Poly* A, const c2x* ax, const c2Poly* B, co CUTE_C2_API int c2RaytoCircle(c2Ray A, c2Circle B, c2Raycast* out); CUTE_C2_API int c2RaytoAABB(c2Ray A, c2AABB B, c2Raycast* out); CUTE_C2_API int c2RaytoCapsule(c2Ray A, c2Capsule B, c2Raycast* out); -CUTE_C2_API int c2RaytoPoly(c2Ray A, const c2Poly* B, const c2x* bx_ptr, c2Raycast* out); +CUTE_C2_API int c2RaytoPoly(c2Ray A, const c2Poly* B, c2Raycast* out); // manifold generation // These functions are (generally) slower than the boolean versions, but will compute one @@ -314,10 +314,10 @@ CUTE_C2_API void c2CircletoCapsuleManifold(c2Circle A, c2Capsule B, c2Manifold* CUTE_C2_API void c2AABBtoAABBManifold(c2AABB A, c2AABB B, c2Manifold* m); CUTE_C2_API void c2AABBtoCapsuleManifold(c2AABB A, c2Capsule B, c2Manifold* m); CUTE_C2_API void c2CapsuletoCapsuleManifold(c2Capsule A, c2Capsule B, c2Manifold* m); -CUTE_C2_API void c2CircletoPolyManifold(c2Circle A, const c2Poly* B, const c2x* bx, c2Manifold* m); -CUTE_C2_API void c2AABBtoPolyManifold(c2AABB A, const c2Poly* B, const c2x* bx, c2Manifold* m); -CUTE_C2_API void c2CapsuletoPolyManifold(c2Capsule A, const c2Poly* B, const c2x* bx, c2Manifold* m); -CUTE_C2_API void c2PolytoPolyManifold(const c2Poly* A, const c2x* ax, const c2Poly* B, const c2x* bx, c2Manifold* m); +CUTE_C2_API void c2CircletoPolyManifold(c2Circle A, const c2Poly* B, c2Manifold* m); +CUTE_C2_API void c2AABBtoPolyManifold(c2AABB A, const c2Poly* B, c2Manifold* m); +CUTE_C2_API void c2CapsuletoPolyManifold(c2Capsule A, const c2Poly* B, c2Manifold* m); +CUTE_C2_API void c2PolytoPolyManifold(const c2Poly* A, const c2Poly* B, c2Manifold* m); typedef enum { @@ -342,12 +342,11 @@ typedef struct c2GJKCache // This is an advanced function, intended to be used by people who know what they're doing. // // Runs the GJK algorithm to find closest points, returns distance between closest points. -// outA and outB can be NULL, in this case only distance is returned. ax_ptr and bx_ptr -// can be NULL, and represent local to world transformations for shapes A and B respectively. -// use_radius will apply radii for capsules and circles (if set to false, spheres are -// treated as points and capsules are treated as line segments i.e. rays). The cache parameter -// should be NULL, as it is only for advanced usage (unless you know what you're doing, then -// go ahead and use it). iterations is an optional parameter. +// outA and outB can be NULL, in this case only distance is returned. use_radius will apply +// radii for capsules and circles (if set to false, spheres are treated as points and capsules +// are treated as line segments i.e. rays). The cache parameter should be NULL, as it is only +// for advanced usage (unless you know what you're doing, then go ahead and use it). iterations +// is an optional parameter. // // IMPORTANT NOTE: // The GJK function is sensitive to large shapes, since it internally will compute signed area @@ -355,7 +354,7 @@ typedef struct c2GJKCache // collision shapes are not gigantic. For example, try to keep the volume of all your shapes // less than 100.0f. If you need large shapes, you should use tiny collision geometry for all // cute c2 function, and simply render the geometry larger on-screen by scaling it up. -CUTE_C2_API float c2GJK(const void* A, C2_TYPE typeA, const c2x* ax_ptr, const void* B, C2_TYPE typeB, const c2x* bx_ptr, c2v* outA, c2v* outB, int use_radius, int* iterations, c2GJKCache* cache); +CUTE_C2_API float c2GJK(const void* A, C2_TYPE typeA, const void* B, C2_TYPE typeB, c2v* outA, c2v* outB, int use_radius, int* iterations, c2GJKCache* cache); // Stores results of a time of impact calculation done by `c2TOI`. typedef struct c2TOIResult @@ -373,11 +372,9 @@ typedef struct c2TOIResult // by vA and vB respectively. The shapes are *not* allowed to rotate over time. The velocity is // assumed to represent the change in motion from time 0 to time 1, and so the return value will // be a number from 0 to 1. To move each shape to the colliding configuration, multiply vA and vB -// each by the return value. ax_ptr and bx_ptr are optional parameters to transforms for each shape, -// and are typically used for polygon shapes to transform from model to world space. Set these to -// NULL to represent identity transforms. iterations is an optional parameter. use_radius -// will apply radii for capsules and circles (if set to false, spheres are treated as points and -// capsules are treated as line segments i.e. rays). +// each by the return value. iterations is an optional parameter. use_radius will apply radii +// for capsules and circles (if set to false, spheres are treated as points and capsules are +// treated as line segments i.e. rays). // // IMPORTANT NOTE: // The c2TOI function can be used to implement a "swept character controller", but it can be @@ -396,7 +393,7 @@ typedef struct c2TOIResult // See the function `c2Inflate` for some more details. // 4. Compute the collision manifold between the inflated shapes (for example, use c2PolytoPolyManifold). // 5. Gently push the shapes apart. This will give the next call to c2TOI some breathing room. -CUTE_C2_API c2TOIResult c2TOI(const void* A, C2_TYPE typeA, const c2x* ax_ptr, c2v vA, const void* B, C2_TYPE typeB, const c2x* bx_ptr, c2v vB, int use_radius); +CUTE_C2_API c2TOIResult c2TOI(const void* A, C2_TYPE typeA, c2v vA, const void* B, C2_TYPE typeB, c2v vB, int use_radius); // Inflating a shape. // @@ -422,11 +419,9 @@ CUTE_C2_API void c2MakePoly(c2Poly* p); // Generic collision detection routines, useful for games that want to use some poly- // morphism to write more generic-styled code. Internally calls various above functions. -// For AABBs/Circles/Capsules ax and bx are ignored. For polys ax and bx can define -// model to world transformations (for polys only), or be NULL for identity transforms. -CUTE_C2_API int c2Collided(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const c2x* bx, C2_TYPE typeB); -CUTE_C2_API void c2Collide(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const c2x* bx, C2_TYPE typeB, c2Manifold* m); -CUTE_C2_API int c2CastRay(c2Ray A, const void* B, const c2x* bx, C2_TYPE typeB, c2Raycast* out); +CUTE_C2_API int c2Collided(const void* A, C2_TYPE typeA, const void* B, C2_TYPE typeB); +CUTE_C2_API void c2Collide(const void* A, C2_TYPE typeA, const void* B, C2_TYPE typeB, c2Manifold* m); +CUTE_C2_API int c2CastRay(c2Ray A, const void* B, C2_TYPE typeB, c2Raycast* out); #ifdef _MSC_VER #define C2_INLINE __forceinline @@ -533,7 +528,7 @@ C2_INLINE void c2BBVerts(c2v* out, c2AABB* bb) #ifndef CUTE_C2_IMPLEMENTATION_ONCE #define CUTE_C2_IMPLEMENTATION_ONCE -int c2Collided(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const c2x* bx, C2_TYPE typeB) +int c2Collided(const void* A, C2_TYPE typeA, const void* B, C2_TYPE typeB) { switch (typeA) { @@ -543,7 +538,7 @@ int c2Collided(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const case C2_TYPE_CIRCLE: return c2CircletoCircle(*(c2Circle*)A, *(c2Circle*)B); case C2_TYPE_AABB: return c2CircletoAABB(*(c2Circle*)A, *(c2AABB*)B); case C2_TYPE_CAPSULE: return c2CircletoCapsule(*(c2Circle*)A, *(c2Capsule*)B); - case C2_TYPE_POLY: return c2CircletoPoly(*(c2Circle*)A, (const c2Poly*)B, bx); + case C2_TYPE_POLY: return c2CircletoPoly(*(c2Circle*)A, (const c2Poly*)B); default: return 0; } break; @@ -554,7 +549,7 @@ int c2Collided(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const case C2_TYPE_CIRCLE: return c2CircletoAABB(*(c2Circle*)B, *(c2AABB*)A); case C2_TYPE_AABB: return c2AABBtoAABB(*(c2AABB*)A, *(c2AABB*)B); case C2_TYPE_CAPSULE: return c2AABBtoCapsule(*(c2AABB*)A, *(c2Capsule*)B); - case C2_TYPE_POLY: return c2AABBtoPoly(*(c2AABB*)A, (const c2Poly*)B, bx); + case C2_TYPE_POLY: return c2AABBtoPoly(*(c2AABB*)A, (const c2Poly*)B); default: return 0; } break; @@ -565,7 +560,7 @@ int c2Collided(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const case C2_TYPE_CIRCLE: return c2CircletoCapsule(*(c2Circle*)B, *(c2Capsule*)A); case C2_TYPE_AABB: return c2AABBtoCapsule(*(c2AABB*)B, *(c2Capsule*)A); case C2_TYPE_CAPSULE: return c2CapsuletoCapsule(*(c2Capsule*)A, *(c2Capsule*)B); - case C2_TYPE_POLY: return c2CapsuletoPoly(*(c2Capsule*)A, (const c2Poly*)B, bx); + case C2_TYPE_POLY: return c2CapsuletoPoly(*(c2Capsule*)A, (const c2Poly*)B); default: return 0; } break; @@ -573,10 +568,10 @@ int c2Collided(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const case C2_TYPE_POLY: switch (typeB) { - case C2_TYPE_CIRCLE: return c2CircletoPoly(*(c2Circle*)B, (const c2Poly*)A, ax); - case C2_TYPE_AABB: return c2AABBtoPoly(*(c2AABB*)B, (const c2Poly*)A, ax); - case C2_TYPE_CAPSULE: return c2CapsuletoPoly(*(c2Capsule*)B, (const c2Poly*)A, ax); - case C2_TYPE_POLY: return c2PolytoPoly((const c2Poly*)A, ax, (const c2Poly*)B, bx); + case C2_TYPE_CIRCLE: return c2CircletoPoly(*(c2Circle*)B, (const c2Poly*)A); + case C2_TYPE_AABB: return c2AABBtoPoly(*(c2AABB*)B, (const c2Poly*)A); + case C2_TYPE_CAPSULE: return c2CapsuletoPoly(*(c2Capsule*)B, (const c2Poly*)A); + case C2_TYPE_POLY: return c2PolytoPoly((const c2Poly*)A, (const c2Poly*)B); default: return 0; } break; @@ -586,7 +581,7 @@ int c2Collided(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const } } -void c2Collide(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const c2x* bx, C2_TYPE typeB, c2Manifold* m) +void c2Collide(const void* A, C2_TYPE typeA, const void* B, C2_TYPE typeB, c2Manifold* m) { m->count = 0; @@ -598,7 +593,7 @@ void c2Collide(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const case C2_TYPE_CIRCLE: c2CircletoCircleManifold(*(c2Circle*)A, *(c2Circle*)B, m); break; case C2_TYPE_AABB: c2CircletoAABBManifold(*(c2Circle*)A, *(c2AABB*)B, m); break; case C2_TYPE_CAPSULE: c2CircletoCapsuleManifold(*(c2Circle*)A, *(c2Capsule*)B, m); break; - case C2_TYPE_POLY: c2CircletoPolyManifold(*(c2Circle*)A, (const c2Poly*)B, bx, m); break; + case C2_TYPE_POLY: c2CircletoPolyManifold(*(c2Circle*)A, (const c2Poly*)B, m); break; default: break; } break; @@ -609,7 +604,7 @@ void c2Collide(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const case C2_TYPE_CIRCLE: c2CircletoAABBManifold(*(c2Circle*)B, *(c2AABB*)A, m); m->n = c2Neg(m->n); break; case C2_TYPE_AABB: c2AABBtoAABBManifold(*(c2AABB*)A, *(c2AABB*)B, m); break; case C2_TYPE_CAPSULE: c2AABBtoCapsuleManifold(*(c2AABB*)A, *(c2Capsule*)B, m); break; - case C2_TYPE_POLY: c2AABBtoPolyManifold(*(c2AABB*)A, (const c2Poly*)B, bx, m); break; + case C2_TYPE_POLY: c2AABBtoPolyManifold(*(c2AABB*)A, (const c2Poly*)B, m); break; default: break; } break; @@ -620,7 +615,7 @@ void c2Collide(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const case C2_TYPE_CIRCLE: c2CircletoCapsuleManifold(*(c2Circle*)B, *(c2Capsule*)A, m); m->n = c2Neg(m->n); break; case C2_TYPE_AABB: c2AABBtoCapsuleManifold(*(c2AABB*)B, *(c2Capsule*)A, m); m->n = c2Neg(m->n); break; case C2_TYPE_CAPSULE: c2CapsuletoCapsuleManifold(*(c2Capsule*)A, *(c2Capsule*)B, m); break; - case C2_TYPE_POLY: c2CapsuletoPolyManifold(*(c2Capsule*)A, (const c2Poly*)B, bx, m); break; + case C2_TYPE_POLY: c2CapsuletoPolyManifold(*(c2Capsule*)A, (const c2Poly*)B, m); break; default: break; } break; @@ -628,10 +623,10 @@ void c2Collide(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const case C2_TYPE_POLY: switch (typeB) { - case C2_TYPE_CIRCLE: c2CircletoPolyManifold(*(c2Circle*)B, (const c2Poly*)A, ax, m); m->n = c2Neg(m->n); break; - case C2_TYPE_AABB: c2AABBtoPolyManifold(*(c2AABB*)B, (const c2Poly*)A, ax, m); m->n = c2Neg(m->n); break; - case C2_TYPE_CAPSULE: c2CapsuletoPolyManifold(*(c2Capsule*)B, (const c2Poly*)A, ax, m); m->n = c2Neg(m->n); break; - case C2_TYPE_POLY: c2PolytoPolyManifold((const c2Poly*)A, ax, (const c2Poly*)B, bx, m); break; + case C2_TYPE_CIRCLE: c2CircletoPolyManifold(*(c2Circle*)B, (const c2Poly*)A, m); m->n = c2Neg(m->n); break; + case C2_TYPE_AABB: c2AABBtoPolyManifold(*(c2AABB*)B, (const c2Poly*)A, m); m->n = c2Neg(m->n); break; + case C2_TYPE_CAPSULE: c2CapsuletoPolyManifold(*(c2Capsule*)B, (const c2Poly*)A, m); m->n = c2Neg(m->n); break; + case C2_TYPE_POLY: c2PolytoPolyManifold((const c2Poly*)A, (const c2Poly*)B, m); break; default: break; } break; @@ -639,14 +634,14 @@ void c2Collide(const void* A, const c2x* ax, C2_TYPE typeA, const void* B, const } } -int c2CastRay(c2Ray A, const void* B, const c2x* bx, C2_TYPE typeB, c2Raycast* out) +int c2CastRay(c2Ray A, const void* B, C2_TYPE typeB, c2Raycast* out) { switch (typeB) { case C2_TYPE_CIRCLE: return c2RaytoCircle(A, *(c2Circle*)B, out); case C2_TYPE_AABB: return c2RaytoAABB(A, *(c2AABB*)B, out); case C2_TYPE_CAPSULE: return c2RaytoCapsule(A, *(c2Capsule*)B, out); - case C2_TYPE_POLY: return c2RaytoPoly(A, (const c2Poly*)B, bx, out); + case C2_TYPE_POLY: return c2RaytoPoly(A, (const c2Poly*)B, out); default: return 0; } } @@ -903,15 +898,8 @@ static C2_INLINE float c2GJKSimplexMetric(c2Simplex* s) // Please see http://box2d.org/downloads/ under GDC 2010 for Erin's demo code // and PDF slides for documentation on the GJK algorithm. This function is mostly // from Erin's version from his online resources. -float c2GJK(const void* A, C2_TYPE typeA, const c2x* ax_ptr, const void* B, C2_TYPE typeB, const c2x* bx_ptr, c2v* outA, c2v* outB, int use_radius, int* iterations, c2GJKCache* cache) +float c2GJK(const void* A, C2_TYPE typeA, const void* B, C2_TYPE typeB, c2v* outA, c2v* outB, int use_radius, int* iterations, c2GJKCache* cache) { - c2x ax; - c2x bx; - if (!ax_ptr) ax = c2xIdentity(); - else ax = *ax_ptr; - if (!bx_ptr) bx = c2xIdentity(); - else bx = *bx_ptr; - c2Proxy pA; c2Proxy pB; c2MakeProxy(A, typeA, &pA); @@ -936,8 +924,8 @@ float c2GJK(const void* A, C2_TYPE typeA, const c2x* ax_ptr, const void* B, C2_T { int iA = cache->iA[i]; int iB = cache->iB[i]; - c2v sA = c2Mulxv(ax, pA.verts[iA]); - c2v sB = c2Mulxv(bx, pB.verts[iB]); + c2v sA = pA.verts[iA]; + c2v sB = pB.verts[iB]; c2sv* v = verts + i; v->iA = iA; v->sA = sA; @@ -963,8 +951,8 @@ float c2GJK(const void* A, C2_TYPE typeA, const c2x* ax_ptr, const void* B, C2_T { s.a.iA = 0; s.a.iB = 0; - s.a.sA = c2Mulxv(ax, pA.verts[0]); - s.a.sB = c2Mulxv(bx, pB.verts[0]); + s.a.sA = pA.verts[0]; + s.a.sB = pB.verts[0]; s.a.p = c2Sub(s.a.sB, s.a.sA); s.a.u = 1.0f; s.div = 1.0f; @@ -985,7 +973,7 @@ float c2GJK(const void* A, C2_TYPE typeA, const c2x* ax_ptr, const void* B, C2_T saveA[i] = verts[i].iA; saveB[i] = verts[i].iB; } - + switch (s.count) { case 1: break; @@ -1008,10 +996,10 @@ float c2GJK(const void* A, C2_TYPE typeA, const c2x* ax_ptr, const void* B, C2_T c2v d = c2D(&s); if (c2Dot(d, d) < FLT_EPSILON * FLT_EPSILON) break; - int iA = c2Support(pA.verts, pA.count, c2MulrvT(ax.r, c2Neg(d))); - c2v sA = c2Mulxv(ax, pA.verts[iA]); - int iB = c2Support(pB.verts, pB.count, c2MulrvT(bx.r, d)); - c2v sB = c2Mulxv(bx, pB.verts[iB]); + int iA = c2Support(pA.verts, pA.count, c2Neg(d)); + c2v sA = pA.verts[iA]; + int iB = c2Support(pB.verts, pB.count, d); + c2v sB = pB.verts[iB]; c2sv* v = verts + s.count; v->iA = iA; @@ -1090,15 +1078,9 @@ float c2GJK(const void* A, C2_TYPE typeA, const c2x* ax_ptr, const void* B, C2_T // Referenced from Box2D's b2ShapeCast function. // GJK-Raycast algorithm by Gino van den Bergen. // "Smooth Mesh Contacts with GJK" in Game Physics Pearls, 2010. -c2TOIResult c2TOI(const void* A, C2_TYPE typeA, const c2x* ax_ptr, c2v vA, const void* B, C2_TYPE typeB, const c2x* bx_ptr, c2v vB, int use_radius) +c2TOIResult c2TOI(const void* A, C2_TYPE typeA, c2v vA, const void* B, C2_TYPE typeB, c2v vB, int use_radius) { float t = 0; - c2x ax; - c2x bx; - if (!ax_ptr) ax = c2xIdentity(); - else ax = *ax_ptr; - if (!bx_ptr) bx = c2xIdentity(); - else bx = *bx_ptr; c2Proxy pA; c2Proxy pB; @@ -1110,10 +1092,10 @@ c2TOIResult c2TOI(const void* A, C2_TYPE typeA, const c2x* ax_ptr, c2v vA, const c2sv* verts = &s.a; c2v rv = c2Sub(vB, vA); - int iA = c2Support(pA.verts, pA.count, c2MulrvT(ax.r, c2Neg(rv))); - c2v sA = c2Mulxv(ax, pA.verts[iA]); - int iB = c2Support(pB.verts, pB.count, c2MulrvT(bx.r, rv)); - c2v sB = c2Mulxv(bx, pB.verts[iB]); + int iA = c2Support(pA.verts, pA.count, c2Neg(rv)); + c2v sA = pA.verts[iA]; + int iB = c2Support(pB.verts, pB.count, rv); + c2v sB = pB.verts[iB]; c2v v = c2Sub(sA, sB); float rA = pA.radius; @@ -1141,10 +1123,10 @@ c2TOIResult c2TOI(const void* A, C2_TYPE typeA, const c2x* ax_ptr, c2v vA, const while (result.iterations < 20) { - iA = c2Support(pA.verts, pA.count, c2MulrvT(ax.r, c2Neg(v))); - sA = c2Mulxv(ax, pA.verts[iA]); - iB = c2Support(pB.verts, pB.count, c2MulrvT(bx.r, v)); - sB = c2Mulxv(bx, pB.verts[iB]); + iA = c2Support(pA.verts, pA.count, c2Neg(v)); + sA = pA.verts[iA]; + iB = c2Support(pB.verts, pB.count, v); + sB = pB.verts[iB]; c2v p = c2Sub(sA, sB); v = c2Norm(v); float vp = c2Dot(v, p) - radius; @@ -1186,8 +1168,8 @@ c2TOIResult c2TOI(const void* A, C2_TYPE typeA, const c2x* ax_ptr, c2v vA, const result.hit = 0; } else { if (c2Dot(v, v) > 0) result.n = c2SafeNorm(c2Neg(v)); - int i = c2Support(pA.verts, pA.count, c2MulrvT(ax.r, result.n)); - c2v p = c2Mulxv(ax, pA.verts[i]); + int i = c2Support(pA.verts, pA.count, result.n); + c2v p = pA.verts[i]; p = c2Add(c2Add(p, c2Mulvs(result.n, rA)), c2Mulvs(vA, t)); result.p = p; result.toi = t; @@ -1434,37 +1416,37 @@ int c2CircletoCapsule(c2Circle A, c2Capsule B) int c2AABBtoCapsule(c2AABB A, c2Capsule B) { - if (c2GJK(&A, C2_TYPE_AABB, 0, &B, C2_TYPE_CAPSULE, 0, 0, 0, 1, 0, 0)) return 0; + if (c2GJK(&A, C2_TYPE_AABB, &B, C2_TYPE_CAPSULE, 0, 0, 1, 0, 0)) return 0; return 1; } int c2CapsuletoCapsule(c2Capsule A, c2Capsule B) { - if (c2GJK(&A, C2_TYPE_CAPSULE, 0, &B, C2_TYPE_CAPSULE, 0, 0, 0, 1, 0, 0)) return 0; + if (c2GJK(&A, C2_TYPE_CAPSULE, &B, C2_TYPE_CAPSULE, 0, 0, 1, 0, 0)) return 0; return 1; } -int c2CircletoPoly(c2Circle A, const c2Poly* B, const c2x* bx) +int c2CircletoPoly(c2Circle A, const c2Poly* B) { - if (c2GJK(&A, C2_TYPE_CIRCLE, 0, B, C2_TYPE_POLY, bx, 0, 0, 1, 0, 0)) return 0; + if (c2GJK(&A, C2_TYPE_CIRCLE, B, C2_TYPE_POLY, 0, 0, 1, 0, 0)) return 0; return 1; } -int c2AABBtoPoly(c2AABB A, const c2Poly* B, const c2x* bx) +int c2AABBtoPoly(c2AABB A, const c2Poly* B) { - if (c2GJK(&A, C2_TYPE_AABB, 0, B, C2_TYPE_POLY, bx, 0, 0, 1, 0, 0)) return 0; + if (c2GJK(&A, C2_TYPE_AABB, B, C2_TYPE_POLY, 0, 0, 1, 0, 0)) return 0; return 1; } -int c2CapsuletoPoly(c2Capsule A, const c2Poly* B, const c2x* bx) +int c2CapsuletoPoly(c2Capsule A, const c2Poly* B) { - if (c2GJK(&A, C2_TYPE_CAPSULE, 0, B, C2_TYPE_POLY, bx, 0, 0, 1, 0, 0)) return 0; + if (c2GJK(&A, C2_TYPE_CAPSULE, B, C2_TYPE_POLY, 0, 0, 1, 0, 0)) return 0; return 1; } -int c2PolytoPoly(const c2Poly* A, const c2x* ax, const c2Poly* B, const c2x* bx) +int c2PolytoPoly(const c2Poly* A, const c2Poly* B) { - if (c2GJK(A, C2_TYPE_POLY, ax, B, C2_TYPE_POLY, bx, 0, 0, 1, 0, 0)) return 0; + if (c2GJK(A, C2_TYPE_POLY, B, C2_TYPE_POLY, 0, 0, 1, 0, 0)) return 0; return 1; } @@ -1657,11 +1639,10 @@ int c2RaytoCapsule(c2Ray A, c2Capsule B, c2Raycast* out) return 0; } -int c2RaytoPoly(c2Ray A, const c2Poly* B, const c2x* bx_ptr, c2Raycast* out) +int c2RaytoPoly(c2Ray A, const c2Poly* B, c2Raycast* out) { - c2x bx = bx_ptr ? *bx_ptr : c2xIdentity(); - c2v p = c2MulxvT(bx, A.p); - c2v d = c2MulrvT(bx.r, A.d); + c2v p = A.p; + c2v d = A.d; float lo = 0; float hi = A.t; int index = ~0; @@ -1687,7 +1668,7 @@ int c2RaytoPoly(c2Ray A, const c2Poly* B, const c2x* bx_ptr, c2Raycast* out) if (index != ~0) { out->t = lo; - out->n = c2Mulrv(bx.r, B->norms[index]); + out->n = B->norms[index]; return 1; } @@ -1773,7 +1754,7 @@ void c2CircletoCapsuleManifold(c2Circle A, c2Capsule B, c2Manifold* m) m->count = 0; c2v a, b; float r = A.r + B.r; - float d = c2GJK(&A, C2_TYPE_CIRCLE, 0, &B, C2_TYPE_CAPSULE, 0, &a, &b, 0, 0, 0); + float d = c2GJK(&A, C2_TYPE_CIRCLE, &B, C2_TYPE_CAPSULE, &a, &b, 0, 0, 0); if (d < r) { c2v n; @@ -1851,7 +1832,7 @@ void c2AABBtoCapsuleManifold(c2AABB A, c2Capsule B, c2Manifold* m) c2BBVerts(p.verts, &A); p.count = 4; c2Norms(p.verts, p.norms, 4); - c2CapsuletoPolyManifold(B, &p, 0, m); + c2CapsuletoPolyManifold(B, &p, m); m->n = c2Neg(m->n); } @@ -1860,7 +1841,7 @@ void c2CapsuletoCapsuleManifold(c2Capsule A, c2Capsule B, c2Manifold* m) m->count = 0; c2v a, b; float r = A.r + B.r; - float d = c2GJK(&A, C2_TYPE_CAPSULE, 0, &B, C2_TYPE_CAPSULE, 0, &a, &b, 0, 0, 0); + float d = c2GJK(&A, C2_TYPE_CAPSULE, &B, C2_TYPE_CAPSULE, &a, &b, 0, 0, 0); if (d < r) { c2v n; @@ -1882,11 +1863,11 @@ static C2_INLINE c2h c2PlaneAt(const c2Poly* p, const int i) return h; } -void c2CircletoPolyManifold(c2Circle A, const c2Poly* B, const c2x* bx_tr, c2Manifold* m) +void c2CircletoPolyManifold(c2Circle A, const c2Poly* B, c2Manifold* m) { m->count = 0; c2v a, b; - float d = c2GJK(&A, C2_TYPE_CIRCLE, 0, B, C2_TYPE_POLY, bx_tr, &a, &b, 0, 0, 0); + float d = c2GJK(&A, C2_TYPE_CIRCLE, B, C2_TYPE_POLY, &a, &b, 0, 0, 0); // shallow, the circle center did not hit the polygon // just use a and b from GJK to define the collision @@ -1908,15 +1889,13 @@ void c2CircletoPolyManifold(c2Circle A, const c2Poly* B, const c2x* bx_tr, c2Man // find the face closest to circle center to form manifold else { - c2x bx = bx_tr ? *bx_tr : c2xIdentity(); float sep = -FLT_MAX; int index = ~0; - c2v local = c2MulxvT(bx, A.p); for (int i = 0; i < B->count; ++i) { c2h h = c2PlaneAt(B, i); - d = c2Dist(h, local); + d = c2Dist(h, A.p); if (d > A.r) return; if (d > sep) { @@ -1926,23 +1905,23 @@ void c2CircletoPolyManifold(c2Circle A, const c2Poly* B, const c2x* bx_tr, c2Man } c2h h = c2PlaneAt(B, index); - c2v p = c2Project(h, local); + c2v p = c2Project(h, A.p); m->count = 1; - m->contact_points[0] = c2Mulxv(bx, p); + m->contact_points[0] = p; m->depths[0] = A.r - sep; - m->n = c2Neg(c2Mulrv(bx.r, B->norms[index])); + m->n = c2Neg(B->norms[index]); } } // Forms a c2Poly and uses c2PolytoPolyManifold -void c2AABBtoPolyManifold(c2AABB A, const c2Poly* B, const c2x* bx, c2Manifold* m) +void c2AABBtoPolyManifold(c2AABB A, const c2Poly* B, c2Manifold* m) { m->count = 0; c2Poly p; c2BBVerts(p.verts, &A); p.count = 4; c2Norms(p.verts, p.norms, 4); - c2PolytoPolyManifold(&p, 0, B, bx, m); + c2PolytoPolyManifold(&p, B, m); } // clip a segment to a plane @@ -1983,10 +1962,10 @@ static int c2SidePlanes(c2v* seg, c2v ra, c2v rb, c2h* h) // clip a segment to the "side planes" of another segment. // side planes are planes orthogonal to a segment and attached to the // endpoints of the segment -static int c2SidePlanesFromPoly(c2v* seg, c2x x, const c2Poly* p, int e, c2h* h) +static int c2SidePlanesFromPoly(c2v* seg, const c2Poly* p, int e, c2h* h) { - c2v ra = c2Mulxv(x, p->verts[e]); - c2v rb = c2Mulxv(x, p->verts[e + 1 == p->count ? 0 : e + 1]); + c2v ra = p->verts[e]; + c2v rb = p->verts[e + 1 == p->count ? 0 : e + 1]; return c2SidePlanes(seg, ra, rb, h); } @@ -2038,7 +2017,7 @@ static void c2AntinormalFace(c2Capsule cap, const c2Poly* p, c2x x, int* face_ou *n_out = n; } -static void c2Incident(c2v* incident, const c2Poly* ip, c2x ix, c2v rn_in_incident_space) +static void c2Incident(c2v* incident, const c2Poly* ip, c2v rn_in_incident_space) { int index = ~0; float min_dot = FLT_MAX; @@ -2051,35 +2030,31 @@ static void c2Incident(c2v* incident, const c2Poly* ip, c2x ix, c2v rn_in_incide index = i; } } - incident[0] = c2Mulxv(ix, ip->verts[index]); - incident[1] = c2Mulxv(ix, ip->verts[index + 1 == ip->count ? 0 : index + 1]); + incident[0] = ip->verts[index]; + incident[1] = ip->verts[index + 1 == ip->count ? 0 : index + 1]; } -void c2CapsuletoPolyManifold(c2Capsule A, const c2Poly* B, const c2x* bx_ptr, c2Manifold* m) +void c2CapsuletoPolyManifold(c2Capsule A, const c2Poly* B, c2Manifold* m) { m->count = 0; c2v a, b; - float d = c2GJK(&A, C2_TYPE_CAPSULE, 0, B, C2_TYPE_POLY, bx_ptr, &a, &b, 0, 0, 0); + float d = c2GJK(&A, C2_TYPE_CAPSULE, B, C2_TYPE_POLY, &a, &b, 0, 0, 0); // deep, treat as segment to poly collision if (d < 1.0e-6f) { - c2x bx = bx_ptr ? *bx_ptr : c2xIdentity(); - c2Capsule A_in_B; - A_in_B.a = c2MulxvT(bx, A.a); - A_in_B.b = c2MulxvT(bx, A.b); - c2v ab = c2Norm(c2Sub(A_in_B.a, A_in_B.b)); + c2v ab = c2Norm(c2Sub(A.a, A.b)); // test capsule axes c2h ab_h0; ab_h0.n = c2CCW90(ab); - ab_h0.d = c2Dot(A_in_B.a, ab_h0.n); + ab_h0.d = c2Dot(A.a, ab_h0.n); int v0 = c2Support(B->verts, B->count, c2Neg(ab_h0.n)); float s0 = c2Dist(ab_h0, B->verts[v0]); c2h ab_h1; ab_h1.n = c2Skew(ab); - ab_h1.d = c2Dot(A_in_B.a, ab_h1.n); + ab_h1.d = c2Dot(A.a, ab_h1.n); int v1 = c2Support(B->verts, B->count, c2Neg(ab_h1.n)); float s1 = c2Dist(ab_h1, B->verts[v1]); @@ -2090,11 +2065,11 @@ void c2CapsuletoPolyManifold(c2Capsule A, const c2Poly* B, const c2x* bx_ptr, c2 for (int i = 0; i < B->count; ++i) { c2h h = c2PlaneAt(B, i); - float da = c2Dot(A_in_B.a, c2Neg(h.n)); - float db = c2Dot(A_in_B.b, c2Neg(h.n)); + float da = c2Dot(A.a, c2Neg(h.n)); + float db = c2Dot(A.b, c2Neg(h.n)); float d; - if (da > db) d = c2Dist(h, A_in_B.a); - else d = c2Dist(h, A_in_B.b); + if (da > db) d = c2Dist(h, A.a); + else d = c2Dist(h, A.b); if (d > sep) { sep = d; @@ -2121,7 +2096,7 @@ void c2CapsuletoPolyManifold(c2Capsule A, const c2Poly* B, const c2x* bx_ptr, c2 { c2v seg[2] = { A.a, A.b }; c2h h; - if (!c2SidePlanesFromPoly(seg, bx, B, index, &h)) return; + if (!c2SidePlanesFromPoly(seg, B, index, &h)) return; c2KeepDeep(seg, h, m); m->n = c2Neg(m->n); } break; @@ -2129,18 +2104,18 @@ void c2CapsuletoPolyManifold(c2Capsule A, const c2Poly* B, const c2x* bx_ptr, c2 case 1: // side 0 of capsule segment { c2v incident[2]; - c2Incident(incident, B, bx, ab_h0.n); + c2Incident(incident, B, ab_h0.n); c2h h; - if (!c2SidePlanes(incident, A_in_B.b, A_in_B.a, &h)) return; + if (!c2SidePlanes(incident, A.b, A.a, &h)) return; c2KeepDeep(incident, h, m); } break; case 2: // side 1 of capsule segment { c2v incident[2]; - c2Incident(incident, B, bx, ab_h1.n); + c2Incident(incident, B, ab_h1.n); c2h h; - if (!c2SidePlanes(incident, A_in_B.a, A_in_B.b, &h)) return; + if (!c2SidePlanes(incident, A.a, A.b, &h)) return; c2KeepDeep(incident, h, m); } break; @@ -2166,18 +2141,16 @@ void c2CapsuletoPolyManifold(c2Capsule A, const c2Poly* B, const c2x* bx_ptr, c2 #pragma warning(pop) #endif -static float c2CheckFaces(const c2Poly* A, c2x ax, const c2Poly* B, c2x bx, int* face_index) +static float c2CheckFaces(const c2Poly* A, const c2Poly* B, int* face_index) { - c2x b_in_a = c2MulxxT(ax, bx); - c2x a_in_b = c2MulxxT(bx, ax); float sep = -FLT_MAX; int index = ~0; for (int i = 0; i < A->count; ++i) { c2h h = c2PlaneAt(A, i); - int idx = c2Support(B->verts, B->count, c2Mulrv(a_in_b.r, c2Neg(h.n))); - c2v p = c2Mulxv(b_in_a, B->verts[idx]); + int idx = c2Support(B->verts, B->count, c2Neg(h.n)); + c2v p = B->verts[idx]; float d = c2Dist(h, p); if (d > sep) { @@ -2198,40 +2171,37 @@ static float c2CheckFaces(const c2Poly* A, c2x ax, const c2Poly* B, c2x bx, int* // Find the incident face, which is most anti-normal face // Clip incident face to reference face side planes // Keep all points behind the reference face -void c2PolytoPolyManifold(const c2Poly* A, const c2x* ax_ptr, const c2Poly* B, const c2x* bx_ptr, c2Manifold* m) +void c2PolytoPolyManifold(const c2Poly* A, const c2Poly* B, c2Manifold* m) { m->count = 0; - c2x ax = ax_ptr ? *ax_ptr : c2xIdentity(); - c2x bx = bx_ptr ? *bx_ptr : c2xIdentity(); int ea, eb; float sa, sb; - if ((sa = c2CheckFaces(A, ax, B, bx, &ea)) >= 0) return; - if ((sb = c2CheckFaces(B, bx, A, ax, &eb)) >= 0) return; + if ((sa = c2CheckFaces(A, B, &ea)) >= 0) return; + if ((sb = c2CheckFaces(B, A, &eb)) >= 0) return; const c2Poly* rp,* ip; - c2x rx, ix; int re; float kRelTol = 0.95f, kAbsTol = 0.01f; int flip; if (sa * kRelTol > sb + kAbsTol) { - rp = A; rx = ax; - ip = B; ix = bx; + rp = A; + ip = B; re = ea; flip = 0; } else { - rp = B; rx = bx; - ip = A; ix = ax; + rp = B; + ip = A; re = eb; flip = 1; } c2v incident[2]; - c2Incident(incident, ip, ix, c2MulrvT(ix.r, c2Mulrv(rx.r, rp->norms[re]))); + c2Incident(incident, ip, rp->norms[re]); c2h rh; - if (!c2SidePlanesFromPoly(incident, rx, rp, re, &rh)) return; + if (!c2SidePlanesFromPoly(incident, rp, re, &rh)) return; c2KeepDeep(incident, rh, m); if (flip) m->n = c2Neg(m->n); } diff --git a/libraries/cute/cute_png.h b/libraries/cute/cute_png.h index 16926cd1a..d06cdd491 100644 --- a/libraries/cute/cute_png.h +++ b/libraries/cute/cute_png.h @@ -3,7 +3,7 @@ Licensing information can be found at the end of the file. ------------------------------------------------------------------------------ - cute_png.h - v1.05 + cute_png.h - v1.06 To create implementation (the function definitions) #define CUTE_PNG_IMPLEMENTATION @@ -25,6 +25,9 @@ 1.04 (08/23/2018) various bug fixes for filter and word decoder added `cp_load_blank` 1.05 (11/10/2022) added `cp_save_png_to_memory` + 1.06 (02/01/2026) upgraded PNG compression from RLE to LZ77 with hash + chains, providing 50-75x better compression for + typical images EXAMPLES: @@ -719,18 +722,101 @@ static uint8_t cp_paeth(uint8_t a, uint8_t b, uint8_t c) return (pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c; } +typedef struct cp_lz77_state_t cp_lz77_state_t; + typedef struct cp_save_png_data_t { uint32_t crc; uint32_t adler; uint32_t bits; - uint32_t prev; - uint32_t runlen; int buflen; int bufcap; char* buffer; + cp_lz77_state_t* lz; // LZ77 compression state } cp_save_png_data_t; +// LZ77 compression state - hash chain approach for pattern matching +typedef struct cp_lz77_state_t +{ + int head[4096]; // Hash table heads (-1 = no entry) + int prev[32768]; // Chain links for positions (32K sliding window) + uint8_t* window; // Points to input data + int window_size; // Total bytes available +} cp_lz77_state_t; + +// Hash function for 3-byte sequences - fast with good distribution +static uint32_t cp_lz77_hash(const uint8_t* p) +{ + return ((uint32_t)(p[0] << 8) ^ (uint32_t)(p[1] << 4) ^ (uint32_t)p[2]) & 0xFFF; +} + +// Initialize LZ77 state for compression +static void cp_lz77_init(cp_lz77_state_t* lz, uint8_t* data, int size) +{ + CUTE_PNG_MEMSET(lz->head, 0xFF, sizeof(lz->head)); // -1 = no entry + CUTE_PNG_MEMSET(lz->prev, 0xFF, sizeof(lz->prev)); // -1 = end of chain + lz->window = data; + lz->window_size = size; +} + +// Insert position into hash chain +static void cp_lz77_insert(cp_lz77_state_t* lz, int pos) +{ + if (pos + 2 >= lz->window_size) return; + uint32_t hash = cp_lz77_hash(lz->window + pos); + int slot = pos & 0x7FFF; + lz->prev[slot] = lz->head[hash]; + lz->head[hash] = pos; +} + +// Find longest match in sliding window, returns length (0 if none, 3+ if found) +static int cp_lz77_find_match(cp_lz77_state_t* lz, int pos, int max_len, int* out_distance) +{ + uint8_t* data = lz->window; + int best_len = 2; + int best_dist = 0; + int chain_count = 0; + const int max_chain = 256; + + if (max_len < 3) return 0; + if (max_len > 258) max_len = 258; + + uint32_t hash = cp_lz77_hash(data + pos); + int match_pos = lz->head[hash]; + if (match_pos < 0) return 0; + + while (match_pos >= 0 && chain_count < max_chain) + { + int dist = pos - match_pos; + if (dist > 32768 || dist <= 0) break; + + // Quick check: first and current-best bytes + if (data[match_pos] == data[pos] && data[match_pos + best_len] == data[pos + best_len]) + { + int len = 0; + while (len < max_len && data[match_pos + len] == data[pos + len]) + len++; + + if (len > best_len) + { + best_len = len; + best_dist = dist; + if (len >= max_len) break; + } + } + + match_pos = lz->prev[match_pos & 0x7FFF]; + chain_count++; + } + + if (best_len >= 3) + { + *out_distance = best_dist; + return best_len; + } + return 0; +} + uint32_t CP_CRC_TABLE[] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c @@ -799,48 +885,78 @@ static void cp_begin_chunk(cp_save_png_data_t* s, const char* id, uint32_t len) static void cp_encode_literal(cp_save_png_data_t* s, uint32_t v) { - // Encode a literal/length using the built-in tables. - // Could do better with a custom table but whatever. + // Encode a literal/length using fixed Huffman codes (RFC 1951) if (v < 144) cp_put_bitsr(s, 0x030 + v - 0, 8); else if (v < 256) cp_put_bitsr(s, 0x190 + v - 144, 9); else if (v < 280) cp_put_bitsr(s, 0x000 + v - 256, 7); else cp_put_bitsr(s, 0x0c0 + v - 280, 8); } -static void cp_encode_len(cp_save_png_data_t* s, uint32_t code, uint32_t bits, uint32_t len) +// Encode distance using fixed Huffman (5-bit codes, reversed bit order) +static void cp_encode_distance(cp_save_png_data_t* s, int distance) { - cp_encode_literal(s, code + (len >> bits)); - cp_put_bits(s, len, bits); - cp_put_bits(s, 0, 5); + int code = 0; + for (int i = 29; i >= 0; i--) + { + if (distance >= (int)cp_dist_base[i]) + { + code = i; + break; + } + } + cp_put_bitsr(s, code, 5); // Fixed distance codes are 5 bits, MSB first (reversed) + if (cp_dist_extra_bits[code] > 0) + cp_put_bits(s, distance - cp_dist_base[code], cp_dist_extra_bits[code]); } -static void cp_end_run(cp_save_png_data_t* s) +// Encode a length-distance match +static void cp_encode_match(cp_save_png_data_t* s, int length, int distance) { - s->runlen--; - cp_encode_literal(s, s->prev); - - if (s->runlen >= 67) cp_encode_len(s, 277, 4, s->runlen - 67); - else if (s->runlen >= 35) cp_encode_len(s, 273, 3, s->runlen - 35); - else if (s->runlen >= 19) cp_encode_len(s, 269, 2, s->runlen - 19); - else if (s->runlen >= 11) cp_encode_len(s, 265, 1, s->runlen - 11); - else if (s->runlen >= 3) cp_encode_len(s, 257, 0, s->runlen - 3); - else while (s->runlen--) cp_encode_literal(s, s->prev); + int code = 0; + for (int i = 28; i >= 0; i--) + { + if (length >= (int)cp_len_base[i]) + { + code = i; + break; + } + } + cp_encode_literal(s, 257 + code); // Length codes start at 257 + if (cp_len_extra_bits[code] > 0) + cp_put_bits(s, length - cp_len_base[code], cp_len_extra_bits[code]); + cp_encode_distance(s, distance); } -static void cp_encode_byte(cp_save_png_data_t *s, uint8_t v) +// Compress buffer using LZ77 with hash chain matching +static void cp_lz77_compress(cp_save_png_data_t* s, const uint8_t* data, int len) { - cp_update_adler(s, v); + cp_lz77_state_t* lz = s->lz; + cp_lz77_init(lz, (uint8_t*)data, len); + int pos = 0; - // Simple RLE compression. We could do better by doing a search - // to find matches, but this works pretty well TBH. - if (s->prev == v && s->runlen < 115) s->runlen++; - - else + while (pos < len) { - if (s->runlen) cp_end_run(s); + cp_update_adler(s, data[pos]); + + int distance; + int match_len = cp_lz77_find_match(lz, pos, len - pos, &distance); - s->prev = v; - s->runlen = 1; + if (match_len >= 3) + { + cp_encode_match(s, match_len, distance); + for (int i = 0; i < match_len; i++) + { + if (i > 0) cp_update_adler(s, data[pos + i]); + cp_lz77_insert(lz, pos + i); + } + pos += match_len; + } + else + { + cp_encode_literal(s, data[pos]); + cp_lz77_insert(lz, pos); + pos++; + } } } @@ -863,47 +979,61 @@ static void cp_save_header(cp_save_png_data_t* s, cp_image_t* img) static void cp_save_data(cp_save_png_data_t* s, cp_image_t* img, long dataPos, long* dataSize) { - cp_begin_chunk(s, "IDAT", 0); - cp_put8(s, 0x08); // zlib compression method - cp_put8(s, 0x1D); // zlib compression flags - cp_put_bits(s, 3, 3); // zlib last block + fixed dictionary + int row_bytes = 1 + img->w * 4; // filter byte + RGBA per pixel + int total_bytes = row_bytes * img->h; + uint8_t* filtered = (uint8_t*)CUTE_PNG_ALLOC(total_bytes); + int pos = 0; + // Apply Sub filter to all rows and buffer the result for (int y = 0; y < img->h; ++y) { - cp_pixel_t *row = &img->pix[y * img->w]; + cp_pixel_t* row = &img->pix[y * img->w]; cp_pixel_t prev = cp_make_pixel_a(0, 0, 0, 0); - cp_encode_byte(s, 1); // sub filter + filtered[pos++] = 1; // sub filter for (int x = 0; x < img->w; ++x) { - cp_encode_byte(s, row[x].r - prev.r); - cp_encode_byte(s, row[x].g - prev.g); - cp_encode_byte(s, row[x].b - prev.b); - cp_encode_byte(s, row[x].a - prev.a); + filtered[pos++] = (uint8_t)(row[x].r - prev.r); + filtered[pos++] = (uint8_t)(row[x].g - prev.g); + filtered[pos++] = (uint8_t)(row[x].b - prev.b); + filtered[pos++] = (uint8_t)(row[x].a - prev.a); prev = row[x]; } } - cp_end_run(s); - cp_encode_literal(s, 256); // terminator + cp_begin_chunk(s, "IDAT", 0); + cp_put8(s, 0x08); // zlib compression method + cp_put8(s, 0x1D); // zlib compression flags + cp_put_bits(s, 3, 3); // zlib last block + fixed dictionary + + cp_lz77_compress(s, filtered, pos); + + cp_encode_literal(s, 256); // end of block while (s->bits != 0x80) cp_put_bits(s, 0, 1); cp_put32(s, s->adler); *dataSize = (s->buflen - dataPos) - 8; cp_put32(s, ~s->crc); + + CUTE_PNG_FREE(filtered); } cp_saved_png_t cp_save_png_to_memory(const cp_image_t* img) { cp_saved_png_t result = { 0 }; cp_save_png_data_t s = { 0 }; + cp_lz77_state_t* lz = NULL; long dataPos, dataSize, fileSize; if (!img) return result; + // Allocate LZ77 state (~72KB) + lz = (cp_lz77_state_t*)CUTE_PNG_ALLOC(sizeof(cp_lz77_state_t)); + if (!lz) return result; + s.adler = 1; s.bits = 0x80; - s.prev = 0xFFFF; s.bufcap = 1024; s.buffer = (char*)CUTE_PNG_ALLOC(1024); + s.lz = lz; cp_save_header(&s, (cp_image_t*)img); dataPos = s.buflen; @@ -918,6 +1048,8 @@ cp_saved_png_t cp_save_png_to_memory(const cp_image_t* img) s.buflen = dataPos; cp_put32(&s, dataSize); + CUTE_PNG_FREE(lz); + result.size = fileSize; result.data = s.buffer; return result; @@ -1650,9 +1782,11 @@ cp_image_t cp_make_atlas(int atlas_width, int atlas_height, const cp_image_t* pn cp_atlas_node_t* new_nodes = (cp_atlas_node_t*)CUTE_PNG_ALLOC(sizeof(cp_atlas_node_t) * new_capacity); CUTE_PNG_CHECK(new_nodes, "out of mem"); CUTE_PNG_MEMCPY(new_nodes, nodes, sizeof(cp_atlas_node_t) * sp); - CUTE_PNG_FREE(nodes); + // best_fit became a dangling pointer, so relocate it best_fit = new_nodes + (best_fit - nodes); + CUTE_PNG_FREE(nodes); + nodes = new_nodes; atlas_node_capacity = new_capacity; } @@ -1814,7 +1948,7 @@ int cp_default_save_atlas(const char* out_path_image, const char* out_path_atlas This software is available under 2 licenses - you may choose the one you like. ------------------------------------------------------------------------------ ALTERNATIVE A - zlib license - Copyright (c) 2019 Randy Gaul http://www.randygaul.net + Copyright (c) 2026 Randy Gaul https://randygaul.github.io 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. diff --git a/libraries/cute/cute_sound.h b/libraries/cute/cute_sound.h index 62e463e42..39981b485 100644 --- a/libraries/cute/cute_sound.h +++ b/libraries/cute/cute_sound.h @@ -3,8 +3,7 @@ Licensing information can be found at the end of the file. ------------------------------------------------------------------------------ - cute_sound.h - v2.08 - + cute_sound.h - v3.00 To create implementation (the function definitions) #define CUTE_SOUND_IMPLEMENTATION @@ -14,37 +13,8 @@ SUMMARY cute_sound is a C API for loading, playing, looping, panning and fading mono - and stereo sounds, without any external dependencies other than things that ship - with standard OSs, or SDL2 for more uncommon OSs. - - While platform detection is done automatically, you are able to explicitly - specify which to use by defining one of the following: - - #define CUTE_SOUND_PLATFORM_WINDOWS - #define CUTE_SOUND_PLATFORM_APPLE - #define CUTE_SOUND_PLATFORM_SDL - - For Windows cute_sound uses DirectSound. Due to the use of SSE intrinsics, MinGW - builds must be made with the compiler option: -march=native, or optionally SSE - can be disabled with CUTE_SOUND_SCALAR_MODE. More on this mode written below. - - For Apple machines cute_sound uses CoreAudio. - - For Linux builds cute_sound uses SDL2. - - An SDL2 implementation of cute_sound is available on platforms SDL2 is available, - which is pretty much everywhere. To use the SDL2 implementation of cute_sound - define CUTE_SOUND_FORCE_SDL before placing the CUTE_SOUND_IMPLEMENTATION into a - translation unit in order to force the use of SDL. Here is an example: - - #define CUTE_SOUND_FORCE_SDL - #define CUTE_SOUND_IMPLEMENTATION - #include - - If you want to use cute_sound with SDL_RWops, you must enable it by putting this - before you #include cute_sound.h: - - #define CUTE_SOUND_SDL_RWOPS + and stereo sounds. It requires SDL3 for audio output, and optionally + stb_vorbis.c for ogg file loading. REVISION HISTORY @@ -105,6 +75,16 @@ 2.08 (08/07/2024) Added sample_index to sound params, removed unnecessary asserts for stopping music, added callbacks sounds/music ending 2.09 (08/10/2024) Upgrade to SDL3. + 3.00 (02/01/2026) Removed platform-specific code, now SDL3 only. + * Simplified hashtable and removed redundant circular buffer. + * Fixed SIMD sample loading bugs causing audio popping. + * Fixed pitch shift bug at end of tracks (interpolate beyond last sample). + * Refactored mixer to be simpler. + * Replaced sample_index API with time values in seconds. + * Fixed looped/reverse playback pitch shifting (audio crackling). + * Removed cs_cull_duplicates (better handled user-side). + * Fixed music fade to incorporate user volume. + * WAV loader now supports 8/16/24/32-bit PCM and 32/64-bit float. CONTRIBUTORS @@ -117,7 +97,7 @@ Matt Rosen 1.10 - Initial experiments with cute_dsp to figure out plugin interface needs and use-cases fluffrabbit 1.11 - scalar SIMD mode and various compiler warning/error fixes - Daniel Guzman 2.01 - compilation fixes for clang/llvm on MAC. + Daniel Guzman 2.01 - compilation fixes for clang/llvm on MAC. Brie 2.06 - Looping sound rollover ogam x.xx - Lots of bugfixes over time, including support negative pitch @@ -130,17 +110,17 @@ 2. load sounds from disk into memory (call cs_load_wav, or cs_load_ogg with stb_vorbis.c) 3. play sounds (cs_play_sound), or music (cs_music_play) - DISABLE SSE SIMD ACCELERATION + DISABLE SSE/NEON SIMD ACCELERATION - If for whatever reason you don't want to use SSE intrinsics and instead would prefer - plain C (for example if your platform does not support SSE) then define + If for whatever reason you don't want to use SIMD intrinsics and instead would prefer + plain C (for example if your platform does not support SSE/NEON) then define CUTE_SOUND_SCALAR_MODE before including cute_sound.h while also defining the symbol definitions. Here's an example: - #define CUTE_SOUND_IMPLEMENTATION #define CUTE_SOUND_SCALAR_MODE + #define CUTE_SOUND_IMPLEMENTATION #include - + CUSTOMIZATION A few different macros can be overriden to provide custom functionality. Simply define any of these @@ -172,41 +152,6 @@ * Mixer does not do any fancy clipping. The algorithm is to convert all 16 bit samples to float, mix all samples, and write back to audio API as 16 bit integers. In practice this works very well and clipping is not often a big problem. - - - FAQ - - Q : I would like to use my own memory management, how can I achieve this? - A : This header makes a couple uses of malloc/free, and cs_malloc16/cs_free16. Simply find these bits - and replace them with your own memory allocation routines. They can be wrapped up into a macro, - or call your own functions directly -- it's up to you. Generally these functions allocate fairly - large chunks of memory, and not very often (if at all). - - Q : Does this library support audio streaming? Something like System::createStream in FMOD. - A : No. Typically music files aren't that large (in the megabytes). Compare this to a typical - DXT texture of 1024x1024, at 0.5MB of memory. Now say an average music file for a game is three - minutes long. Loading this file into memory and storing it as raw 16bit samples with two channels, - would be: - - num_samples = 3 * 60 * 44100 * 2 - num_bits = num_samples * 16 - num_bytes = num_bits / 8 - num_megabytes = num_bytes / (1024 * 1024) - or 30.3mb - - So say the user has 2gb of spare RAM. That means we could fit 67 different three minute length - music files in there simultaneously. That is a ridiculous amount of spare memory. 30mb is nothing - nowadays. Just load your music file into memory all at once and then play it. - - Q : But I really need streaming of audio files from disk to save memory! Also loading my audio - files (like .OGG) takes a long time (multiple seconds). - A : It is recommended to either A) load up your music files before they are needed, perhaps during - a loading screen, or B) stream in the entire audio into memory on another thread. cs_read_mem_ogg is - a great candidate function to throw onto a job pool. Streaming is more a remnant of older machines - (like in the 90's or early 2000's) where decoding speed and RAM were real nasty bottlenecks. For - more modern machines, these aren't really concerns, even with mobile devices. If even after reading - this Q/A section you still want to stream your audio, you can try mini_al as an alternative: - https://github.com/dr-soft/mini_al */ #if !defined(CUTE_SOUND_H) @@ -230,33 +175,22 @@ typedef enum cs_error_t { CUTE_SOUND_ERROR_NONE, - CUTE_SOUND_ERROR_IMPLEMENTATION_ERROR_PLEASE_REPORT_THIS_ON_GITHUB, // https://github.com/RandyGaul/cute_headers/issues CUTE_SOUND_ERROR_FILE_NOT_FOUND, CUTE_SOUND_ERROR_INVALID_SOUND, - CUTE_SOUND_ERROR_HWND_IS_NULL, - CUTE_SOUND_ERROR_DIRECTSOUND_CREATE_FAILED, - CUTE_SOUND_ERROR_CREATESOUNDBUFFER_FAILED, - CUTE_SOUND_ERROR_SETFORMAT_FAILED, - CUTE_SOUND_ERROR_AUDIOCOMPONENTFINDNEXT_FAILED, - CUTE_SOUND_ERROR_AUDIOCOMPONENTINSTANCENEW_FAILED, - CUTE_SOUND_ERROR_FAILED_TO_SET_STREAM_FORMAT, - CUTE_SOUND_ERROR_FAILED_TO_SET_RENDER_CALLBACK, - CUTE_SOUND_ERROR_AUDIOUNITINITIALIZE_FAILED, - CUTE_SOUND_ERROR_AUDIOUNITSTART_FAILED, CUTE_SOUND_ERROR_CANT_OPEN_AUDIO_DEVICE, CUTE_SOUND_ERROR_CANT_INIT_SDL_AUDIO, CUTE_SOUND_ERROR_THE_FILE_IS_NOT_A_WAV_FILE, CUTE_SOUND_ERROR_WAV_FILE_FORMAT_CHUNK_NOT_FOUND, CUTE_SOUND_ERROR_WAV_DATA_CHUNK_NOT_FOUND, - CUTE_SOUND_ERROR_ONLY_PCM_WAV_FILES_ARE_SUPPORTED, CUTE_SOUND_ERROR_WAV_ONLY_MONO_OR_STEREO_IS_SUPPORTED, - CUTE_SOUND_ERROR_WAV_ONLY_16_BITS_PER_SAMPLE_SUPPORTED, + CUTE_SOUND_ERROR_WAV_UNSUPPORTED_FORMAT, CUTE_SOUND_ERROR_CANNOT_SWITCH_MUSIC_WHILE_PAUSED, CUTE_SOUND_ERROR_CANNOT_CROSSFADE_WHILE_MUSIC_IS_PAUSED, CUTE_SOUND_ERROR_CANNOT_FADEOUT_WHILE_MUSIC_IS_PAUSED, CUTE_SOUND_ERROR_TRIED_TO_SET_SAMPLE_INDEX_BEYOND_THE_AUDIO_SOURCES_SAMPLE_COUNT, CUTE_SOUND_ERROR_STB_VORBIS_DECODE_FAILED, CUTE_SOUND_ERROR_OGG_UNSUPPORTED_CHANNEL_COUNT, + CUTE_SOUND_ERROR_IMPLEMENTATION_ERROR_PLEASE_REPORT_THIS_ON_GITHUB, // https://github.com/RandyGaul/cute_headers/blob/master/cute_sound.h } cs_error_t; const char* cs_error_as_string(cs_error_t error); @@ -265,11 +199,9 @@ const char* cs_error_as_string(cs_error_t error); // Cute sound context functions. /** - * Pass in NULL for `os_handle`, except for the DirectSound backend this should be hwnd. - * play_frequency_in_Hz depends on your audio file, 44100 seems to be fine. - * buffered_samples is clamped to be at least 1024. + * play_frequency_in_Hz depends on your audio files, 44100 is typical. */ -cs_error_t cs_init(void* os_handle, unsigned play_frequency_in_Hz, int buffered_samples, void* user_allocator_context /* = NULL */); +cs_error_t cs_init(unsigned play_frequency_in_Hz, void* user_allocator_context /* = NULL */); void cs_shutdown(); /** @@ -280,18 +212,6 @@ void cs_set_global_volume(float volume_0_to_1); void cs_set_global_pan(float pan_0_to_1); void cs_set_global_pause(bool true_for_paused); -/** - * Spawns a mixing thread dedicated to mixing audio in the background. - * If you don't call this function mixing will happen on the main-thread when you call `cs_update`. - */ -void cs_spawn_mix_thread(); - -/** - * In cases where the mixing thread takes up extra CPU attention doing nothing, you can force - * it to sleep manually. You can tune this as necessary, but it's probably not necessary for you. - */ -void cs_mix_thread_sleep_delay(int milliseconds); - /** * Sometimes useful for dynamic library shenanigans. */ @@ -303,6 +223,19 @@ void cs_set_context_ptr(void* ctx); typedef struct cs_audio_source_t cs_audio_source_t; +/** + * Load a WAV file from disk or memory. + * + * Supported WAV formats: + * - PCM 8-bit unsigned + * - PCM 16-bit signed + * - PCM 24-bit signed + * - PCM 32-bit signed + * - IEEE float 32-bit + * - IEEE float 64-bit + * + * Only mono and stereo channel configurations are supported. + */ cs_audio_source_t* cs_load_wav(const char* path, cs_error_t* err /* = NULL */); cs_audio_source_t* cs_read_mem_wav(const void* memory, size_t size, cs_error_t* err /* = NULL */); void cs_free_audio_source(cs_audio_source_t* audio); @@ -350,8 +283,8 @@ void cs_music_set_pitch(float pitch /* = 1.0f */); void cs_music_set_loop(bool true_to_loop); void cs_music_switch_to(cs_audio_source_t* audio, float fade_out_time /* = 0 */, float fade_in_time /* = 0 */); void cs_music_crossfade(cs_audio_source_t* audio, float cross_fade_time /* = 0 */); -int cs_music_get_sample_index(); -cs_error_t cs_music_set_sample_index(int sample_index); +double cs_music_get_time(); +cs_error_t cs_music_set_time(double time_in_seconds); // ------------------------------------------------------------------------------------------------- // Playing sounds. @@ -366,7 +299,7 @@ typedef struct cs_sound_params_t float volume /* = 1.0f */; float pan /* = 0.5f */; // Can be from 0 to 1. float pitch /* = 1.0f */; - int sample_index /* = 0 */; + double start_time /* = 0.0 */; // Start time in seconds. } cs_sound_params_t; cs_sound_params_t cs_sound_params_default(); @@ -393,26 +326,18 @@ bool cs_sound_get_is_looped(cs_playing_sound_t sound); float cs_sound_get_volume(cs_playing_sound_t sound); float cs_sound_get_pitch(cs_playing_sound_t sound); float cs_sound_get_pan(cs_playing_sound_t sound); -int cs_sound_get_sample_index(cs_playing_sound_t sound); +double cs_sound_get_time(cs_playing_sound_t sound); void cs_sound_set_is_paused(cs_playing_sound_t sound, bool true_for_paused); void cs_sound_set_is_looped(cs_playing_sound_t sound, bool true_for_looped); void cs_sound_set_volume(cs_playing_sound_t sound, float volume_0_to_1); void cs_sound_set_pan(cs_playing_sound_t sound, float pan_0_to_1); void cs_sound_set_pitch(cs_playing_sound_t sound, float pitch); -cs_error_t cs_sound_set_sample_index(cs_playing_sound_t sound, int sample_index); +cs_error_t cs_sound_set_time(cs_playing_sound_t sound, double time_in_seconds); void cs_sound_stop(cs_playing_sound_t sound); void cs_set_playing_sounds_volume(float volume_0_to_1); void cs_stop_all_playing_sounds(); -/** - * Off by default. When enabled only one instance of audio can be created per audio update-tick. This - * does *not* take into account the starting sample index, so disable this feature if you want to spawn - * audio with dynamic sample indices, such as when syncing programmatically generated scores/sequences. - * This also does not take into account pitch differences. - */ -void cs_cull_duplicates(bool true_to_enable); - // ------------------------------------------------------------------------------------------------- // Global context. @@ -429,10 +354,14 @@ void cs_set_global_user_allocator_context(void* user_allocator_context); #ifndef CUTE_SOUND_IMPLEMENTATION_ONCE #define CUTE_SOUND_IMPLEMENTATION_ONCE -#ifndef CUTE_SOUND_MINIMUM_BUFFERED_SAMPLES -# define CUTE_SOUND_MINIMUM_BUFFERED_SAMPLES 1024 +// Internal mixer buffer size in samples. 4096 samples at 44100Hz is ~93ms. +#ifndef CUTE_SOUND_MIXER_BUFFER_SIZE +# define CUTE_SOUND_MIXER_BUFFER_SIZE 4096 #endif +// Bytes per sample frame (16-bit stereo = 4 bytes). +#define CUTE_SOUND_BYTES_PER_SAMPLE_FRAME 4 + #if !defined(CUTE_SOUND_ASSERT) # include # define CUTE_SOUND_ASSERT assert @@ -503,90 +432,12 @@ void cs_set_global_user_allocator_context(void* user_allocator_context); # define CUTE_SOUND_FCLOSE fclose #endif -// Platform detection. -#define CUTE_SOUND_WINDOWS 1 -#define CUTE_SOUND_APPLE 2 -#define CUTE_SOUND_SDL 3 - -// Use CUTE_SOUND_FORCE_SDL as a way to force CUTE_SOUND_PLATFORM_SDL. -#ifdef CUTE_SOUND_FORCE_SDL - #define CUTE_SOUND_PLATFORM_SDL -#endif - -#ifndef CUTE_SOUND_PLATFORM - // Check the specific platform defines. - #ifdef CUTE_SOUND_PLATFORM_WINDOWS - #define CUTE_SOUND_PLATFORM CUTE_SOUND_WINDOWS - #elif defined(CUTE_SOUND_PLATFORM_APPLE) - #define CUTE_SOUND_PLATFORM CUTE_SOUND_APPLE - #elif defined(CUTE_SOUND_PLATFORM_SDL) - #define CUTE_SOUND_PLATFORM CUTE_SOUND_SDL - #else - // Detect the platform automatically. - #if defined(_WIN32) - #if !defined _CRT_SECURE_NO_WARNINGS - #define _CRT_SECURE_NO_WARNINGS - #endif - #if !defined _CRT_NONSTDC_NO_DEPRECATE - #define _CRT_NONSTDC_NO_DEPRECATE - #endif - #define CUTE_SOUND_PLATFORM CUTE_SOUND_WINDOWS - #elif defined(__APPLE__) - #define CUTE_SOUND_PLATFORM CUTE_SOUND_APPLE - #else - // Just use SDL on other esoteric platforms. - #define CUTE_SOUND_PLATFORM CUTE_SOUND_SDL - #endif +// SDL3 is required. +#ifndef SDL_h_ + #ifndef CUTE_SOUND_SDL_H + #define CUTE_SOUND_SDL_H #endif -#endif - -// Platform specific file inclusions. -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - - - #ifndef _WINDOWS_ - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #include - #endif - - #ifndef _WAVEFORMATEX_ - #include - #include - #endif - - #include - #undef PlaySound - - #ifdef _MSC_VER - #pragma comment(lib, "dsound.lib") - #endif - -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE - - #include - #include - #include - #include - -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL - - #ifndef SDL_h_ - // Define CUTE_SOUND_SDL_H to allow changing the SDL.h path. - #ifndef CUTE_SOUND_SDL_H - #define CUTE_SOUND_SDL_H - #endif - #include CUTE_SOUND_SDL_H - #endif - #ifndef _WIN32 - #include - #endif - -#else - - #error Unsupported platform - please choose one of CUTE_SOUND_WINDOWS, CUTE_SOUND_APPLE, CUTE_SOUND_SDL. - + #include CUTE_SOUND_SDL_H #endif // Automatically select SSE/NEON, or default to scalar if `CUTE_SOUND_SCALAR_MODE` is defined. @@ -609,6 +460,11 @@ void cs_set_global_user_allocator_context(void* user_allocator_context); #define cs_mm_cvttps_epi32(a) vcvtq_s32_f32(a) #define cs_mm_cvtepi32_ps(a) vcvtq_f32_s32(a) #define cs_mm_extract_epi32(a, imm8) vgetq_lane_s32(a, imm8) + #define cs_mm_set1_epi32(a) vdupq_n_s32(a) + #define cs_mm_sub_epi32(a, b) vsubq_s32(a, b) + #define cs_mm_and_si128(a, b) vandq_s32(a, b) + #define cs_mm_cmplt_ps(a, b) vreinterpretq_f32_u32(vcltq_f32(a, b)) + #define cs_mm_castps_si128(a) vreinterpretq_s32_f32(a) #elif !defined(CUTE_SOUND_SCALAR_MODE) && defined(__SSE__) || defined(__SSE2__) || defined(__SSE3__) || defined(__SSE4_1__) || defined(__SSE4_2__) @@ -629,6 +485,11 @@ void cs_set_global_user_allocator_context(void* user_allocator_context); #define cs_mm_cvttps_epi32 _mm_cvttps_epi32 #define cs_mm_cvtepi32_ps _mm_cvtepi32_ps #define cs_mm_extract_epi32 _mm_extract_epi32 + #define cs_mm_set1_epi32 _mm_set1_epi32 + #define cs_mm_sub_epi32 _mm_sub_epi32 + #define cs_mm_and_si128 _mm_and_si128 + #define cs_mm_cmplt_ps _mm_cmplt_ps + #define cs_mm_castps_si128 _mm_castps_si128 #else // Scalar mode as fallback. @@ -666,16 +527,6 @@ void cs_set_global_user_allocator_context(void* user_allocator_context); return a; } - cs__m128 cs_mm_load_ps(float const* mem_addr) - { - cs__m128 a; - a.a = mem_addr[0]; - a.b = mem_addr[1]; - a.c = mem_addr[2]; - a.d = mem_addr[3]; - return a; - } - cs__m128 cs_mm_add_ps(cs__m128 a, cs__m128 b) { cs__m128 c; @@ -784,570 +635,197 @@ void cs_set_global_user_allocator_context(void* user_allocator_context); } } -#endif // End of SIMD wrappers. - -#define CUTE_SOUND_ALIGN(X, Y) ((((size_t)X) + ((Y) - 1)) & ~((Y) - 1)) -#define CUTE_SOUND_TRUNC(X, Y) ((size_t)(X) & ~((Y) - 1)) - -// ------------------------------------------------------------------------------------------------- -// hashtable.h implementation by Mattias Gustavsson -// See: http://www.mattiasgustavsson.com/ and https://github.com/mattiasgustavsson/libs/blob/master/hashtable.h -// begin hashtable.h - -#ifndef CS_HASHTABLEMEMSET - #define CS_HASHTABLEMEMSET(ptr, val, n) CUTE_SOUND_MEMSET(ptr, val, n) -#endif - -#ifndef CS_HASHTABLEMEMCPY - #define CS_HASHTABLEMEMCPY(dst, src, n) CUTE_SOUND_MEMCPY(dst, src, n) -#endif - -#ifndef CS_HASHTABLEMALLOC - #define CS_HASHTABLEMALLOC(ctx, size) CUTE_SOUND_ALLOC(size, ctx) -#endif - -#ifndef CS_HASHTABLEFREE - #define CS_HASHTABLEFREE(ctx, ptr) CUTE_SOUND_FREE(ptr, ctx) -#endif - -/* ------------------------------------------------------------------------------- - Licensing information can be found at the end of the file. ------------------------------------------------------------------------------- -hashtable.h - v1.1 - Cache efficient hash table implementation for C/C++. -Do this: - #define CS_HASHTABLEIMPLEMENTATION -before you include this file in *one* C/C++ file to create the implementation. -*/ - -#ifndef cs_hashtableh -#define cs_hashtableh - -#ifndef CS_HASHTABLEU64 - #define CS_HASHTABLEU64 unsigned long long -#endif - -typedef struct cs_hashtablet cs_hashtablet; - -static void cs_hashtableinit( cs_hashtablet* table, int item_size, int initial_capacity, void* memctx ); -static void cs_hashtableterm( cs_hashtablet* table ); - -static void* cs_hashtableinsert( cs_hashtablet* table, CS_HASHTABLEU64 key, void const* item ); -static void cs_hashtableremove( cs_hashtablet* table, CS_HASHTABLEU64 key ); -static void cs_hashtableclear( cs_hashtablet* table ); - -static void* cs_hashtablefind( cs_hashtablet const* table, CS_HASHTABLEU64 key ); - -static int cs_hashtablecount( cs_hashtablet const* table ); -static void* cs_hashtableitems( cs_hashtablet const* table ); -static CS_HASHTABLEU64 const* cs_hashtablekeys( cs_hashtablet const* table ); - -static void cs_hashtableswap( cs_hashtablet* table, int index_a, int index_b ); - - -#endif /* cs_hashtableh */ - -/* ----------------------- - IMPLEMENTATION ----------------------- -*/ - -#ifndef cs_hashtablet_h -#define cs_hashtablet_h - -#ifndef CS_HASHTABLEU32 - #define CS_HASHTABLEU32 unsigned int -#endif - -struct cs_hashtableinternal_slot_t - { - CS_HASHTABLEU32 key_hash; - int item_index; - int base_count; - }; - -struct cs_hashtablet - { - void* memctx; - int count; - int item_size; - - struct cs_hashtableinternal_slot_t* slots; - int slot_capacity; - - CS_HASHTABLEU64* items_key; - int* items_slot; - void* items_data; - int item_capacity; - - void* swap_temp; - }; - -#endif /* cs_hashtablet_h */ - -// end hashtable.h - -#define CS_HASHTABLEIMPLEMENTATION - -#ifdef CS_HASHTABLEIMPLEMENTATION -#ifndef CS_HASHTABLEIMPLEMENTATION_ONCE -#define CS_HASHTABLEIMPLEMENTATION_ONCE - -// hashtable.h implementation by Mattias Gustavsson -// See: http://www.mattiasgustavsson.com/ and https://github.com/mattiasgustavsson/libs/blob/master/hashtable.h -// begin hashtable.h (continuing from first time) - -#ifndef CS_HASHTABLESIZE_T - #include - #define CS_HASHTABLESIZE_T size_t -#endif - -#ifndef CS_HASHTABLEASSERT - #include - #define CS_HASHTABLEASSERT( x ) assert( x ) -#endif - -#ifndef CS_HASHTABLEMEMSET - #include - #define CS_HASHTABLEMEMSET( ptr, val, cnt ) ( memset( ptr, val, cnt ) ) -#endif - -#ifndef CS_HASHTABLEMEMCPY - #include - #define CS_HASHTABLEMEMCPY( dst, src, cnt ) ( memcpy( dst, src, cnt ) ) -#endif - -#ifndef CS_HASHTABLEMALLOC - #include - #define CS_HASHTABLEMALLOC( ctx, size ) ( malloc( size ) ) - #define CS_HASHTABLEFREE( ctx, ptr ) ( free( ptr ) ) -#endif - - -static CS_HASHTABLEU32 cs_hashtableinternal_pow2ceil( CS_HASHTABLEU32 v ) - { - --v; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - ++v; - v += ( v == 0 ); - return v; - } - - -static void cs_hashtableinit( cs_hashtablet* table, int item_size, int initial_capacity, void* memctx ) - { - initial_capacity = (int)cs_hashtableinternal_pow2ceil( initial_capacity >=0 ? (CS_HASHTABLEU32) initial_capacity : 32U ); - table->memctx = memctx; - table->count = 0; - table->item_size = item_size; - table->slot_capacity = (int) cs_hashtableinternal_pow2ceil( (CS_HASHTABLEU32) ( initial_capacity + initial_capacity / 2 ) ); - int slots_size = (int)( table->slot_capacity * sizeof( *table->slots ) ); - table->slots = (struct cs_hashtableinternal_slot_t*) CS_HASHTABLEMALLOC( table->memctx, (CS_HASHTABLESIZE_T) slots_size ); - CS_HASHTABLEASSERT( table->slots ); - CS_HASHTABLEMEMSET( table->slots, 0, (CS_HASHTABLESIZE_T) slots_size ); - table->item_capacity = (int) cs_hashtableinternal_pow2ceil( (CS_HASHTABLEU32) initial_capacity ); - table->items_key = (CS_HASHTABLEU64*) CS_HASHTABLEMALLOC( table->memctx, - table->item_capacity * ( sizeof( *table->items_key ) + sizeof( *table->items_slot ) + table->item_size ) + table->item_size ); - CS_HASHTABLEASSERT( table->items_key ); - table->items_slot = (int*)( table->items_key + table->item_capacity ); - table->items_data = (void*)( table->items_slot + table->item_capacity ); - table->swap_temp = (void*)( ( (uintptr_t) table->items_data ) + table->item_size * table->item_capacity ); - } - - -static void cs_hashtableterm( cs_hashtablet* table ) - { - CS_HASHTABLEFREE( table->memctx, table->items_key ); - CS_HASHTABLEFREE( table->memctx, table->slots ); - } - - -// from https://gist.github.com/badboy/6267743 -static CS_HASHTABLEU32 cs_hashtableinternal_calculate_hash( CS_HASHTABLEU64 key ) - { - key = ( ~key ) + ( key << 18 ); - key = key ^ ( key >> 31 ); - key = key * 21; - key = key ^ ( key >> 11 ); - key = key + ( key << 6 ); - key = key ^ ( key >> 22 ); - CS_HASHTABLEASSERT( key ); - return (CS_HASHTABLEU32) key; - } - - -static int cs_hashtableinternal_find_slot( cs_hashtablet const* table, CS_HASHTABLEU64 key ) - { - int const slot_mask = table->slot_capacity - 1; - CS_HASHTABLEU32 const hash = cs_hashtableinternal_calculate_hash( key ); - - int const base_slot = (int)( hash & (CS_HASHTABLEU32)slot_mask ); - int base_count = table->slots[ base_slot ].base_count; - int slot = base_slot; - - while( base_count > 0 ) - { - CS_HASHTABLEU32 slot_hash = table->slots[ slot ].key_hash; - if( slot_hash ) - { - int slot_base = (int)( slot_hash & (CS_HASHTABLEU32)slot_mask ); - if( slot_base == base_slot ) - { - CS_HASHTABLEASSERT( base_count > 0 ); - --base_count; - if( slot_hash == hash && table->items_key[ table->slots[ slot ].item_index ] == key ) - return slot; - } - } - slot = ( slot + 1 ) & slot_mask; - } - - return -1; - } - - -static void cs_hashtableinternal_expand_slots( cs_hashtablet* table ) - { - int const old_capacity = table->slot_capacity; - struct cs_hashtableinternal_slot_t* old_slots = table->slots; - - table->slot_capacity *= 2; - int const slot_mask = table->slot_capacity - 1; - - int const size = (int)( table->slot_capacity * sizeof( *table->slots ) ); - table->slots = (struct cs_hashtableinternal_slot_t*) CS_HASHTABLEMALLOC( table->memctx, (CS_HASHTABLESIZE_T) size ); - CS_HASHTABLEASSERT( table->slots ); - CS_HASHTABLEMEMSET( table->slots, 0, (CS_HASHTABLESIZE_T) size ); - - for( int i = 0; i < old_capacity; ++i ) - { - CS_HASHTABLEU32 const hash = old_slots[ i ].key_hash; - if( hash ) - { - int const base_slot = (int)( hash & (CS_HASHTABLEU32)slot_mask ); - int slot = base_slot; - while( table->slots[ slot ].key_hash ) - slot = ( slot + 1 ) & slot_mask; - table->slots[ slot ].key_hash = hash; - int item_index = old_slots[ i ].item_index; - table->slots[ slot ].item_index = item_index; - table->items_slot[ item_index ] = slot; - ++table->slots[ base_slot ].base_count; - } - } - - CS_HASHTABLEFREE( table->memctx, old_slots ); - } - - -static void cs_hashtableinternal_expand_items( cs_hashtablet* table ) - { - table->item_capacity *= 2; - CS_HASHTABLEU64* const new_items_key = (CS_HASHTABLEU64*) CS_HASHTABLEMALLOC( table->memctx, - table->item_capacity * ( sizeof( *table->items_key ) + sizeof( *table->items_slot ) + table->item_size ) + table->item_size); - CS_HASHTABLEASSERT( new_items_key ); - - int* const new_items_slot = (int*)( new_items_key + table->item_capacity ); - void* const new_items_data = (void*)( new_items_slot + table->item_capacity ); - void* const new_swap_temp = (void*)( ( (uintptr_t) new_items_data ) + table->item_size * table->item_capacity ); - - CS_HASHTABLEMEMCPY( new_items_key, table->items_key, table->count * sizeof( *table->items_key ) ); - CS_HASHTABLEMEMCPY( new_items_slot, table->items_slot, table->count * sizeof( *table->items_key ) ); - CS_HASHTABLEMEMCPY( new_items_data, table->items_data, (CS_HASHTABLESIZE_T) table->count * table->item_size ); - - CS_HASHTABLEFREE( table->memctx, table->items_key ); - - table->items_key = new_items_key; - table->items_slot = new_items_slot; - table->items_data = new_items_data; - table->swap_temp = new_swap_temp; - } - - -static void* cs_hashtableinsert( cs_hashtablet* table, CS_HASHTABLEU64 key, void const* item ) - { - CS_HASHTABLEASSERT( cs_hashtableinternal_find_slot( table, key ) < 0 ); - - if( table->count >= ( table->slot_capacity - table->slot_capacity / 3 ) ) - cs_hashtableinternal_expand_slots( table ); - - int const slot_mask = table->slot_capacity - 1; - CS_HASHTABLEU32 const hash = cs_hashtableinternal_calculate_hash( key ); - - int const base_slot = (int)( hash & (CS_HASHTABLEU32)slot_mask ); - int base_count = table->slots[ base_slot ].base_count; - int slot = base_slot; - int first_free = slot; - while( base_count ) - { - CS_HASHTABLEU32 const slot_hash = table->slots[ slot ].key_hash; - if( slot_hash == 0 && table->slots[ first_free ].key_hash != 0 ) first_free = slot; - int slot_base = (int)( slot_hash & (CS_HASHTABLEU32)slot_mask ); - if( slot_base == base_slot ) - --base_count; - slot = ( slot + 1 ) & slot_mask; - } - - slot = first_free; - while( table->slots[ slot ].key_hash ) - slot = ( slot + 1 ) & slot_mask; - - if( table->count >= table->item_capacity ) - cs_hashtableinternal_expand_items( table ); - - CS_HASHTABLEASSERT( !table->slots[ slot ].key_hash && ( hash & (CS_HASHTABLEU32) slot_mask ) == (CS_HASHTABLEU32) base_slot ); - CS_HASHTABLEASSERT( hash ); - table->slots[ slot ].key_hash = hash; - table->slots[ slot ].item_index = table->count; - ++table->slots[ base_slot ].base_count; - - - void* dest_item = (void*)( ( (uintptr_t) table->items_data ) + table->count * table->item_size ); - CS_HASHTABLEMEMCPY( dest_item, item, (CS_HASHTABLESIZE_T) table->item_size ); - table->items_key[ table->count ] = key; - table->items_slot[ table->count ] = slot; - ++table->count; - return dest_item; - } - - -static void cs_hashtableremove( cs_hashtablet* table, CS_HASHTABLEU64 key ) - { - int const slot = cs_hashtableinternal_find_slot( table, key ); - CS_HASHTABLEASSERT( slot >= 0 ); - - int const slot_mask = table->slot_capacity - 1; - CS_HASHTABLEU32 const hash = table->slots[ slot ].key_hash; - int const base_slot = (int)( hash & (CS_HASHTABLEU32) slot_mask ); - CS_HASHTABLEASSERT( hash ); - --table->slots[ base_slot ].base_count; - table->slots[ slot ].key_hash = 0; - - int index = table->slots[ slot ].item_index; - int last_index = table->count - 1; - if( index != last_index ) - { - table->items_key[ index ] = table->items_key[ last_index ]; - table->items_slot[ index ] = table->items_slot[ last_index ]; - void* dst_item = (void*)( ( (uintptr_t) table->items_data ) + index * table->item_size ); - void* src_item = (void*)( ( (uintptr_t) table->items_data ) + last_index * table->item_size ); - CS_HASHTABLEMEMCPY( dst_item, src_item, (CS_HASHTABLESIZE_T) table->item_size ); - table->slots[ table->items_slot[ last_index ] ].item_index = index; - } - --table->count; - } - - -static void cs_hashtableclear( cs_hashtablet* table ) - { - table->count = 0; - CS_HASHTABLEMEMSET( table->slots, 0, table->slot_capacity * sizeof( *table->slots ) ); - } - - -static void* cs_hashtablefind( cs_hashtablet const* table, CS_HASHTABLEU64 key ) - { - int const slot = cs_hashtableinternal_find_slot( table, key ); - if( slot < 0 ) return 0; - - int const index = table->slots[ slot ].item_index; - void* const item = (void*)( ( (uintptr_t) table->items_data ) + index * table->item_size ); - return item; - } - - -static int cs_hashtablecount( cs_hashtablet const* table ) - { - return table->count; - } - - -static void* cs_hashtableitems( cs_hashtablet const* table ) - { - return table->items_data; - } - - -static CS_HASHTABLEU64 const* cs_hashtablekeys( cs_hashtablet const* table ) - { - return table->items_key; - } - - -static void cs_hashtableswap( cs_hashtablet* table, int index_a, int index_b ) - { - if( index_a < 0 || index_a >= table->count || index_b < 0 || index_b >= table->count ) return; - - int slot_a = table->items_slot[ index_a ]; - int slot_b = table->items_slot[ index_b ]; - - table->items_slot[ index_a ] = slot_b; - table->items_slot[ index_b ] = slot_a; - - CS_HASHTABLEU64 temp_key = table->items_key[ index_a ]; - table->items_key[ index_a ] = table->items_key[ index_b ]; - table->items_key[ index_b ] = temp_key; - - void* item_a = (void*)( ( (uintptr_t) table->items_data ) + index_a * table->item_size ); - void* item_b = (void*)( ( (uintptr_t) table->items_data ) + index_b * table->item_size ); - CS_HASHTABLEMEMCPY( table->swap_temp, item_a, table->item_size ); - CS_HASHTABLEMEMCPY( item_a, item_b, table->item_size ); - CS_HASHTABLEMEMCPY( item_b, table->swap_temp, table->item_size ); + cs__m128i cs_mm_set1_epi32(int32_t e) + { + cs__m128i a; + a.a = e; + a.b = e; + a.c = e; + a.d = e; + return a; + } - table->slots[ slot_a ].item_index = index_b; - table->slots[ slot_b ].item_index = index_a; - } + cs__m128i cs_mm_sub_epi32(cs__m128i a, cs__m128i b) + { + cs__m128i c; + c.a = a.a - b.a; + c.b = a.b - b.b; + c.c = a.c - b.c; + c.d = a.d - b.d; + return c; + } + cs__m128i cs_mm_and_si128(cs__m128i a, cs__m128i b) + { + cs__m128i c; + c.a = a.a & b.a; + c.b = a.b & b.b; + c.c = a.c & b.c; + c.d = a.d & b.d; + return c; + } -#endif /* CS_HASHTABLEIMPLEMENTATION */ -#endif // CS_HASHTABLEIMPLEMENTATION_ONCE + cs__m128 cs_mm_cmplt_ps(cs__m128 a, cs__m128 b) + { + cs__m128 c; + union { float f; uint32_t u; } mask; + mask.u = 0xFFFFFFFF; + c.a = a.a < b.a ? mask.f : 0.0f; + c.b = a.b < b.b ? mask.f : 0.0f; + c.c = a.c < b.c ? mask.f : 0.0f; + c.d = a.d < b.d ? mask.f : 0.0f; + return c; + } -/* -contributors: - Randy Gaul (cs_hashtableclear, cs_hashtableswap ) -revision history: - 1.1 added cs_hashtableclear, cs_hashtableswap - 1.0 first released version -*/ + cs__m128i cs_mm_castps_si128(cs__m128 a) + { + union { cs__m128 f; cs__m128i i; } u; + u.f = a; + return u.i; + } -/* ------------------------------------------------------------------------------- -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. ------------------------------------------------------------------------------- -*/ +#endif // End of SIMD wrappers. -// end of hashtable.h +#define CUTE_SOUND_ALIGN(X, Y) ((((size_t)X) + ((Y) - 1)) & ~((Y) - 1)) +#define CUTE_SOUND_TRUNC(X, Y) ((size_t)(X) & ~((Y) - 1)) // ------------------------------------------------------------------------------------------------- -// Doubly list. +// Simple hash map for uint64_t keys to void* values. -typedef struct cs_list_node_t +typedef struct cs_map_slot_t { - struct cs_list_node_t* next /* = this */; - struct cs_list_node_t* prev /* = this */; -} cs_list_node_t; + uint64_t key; + void* val; +} cs_map_slot_t; -typedef struct cs_list_t +typedef struct cs_map_t { - cs_list_node_t nodes; -} cs_list_t; + void* memctx; + cs_map_slot_t* slots; + int capacity; + int count; +} cs_map_t; -#define CUTE_SOUND_OFFSET_OF(T, member) ((size_t)((uintptr_t)(&(((T*)0)->member)))) -#define CUTE_SOUND_LIST_NODE(T, member, ptr) ((cs_list_node_t*)((uintptr_t)ptr + CUTE_SOUND_OFFSET_OF(T, member))) -#define CUTE_SOUND_LIST_HOST(T, member, ptr) ((T*)((uintptr_t)ptr - CUTE_SOUND_OFFSET_OF(T, member))) - -void cs_list_init_node(cs_list_node_t* node) +static uint32_t cs_map_hash(uint64_t key) { - node->next = node; - node->prev = node; + key = (~key) + (key << 18); + key = key ^ (key >> 31); + key = key * 21; + key = key ^ (key >> 11); + key = key + (key << 6); + key = key ^ (key >> 22); + return (uint32_t)key; } -void cs_list_init(cs_list_t* list) +static void cs_map_init(cs_map_t* map, int capacity, void* memctx) { - cs_list_init_node(&list->nodes); + map->memctx = memctx; + map->capacity = capacity; + map->count = 0; + size_t size = (size_t)capacity * sizeof(cs_map_slot_t); + map->slots = (cs_map_slot_t*)CUTE_SOUND_ALLOC(size, memctx); + CUTE_SOUND_MEMSET(map->slots, 0, size); } -void cs_list_push_front(cs_list_t* list, cs_list_node_t* node) +static void cs_map_term(cs_map_t* map) { - node->next = list->nodes.next; - node->prev = &list->nodes; - list->nodes.next->prev = node; - list->nodes.next = node; + CUTE_SOUND_FREE(map->slots, map->memctx); } -void cs_list_push_back(cs_list_t* list, cs_list_node_t* node) +static void cs_map_grow(cs_map_t* map) { - node->prev = list->nodes.prev; - node->next = &list->nodes; - list->nodes.prev->next = node; - list->nodes.prev = node; -} + int old_capacity = map->capacity; + cs_map_slot_t* old_slots = map->slots; -void cs_list_remove(cs_list_node_t* node) -{ - node->prev->next = node->next; - node->next->prev = node->prev; - cs_list_init_node(node); -} + map->capacity *= 2; + size_t size = (size_t)map->capacity * sizeof(cs_map_slot_t); + map->slots = (cs_map_slot_t*)CUTE_SOUND_ALLOC(size, map->memctx); + CUTE_SOUND_MEMSET(map->slots, 0, size); -cs_list_node_t* cs_list_pop_front(cs_list_t* list) -{ - cs_list_node_t* node = list->nodes.next; - cs_list_remove(node); - return node; -} + int mask = map->capacity - 1; + for (int i = 0; i < old_capacity; ++i) { + if (old_slots[i].key) { + uint32_t h = cs_map_hash(old_slots[i].key); + int idx = h & mask; + while (map->slots[idx].key) { + idx = (idx + 1) & mask; + } + map->slots[idx] = old_slots[i]; + } + } -cs_list_node_t* cs_list_pop_back(cs_list_t* list) -{ - cs_list_node_t* node = list->nodes.prev; - cs_list_remove(node); - return node; + CUTE_SOUND_FREE(old_slots, map->memctx); } -int cs_list_empty(cs_list_t* list) +static void cs_map_insert(cs_map_t* map, uint64_t key, void* val) { - return list->nodes.next == list->nodes.prev && list->nodes.next == &list->nodes; -} + CUTE_SOUND_ASSERT(key != 0); + if (map->count >= map->capacity / 2) { + cs_map_grow(map); + } -cs_list_node_t* cs_list_begin(cs_list_t* list) -{ - return list->nodes.next; + int mask = map->capacity - 1; + uint32_t h = cs_map_hash(key); + int idx = h & mask; + while (map->slots[idx].key && map->slots[idx].key != key) { + idx = (idx + 1) & mask; + } + + if (!map->slots[idx].key) { + ++map->count; + } + map->slots[idx].key = key; + map->slots[idx].val = val; } -cs_list_node_t* cs_list_end(cs_list_t* list) +static void* cs_map_find(cs_map_t* map, uint64_t key) { - return &list->nodes; + if (!key) return NULL; + int mask = map->capacity - 1; + uint32_t h = cs_map_hash(key); + int idx = h & mask; + while (map->slots[idx].key) { + if (map->slots[idx].key == key) { + return map->slots[idx].val; + } + idx = (idx + 1) & mask; + } + return NULL; } -cs_list_node_t* cs_list_front(cs_list_t* list) -{ - return list->nodes.next; +static void cs_map_remove(cs_map_t* map, uint64_t key) +{ + if (!key) return; + int mask = map->capacity - 1; + uint32_t h = cs_map_hash(key); + int idx = h & mask; + + while (map->slots[idx].key) { + if (map->slots[idx].key == key) { + map->slots[idx].key = 0; + map->slots[idx].val = NULL; + --map->count; + + // Reinsert subsequent entries to maintain probe chain. + int next = (idx + 1) & mask; + while (map->slots[next].key) { + cs_map_slot_t slot = map->slots[next]; + map->slots[next].key = 0; + map->slots[next].val = NULL; + --map->count; + cs_map_insert(map, slot.key, slot.val); + next = (next + 1) & mask; + } + return; + } + idx = (idx + 1) & mask; + } } -cs_list_node_t* cs_list_back(cs_list_t* list) +static void cs_map_clear(cs_map_t* map) { - return list->nodes.prev; + CUTE_SOUND_MEMSET(map->slots, 0, (size_t)map->capacity * sizeof(cs_map_slot_t)); + map->count = 0; } // ------------------------------------------------------------------------------------------------- @@ -1355,41 +833,28 @@ cs_list_node_t* cs_list_back(cs_list_t* list) const char* cs_error_as_string(cs_error_t error) { switch (error) { case CUTE_SOUND_ERROR_NONE: return "CUTE_SOUND_ERROR_NONE"; - case CUTE_SOUND_ERROR_IMPLEMENTATION_ERROR_PLEASE_REPORT_THIS_ON_GITHUB: return "CUTE_SOUND_ERROR_IMPLEMENTATION_ERROR_PLEASE_REPORT_THIS_ON_GITHUB"; case CUTE_SOUND_ERROR_FILE_NOT_FOUND: return "CUTE_SOUND_ERROR_FILE_NOT_FOUND"; case CUTE_SOUND_ERROR_INVALID_SOUND: return "CUTE_SOUND_ERROR_INVALID_SOUND"; - case CUTE_SOUND_ERROR_HWND_IS_NULL: return "CUTE_SOUND_ERROR_HWND_IS_NULL"; - case CUTE_SOUND_ERROR_DIRECTSOUND_CREATE_FAILED: return "CUTE_SOUND_ERROR_DIRECTSOUND_CREATE_FAILED"; - case CUTE_SOUND_ERROR_CREATESOUNDBUFFER_FAILED: return "CUTE_SOUND_ERROR_CREATESOUNDBUFFER_FAILED"; - case CUTE_SOUND_ERROR_SETFORMAT_FAILED: return "CUTE_SOUND_ERROR_SETFORMAT_FAILED"; - case CUTE_SOUND_ERROR_AUDIOCOMPONENTFINDNEXT_FAILED: return "CUTE_SOUND_ERROR_AUDIOCOMPONENTFINDNEXT_FAILED"; - case CUTE_SOUND_ERROR_AUDIOCOMPONENTINSTANCENEW_FAILED: return "CUTE_SOUND_ERROR_AUDIOCOMPONENTINSTANCENEW_FAILED"; - case CUTE_SOUND_ERROR_FAILED_TO_SET_STREAM_FORMAT: return "CUTE_SOUND_ERROR_FAILED_TO_SET_STREAM_FORMAT"; - case CUTE_SOUND_ERROR_FAILED_TO_SET_RENDER_CALLBACK: return "CUTE_SOUND_ERROR_FAILED_TO_SET_RENDER_CALLBACK"; - case CUTE_SOUND_ERROR_AUDIOUNITINITIALIZE_FAILED: return "CUTE_SOUND_ERROR_AUDIOUNITINITIALIZE_FAILED"; - case CUTE_SOUND_ERROR_AUDIOUNITSTART_FAILED: return "CUTE_SOUND_ERROR_AUDIOUNITSTART_FAILED"; case CUTE_SOUND_ERROR_CANT_OPEN_AUDIO_DEVICE: return "CUTE_SOUND_ERROR_CANT_OPEN_AUDIO_DEVICE"; case CUTE_SOUND_ERROR_CANT_INIT_SDL_AUDIO: return "CUTE_SOUND_ERROR_CANT_INIT_SDL_AUDIO"; case CUTE_SOUND_ERROR_THE_FILE_IS_NOT_A_WAV_FILE: return "CUTE_SOUND_ERROR_THE_FILE_IS_NOT_A_WAV_FILE"; case CUTE_SOUND_ERROR_WAV_FILE_FORMAT_CHUNK_NOT_FOUND: return "CUTE_SOUND_ERROR_WAV_FILE_FORMAT_CHUNK_NOT_FOUND"; case CUTE_SOUND_ERROR_WAV_DATA_CHUNK_NOT_FOUND: return "CUTE_SOUND_ERROR_WAV_DATA_CHUNK_NOT_FOUND"; - case CUTE_SOUND_ERROR_ONLY_PCM_WAV_FILES_ARE_SUPPORTED: return "CUTE_SOUND_ERROR_ONLY_PCM_WAV_FILES_ARE_SUPPORTED"; case CUTE_SOUND_ERROR_WAV_ONLY_MONO_OR_STEREO_IS_SUPPORTED: return "CUTE_SOUND_ERROR_WAV_ONLY_MONO_OR_STEREO_IS_SUPPORTED"; - case CUTE_SOUND_ERROR_WAV_ONLY_16_BITS_PER_SAMPLE_SUPPORTED: return "CUTE_SOUND_ERROR_WAV_ONLY_16_BITS_PER_SAMPLE_SUPPORTED"; + case CUTE_SOUND_ERROR_WAV_UNSUPPORTED_FORMAT: return "CUTE_SOUND_ERROR_WAV_UNSUPPORTED_FORMAT"; case CUTE_SOUND_ERROR_CANNOT_SWITCH_MUSIC_WHILE_PAUSED: return "CUTE_SOUND_ERROR_CANNOT_SWITCH_MUSIC_WHILE_PAUSED"; case CUTE_SOUND_ERROR_CANNOT_CROSSFADE_WHILE_MUSIC_IS_PAUSED: return "CUTE_SOUND_ERROR_CANNOT_CROSSFADE_WHILE_MUSIC_IS_PAUSED"; case CUTE_SOUND_ERROR_CANNOT_FADEOUT_WHILE_MUSIC_IS_PAUSED: return "CUTE_SOUND_ERROR_CANNOT_FADEOUT_WHILE_MUSIC_IS_PAUSED"; case CUTE_SOUND_ERROR_TRIED_TO_SET_SAMPLE_INDEX_BEYOND_THE_AUDIO_SOURCES_SAMPLE_COUNT: return "CUTE_SOUND_ERROR_TRIED_TO_SET_SAMPLE_INDEX_BEYOND_THE_AUDIO_SOURCES_SAMPLE_COUNT"; case CUTE_SOUND_ERROR_STB_VORBIS_DECODE_FAILED: return "CUTE_SOUND_ERROR_STB_VORBIS_DECODE_FAILED"; - case CUTE_SOUND_ERROR_OGG_UNSUPPORTED_CHANNEL_COUNT: return "CUTE_SOUND_ERROR_OGG_UNSUPPORTED_CHANNEL_COUN"; + case CUTE_SOUND_ERROR_OGG_UNSUPPORTED_CHANNEL_COUNT: return "CUTE_SOUND_ERROR_OGG_UNSUPPORTED_CHANNEL_COUNT"; + case CUTE_SOUND_ERROR_IMPLEMENTATION_ERROR_PLEASE_REPORT_THIS_ON_GITHUB: return "CUTE_SOUND_ERROR_IMPLEMENTATION_ERROR_PLEASE_REPORT_THIS_ON_GITHUB"; default: return "UNKNOWN"; } } // Cute sound context functions. -void cs_mix(); - typedef struct cs_audio_source_t { int sample_rate; @@ -1416,9 +881,10 @@ typedef struct cs_sound_inst_t float pan0; float pan1; float pitch; - int sample_index; + double sample_index; cs_audio_source_t* audio; - cs_list_node_t node; + struct cs_sound_inst_t* next; + struct cs_sound_inst_t* prev; } cs_sound_inst_t; typedef enum cs_music_state_t @@ -1449,10 +915,6 @@ typedef struct cs_context_t float music_volume /* = 1.0f */; float music_pitch /* = 1.0f */; float sound_volume /* = 1.0f */; - bool cull_duplicates /* = false */; - void** duplicates /* = NULL */; - int duplicate_count /* = 0 */; - int duplicate_capacity /* = 0 */; void (*on_finish)(cs_playing_sound_t, void*); /* = NULL */; void* on_finish_udata /* = NULL */; void (*on_music_finish)(void*); /* = NULL */; @@ -1463,6 +925,7 @@ typedef struct cs_context_t float t /* = 0 */; float fade /* = 0 */; float fade_switch_1 /* = 0 */; + float fade_start_volume /* = 0 */; cs_music_state_t music_state /* = MUSIC_STATE_NONE */; cs_music_state_t music_state_to_resume_from_paused /* = MUSIC_STATE_NONE */; cs_sound_inst_t* music_playing /* = NULL */; @@ -1472,78 +935,23 @@ typedef struct cs_context_t int audio_sources_to_free_size /* = 0 */; cs_audio_source_t** audio_sources_to_free /* = NULL */; uint64_t instance_id_gen /* = 1 */; - cs_hashtablet instance_map; // + cs_map_t instance_map; cs_inst_page_t* pages /* = NULL */; - cs_list_t playing_sounds; - cs_list_t free_sounds; + cs_sound_inst_t* playing_sounds /* = NULL */; + cs_sound_inst_t* free_sounds /* = NULL */; - unsigned latency_samples; - int Hz; - int bps; int wide_count; cs__m128* floatA; cs__m128* floatB; cs__m128i* samples; - bool separate_thread; bool running; - int sleep_milliseconds; - -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - - DWORD last_cursor; - unsigned running_index; - int buffer_size; - LPDIRECTSOUND dsound; - LPDIRECTSOUNDBUFFER primary; - LPDIRECTSOUNDBUFFER secondary; - - // data for cs_mix thread, enable these with cs_spawn_mix_thread - CRITICAL_SECTION critical_section; - -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE - - unsigned index0; // read - unsigned index1; // write - unsigned samples_in_circular_buffer; - int sample_count; - - // platform specific stuff - AudioComponentInstance inst; - - // data for cs_mix thread, enable these with cs_spawn_mix_thread - pthread_t thread; - pthread_mutex_t mutex; - -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL - - unsigned index0; // read - unsigned index1; // write - unsigned samples_in_circular_buffer; - int sample_count; - SDL_AudioDeviceID dev; - - // data for cs_mix thread, enable these with cs_spawn_mix_thread - SDL_Thread* thread; + SDL_AudioStream* stream; SDL_Mutex* mutex; - -#endif } cs_context_t; void* s_mem_ctx; cs_context_t* s_ctx = NULL; -void cs_sleep(int milliseconds) -{ -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - Sleep(milliseconds); -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE - struct timespec ts = { 0, milliseconds * 1000000 }; - nanosleep(&ts, NULL); -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL - SDL_Delay(milliseconds); -#endif -} - static void* cs_malloc16(size_t size) { void* p = CUTE_SOUND_ALLOC(size + 16, s_mem_ctx); @@ -1561,431 +969,106 @@ static void cs_free16(void* p) CUTE_SOUND_FREE((char*)p - (((size_t)*((char*)p - 1)) & 0xFF), s_mem_ctx); } -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL || CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE - - static int cs_samples_written() - { - return s_ctx->samples_in_circular_buffer; - } +static void cs_mix(int bytes_to_write); - static int cs_samples_unwritten() - { - return s_ctx->sample_count - s_ctx->samples_in_circular_buffer; +static void cs_sdl_audio_callback(void* udata, SDL_AudioStream* stream, int additional_amount, int total_amount) +{ + if (additional_amount > 0) { + cs_mix(additional_amount); + SDL_PutAudioStreamData(stream, s_ctx->samples, additional_amount); } +} - static int cs_samples_to_mix() - { - int lat = s_ctx->latency_samples; - int written = cs_samples_written(); - int dif = lat - written; - CUTE_SOUND_ASSERT(dif >= 0); - if (dif) - { - int unwritten = cs_samples_unwritten(); - return dif < unwritten ? dif : unwritten; - } - return 0; +static void s_add_page() +{ + cs_inst_page_t* page = (cs_inst_page_t*)CUTE_SOUND_ALLOC(sizeof(cs_inst_page_t), user_allocator_context); + for (int i = 0; i < CUTE_SOUND_PAGE_INSTANCE_COUNT; ++i) { + cs_sound_inst_t* inst = &page->instances[i]; + inst->next = s_ctx->free_sounds; + inst->prev = NULL; + if (s_ctx->free_sounds) s_ctx->free_sounds->prev = inst; + s_ctx->free_sounds = inst; } + page->next = s_ctx->pages; + s_ctx->pages = page; +} - #define CUTE_SOUND_SAMPLES_TO_BYTES(interleaved_sample_count) ((interleaved_sample_count) * s_ctx->bps) - #define CUTE_SOUND_BYTES_TO_SAMPLES(byte_count) ((byte_count) / s_ctx->bps) +cs_error_t cs_init(unsigned play_frequency_in_Hz, void* user_allocator_context /* = NULL */) +{ + int wide_count = (int)CUTE_SOUND_ALIGN(CUTE_SOUND_MIXER_BUFFER_SIZE, 4); - static void cs_push_bytes(void* data, int size) - { - int index1 = s_ctx->index1; - int samples_to_write = CUTE_SOUND_BYTES_TO_SAMPLES(size); - int sample_count = s_ctx->sample_count; - - int unwritten = cs_samples_unwritten(); - if (unwritten < samples_to_write) samples_to_write = unwritten; - int samples_to_end = sample_count - index1; - - if (samples_to_write > samples_to_end) { - CUTE_SOUND_MEMCPY((char*)s_ctx->samples + CUTE_SOUND_SAMPLES_TO_BYTES(index1), data, CUTE_SOUND_SAMPLES_TO_BYTES(samples_to_end)); - CUTE_SOUND_MEMCPY(s_ctx->samples, (char*)data + CUTE_SOUND_SAMPLES_TO_BYTES(samples_to_end), size - CUTE_SOUND_SAMPLES_TO_BYTES(samples_to_end)); - s_ctx->index1 = (samples_to_write - samples_to_end) % sample_count; - } else { - CUTE_SOUND_MEMCPY((char*)s_ctx->samples + CUTE_SOUND_SAMPLES_TO_BYTES(index1), data, size); - s_ctx->index1 = (s_ctx->index1 + samples_to_write) % sample_count; - } + SDL_AudioSpec wanted = { SDL_AUDIO_S16, 2, (int)play_frequency_in_Hz }; + if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) return CUTE_SOUND_ERROR_CANT_INIT_SDL_AUDIO; - s_ctx->samples_in_circular_buffer += samples_to_write; - } + s_mem_ctx = user_allocator_context; + s_ctx = (cs_context_t*)CUTE_SOUND_ALLOC(sizeof(cs_context_t), user_allocator_context); + CUTE_SOUND_MEMSET(s_ctx, 0, sizeof(cs_context_t)); + s_ctx->global_pan = 0.5f; + s_ctx->global_volume = 1.0f; + s_ctx->music_volume = 1.0f; + s_ctx->music_pitch = 1.0f; + s_ctx->sound_volume = 1.0f; + s_ctx->music_looped = true; + s_ctx->audio_sources_to_free_capacity = 32; + s_ctx->audio_sources_to_free = (cs_audio_source_t**)CUTE_SOUND_ALLOC(sizeof(cs_audio_source_t*) * s_ctx->audio_sources_to_free_capacity, s_mem_ctx); + s_ctx->instance_id_gen = 1; + cs_map_init(&s_ctx->instance_map, 1024, user_allocator_context); + s_ctx->playing_sounds = NULL; + s_ctx->free_sounds = NULL; + s_add_page(); + s_ctx->wide_count = wide_count; + s_ctx->floatA = (cs__m128*)cs_malloc16(sizeof(cs__m128) * wide_count); + s_ctx->floatB = (cs__m128*)cs_malloc16(sizeof(cs__m128) * wide_count); + s_ctx->samples = (cs__m128i*)cs_malloc16(sizeof(cs__m128i) * wide_count); + s_ctx->running = true; + s_ctx->mutex = SDL_CreateMutex(); - static int cs_pull_bytes(void* dst, int size) - { - int index0 = s_ctx->index0; - int allowed_size = CUTE_SOUND_SAMPLES_TO_BYTES(cs_samples_written()); - int sample_count = s_ctx->sample_count; - int zeros = 0; - - if (allowed_size < size) { - zeros = size - allowed_size; - size = allowed_size; - } + s_ctx->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &wanted, cs_sdl_audio_callback, NULL); + if (!s_ctx->stream) return CUTE_SOUND_ERROR_CANT_OPEN_AUDIO_DEVICE; + SDL_ResumeAudioStreamDevice(s_ctx->stream); - int samples_to_read = CUTE_SOUND_BYTES_TO_SAMPLES(size); - int samples_to_end = sample_count - index0; + return CUTE_SOUND_ERROR_NONE; +} - if (samples_to_read > samples_to_end) { - CUTE_SOUND_MEMCPY(dst, ((char*)s_ctx->samples) + CUTE_SOUND_SAMPLES_TO_BYTES(index0), CUTE_SOUND_SAMPLES_TO_BYTES(samples_to_end)); - CUTE_SOUND_MEMCPY(((char*)dst) + CUTE_SOUND_SAMPLES_TO_BYTES(samples_to_end), s_ctx->samples, size - CUTE_SOUND_SAMPLES_TO_BYTES(samples_to_end)); - s_ctx->index0 = (samples_to_read - samples_to_end) % sample_count; - } else { - CUTE_SOUND_MEMCPY(dst, ((char*)s_ctx->samples) + CUTE_SOUND_SAMPLES_TO_BYTES(index0), size); - s_ctx->index0 = (s_ctx->index0 + samples_to_read) % sample_count; - } +void cs_shutdown() +{ + if (!s_ctx) return; - s_ctx->samples_in_circular_buffer -= samples_to_read; + s_ctx->running = false; + SDL_DestroyAudioStream(s_ctx->stream); + SDL_DestroyMutex(s_ctx->mutex); - return zeros; + for (cs_sound_inst_t* inst = s_ctx->playing_sounds; inst; inst = inst->next) { + if (inst->audio) inst->audio->playing_count = 0; } -#endif - -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - - static DWORD WINAPI cs_ctx_thread(LPVOID lpParameter) - { - (void)lpParameter; - while (s_ctx->running) { - cs_mix(); - if (s_ctx->sleep_milliseconds) cs_sleep(s_ctx->sleep_milliseconds); - else YieldProcessor(); - } - - s_ctx->separate_thread = false; - return 0; + cs_inst_page_t* page = s_ctx->pages; + while (page) { + cs_inst_page_t* next = page->next; + CUTE_SOUND_FREE(page, s_mem_ctx); + page = next; } -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE - - static void* cs_ctx_thread(void* udata) - { - while (s_ctx->running) { - cs_mix(); - if (s_ctx->sleep_milliseconds) cs_sleep(s_ctx->sleep_milliseconds); - else pthread_yield_np(); - } - - s_ctx->separate_thread = 0; - pthread_exit(0); - return 0; - } - - static OSStatus cs_memcpy_to_coreaudio(void* udata, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) - { - int bps = s_ctx->bps; - int samples_requested_to_consume = inNumberFrames; - AudioBuffer* buffer = ioData->mBuffers; - - CUTE_SOUND_ASSERT(ioData->mNumberBuffers == 1); - CUTE_SOUND_ASSERT(buffer->mNumberChannels == 2); - int byte_size = buffer->mDataByteSize; - CUTE_SOUND_ASSERT(byte_size == samples_requested_to_consume * bps); - - int zero_bytes = cs_pull_bytes(buffer->mData, byte_size); - CUTE_SOUND_MEMSET(((char*)buffer->mData) + (byte_size - zero_bytes), 0, zero_bytes); - - return noErr; - } - -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL - - int cs_ctx_thread(void* udata) - { - while (s_ctx->running) { - cs_mix(); - if (s_ctx->sleep_milliseconds) cs_sleep(s_ctx->sleep_milliseconds); - else cs_sleep(1); - } - - s_ctx->separate_thread = false; - return 0; - } - - static void cs_sdl_audio_callback(void *userdata, SDL_AudioStream *stream, int additional_amount, int total_amount) - { - if (additional_amount > 0) { - Uint8 *data = SDL_stack_alloc(Uint8, additional_amount); - if (data) { - int zero_bytes = cs_pull_bytes(data, additional_amount); - CUTE_SOUND_MEMSET(data + (additional_amount - zero_bytes), 0, zero_bytes); - SDL_PutAudioStreamData(stream, data, additional_amount); - SDL_stack_free(data); - } - } - } - -#endif - -static void s_add_page() -{ - cs_inst_page_t* page = (cs_inst_page_t*)CUTE_SOUND_ALLOC(sizeof(cs_inst_page_t), user_allocator_context); - for (int i = 0; i < CUTE_SOUND_PAGE_INSTANCE_COUNT; ++i) { - cs_list_init_node(&page->instances[i].node); - cs_list_push_back(&s_ctx->free_sounds, &page->instances[i].node); - } - page->next = s_ctx->pages; - s_ctx->pages = page; -} - -cs_error_t cs_init(void* os_handle, unsigned play_frequency_in_Hz, int buffered_samples, void* user_allocator_context /* = NULL */) -{ - buffered_samples = buffered_samples < CUTE_SOUND_MINIMUM_BUFFERED_SAMPLES ? CUTE_SOUND_MINIMUM_BUFFERED_SAMPLES : buffered_samples; - int sample_count = buffered_samples; - int wide_count = (int)CUTE_SOUND_ALIGN(sample_count, 4); - int bps = sizeof(uint16_t) * 2; - -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - - int buffer_size = buffered_samples * bps; - LPDIRECTSOUND dsound = NULL; - LPDIRECTSOUNDBUFFER primary_buffer = NULL; - LPDIRECTSOUNDBUFFER secondary_buffer = NULL; - - if (!os_handle) return CUTE_SOUND_ERROR_HWND_IS_NULL; - { - WAVEFORMATEX format = { 0, 0, 0, 0, 0, 0, 0 }; - DSBUFFERDESC bufdesc = { 0, 0, 0, 0, 0, { 0, 0, 0, 0 } }; - HRESULT res = DirectSoundCreate(0, &dsound, 0); - if (res != DS_OK) return CUTE_SOUND_ERROR_DIRECTSOUND_CREATE_FAILED; - IDirectSound_SetCooperativeLevel(dsound, (HWND)os_handle, DSSCL_PRIORITY); - bufdesc.dwSize = sizeof(bufdesc); - bufdesc.dwFlags = DSBCAPS_PRIMARYBUFFER; - - res = IDirectSound_CreateSoundBuffer(dsound, &bufdesc, &primary_buffer, 0); - if (res != DS_OK) CUTE_SOUND_ERROR_CREATESOUNDBUFFER_FAILED; - - format.wFormatTag = WAVE_FORMAT_PCM; - format.nChannels = 2; - format.nSamplesPerSec = play_frequency_in_Hz; - format.wBitsPerSample = 16; - format.nBlockAlign = (format.nChannels * format.wBitsPerSample) / 8; - format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; - format.cbSize = 0; - res = IDirectSoundBuffer_SetFormat(primary_buffer, &format); - if (res != DS_OK) CUTE_SOUND_ERROR_SETFORMAT_FAILED; - - bufdesc.dwSize = sizeof(bufdesc); - bufdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2; - bufdesc.dwBufferBytes = buffer_size; - bufdesc.lpwfxFormat = &format; - res = IDirectSound_CreateSoundBuffer(dsound, &bufdesc, &secondary_buffer, 0); - if (res != DS_OK) CUTE_SOUND_ERROR_SETFORMAT_FAILED; - - // Silence the initial audio buffer. - void* region1; - DWORD size1; - void* region2; - DWORD size2; - res = IDirectSoundBuffer_Lock(secondary_buffer, 0, bufdesc.dwBufferBytes, ®ion1, &size1, ®ion2, &size2, DSBLOCK_ENTIREBUFFER); - if (res == DS_OK) { - CUTE_SOUND_MEMSET(region1, 0, size1); - IDirectSoundBuffer_Unlock(secondary_buffer, region1, size1, region2, size2); - } - } - -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE - - AudioComponentDescription comp_desc = { 0 }; - comp_desc.componentType = kAudioUnitType_Output; - comp_desc.componentSubType = kAudioUnitSubType_DefaultOutput; - comp_desc.componentFlags = 0; - comp_desc.componentFlagsMask = 0; - comp_desc.componentManufacturer = kAudioUnitManufacturer_Apple; - - AudioComponent comp = AudioComponentFindNext(NULL, &comp_desc); - if (!comp) return CUTE_SOUND_ERROR_AUDIOCOMPONENTFINDNEXT_FAILED; - - AudioStreamBasicDescription stream_desc = { 0 }; - stream_desc.mSampleRate = (double)play_frequency_in_Hz; - stream_desc.mFormatID = kAudioFormatLinearPCM; - stream_desc.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; - stream_desc.mFramesPerPacket = 1; - stream_desc.mChannelsPerFrame = 2; - stream_desc.mBitsPerChannel = sizeof(uint16_t) * 8; - stream_desc.mBytesPerPacket = bps; - stream_desc.mBytesPerFrame = bps; - stream_desc.mReserved = 0; - - AudioComponentInstance inst; - OSStatus ret; - AURenderCallbackStruct input; - - ret = AudioComponentInstanceNew(comp, &inst); - if (ret != noErr) return CUTE_SOUND_ERROR_AUDIOCOMPONENTINSTANCENEW_FAILED; - - ret = AudioUnitSetProperty(inst, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &stream_desc, sizeof(stream_desc)); - if (ret != noErr) return CUTE_SOUND_ERROR_FAILED_TO_SET_STREAM_FORMAT; - - ret = AudioUnitInitialize(inst); - if (ret != noErr) return CUTE_SOUND_ERROR_AUDIOUNITINITIALIZE_FAILED; - - ret = AudioOutputUnitStart(inst); - if (ret != noErr) return CUTE_SOUND_ERROR_AUDIOUNITSTART_FAILED; - -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL - - SDL_AudioSpec wanted = { SDL_AUDIO_S16, 2, (int)play_frequency_in_Hz }; - int ret = !SDL_InitSubSystem(SDL_INIT_AUDIO); - if (ret < 0) return CUTE_SOUND_ERROR_CANT_INIT_SDL_AUDIO; - -#endif - - s_ctx = (cs_context_t*)CUTE_SOUND_ALLOC(sizeof(cs_context_t), user_allocator_context); - s_ctx->global_pan = 0.5f; - s_ctx->global_volume = 1.0f; - s_ctx->global_pause = false; - s_ctx->music_volume = 1.0f; - s_ctx->music_pitch = 1.0f; - s_ctx->sound_volume = 1.0f; - s_ctx->music_looped = true; - s_ctx->music_paused = false; - s_ctx->on_finish = NULL; - s_ctx->on_finish_udata = NULL; - s_ctx->on_music_finish = NULL; - s_ctx->on_music_finish_udata = NULL; - s_ctx->t = 0; - s_ctx->fade = 0; - s_ctx->fade_switch_1 = 0; - s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_NONE; - s_ctx->music_state_to_resume_from_paused = CUTE_SOUND_MUSIC_STATE_NONE; - s_ctx->music_playing = NULL; - s_ctx->music_next = NULL; - s_ctx->audio_sources_to_free_capacity = 32; - s_ctx->audio_sources_to_free_size = 0; - s_ctx->audio_sources_to_free = (cs_audio_source_t**)CUTE_SOUND_ALLOC(sizeof(cs_audio_source_t*) * s_ctx->audio_sources_to_free_capacity, s_mem_ctx); - s_ctx->instance_id_gen = 1; - cs_hashtableinit(&s_ctx->instance_map, sizeof(cs_audio_source_t*), 1024, user_allocator_context); - s_ctx->pages = NULL; - cs_list_init(&s_ctx->playing_sounds); - cs_list_init(&s_ctx->free_sounds); - s_add_page(); - s_mem_ctx = user_allocator_context; - s_ctx->latency_samples = buffered_samples; - s_ctx->Hz = play_frequency_in_Hz; - s_ctx->bps = bps; - s_ctx->wide_count = wide_count; - s_ctx->floatA = (cs__m128*)cs_malloc16(sizeof(cs__m128) * wide_count); - s_ctx->floatB = (cs__m128*)cs_malloc16(sizeof(cs__m128) * wide_count); - s_ctx->samples = (cs__m128i*)cs_malloc16(sizeof(cs__m128i) * wide_count); - s_ctx->running = true; - s_ctx->separate_thread = false; - s_ctx->sleep_milliseconds = 0; - -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - - s_ctx->last_cursor = 0; - s_ctx->running_index = 0; - s_ctx->buffer_size = buffer_size; - s_ctx->dsound = dsound; - s_ctx->primary = primary_buffer; - s_ctx->secondary = secondary_buffer; - InitializeCriticalSectionAndSpinCount(&s_ctx->critical_section, 0x00000400); - -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE - - s_ctx->index0 = 0; - s_ctx->index1 = 0; - s_ctx->samples_in_circular_buffer = 0; - s_ctx->sample_count = wide_count * 4; - s_ctx->inst = inst; - pthread_mutex_init(&s_ctx->mutex, NULL); - - input.inputProc = cs_memcpy_to_coreaudio; - input.inputProcRefCon = s_ctx; - ret = AudioUnitSetProperty(inst, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &input, sizeof(input)); - if (ret != noErr) return CUTE_SOUND_ERROR_FAILED_TO_SET_RENDER_CALLBACK; // This leaks memory, oh well. - -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL - - s_ctx->index0 = 0; - s_ctx->index1 = 0; - s_ctx->samples_in_circular_buffer = 0; - s_ctx->sample_count = wide_count * 4; - SDL_AudioStream* stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &wanted, cs_sdl_audio_callback, NULL); - s_ctx->dev = SDL_GetAudioStreamDevice(stream); - if (s_ctx->dev < 0) return CUTE_SOUND_ERROR_CANT_OPEN_AUDIO_DEVICE; // This leaks memory, oh well. - SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(stream)); - s_ctx->mutex = SDL_CreateMutex(); - -#endif - s_ctx->duplicate_capacity = 0; - s_ctx->duplicate_count = 0; - s_ctx->duplicates = NULL; - s_ctx->cull_duplicates = false; - return CUTE_SOUND_ERROR_NONE; -} - -void cs_lock(); -void cs_unlock(); - -void cs_shutdown() -{ - if (!s_ctx) return; - if (s_ctx->separate_thread) { - cs_lock(); - s_ctx->running = false; - cs_unlock(); - while (s_ctx->separate_thread) cs_sleep(1); - } -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - - DeleteCriticalSection(&s_ctx->critical_section); - IDirectSoundBuffer_Release(s_ctx->secondary); - IDirectSoundBuffer_Release(s_ctx->primary); - IDirectSoundBuffer_Release(s_ctx->dsound); - -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL - - SDL_DestroyMutex(s_ctx->mutex); - SDL_CloseAudioDevice(s_ctx->dev); - SDL_WaitThread(s_ctx->thread, NULL); - -#endif - - if (!cs_list_empty(&s_ctx->playing_sounds)) { - cs_list_node_t* playing_node = cs_list_begin(&s_ctx->playing_sounds); - cs_list_node_t* end_node = cs_list_end(&s_ctx->playing_sounds); - do { - cs_list_node_t* next_node = playing_node->next; - cs_sound_inst_t* playing = CUTE_SOUND_LIST_HOST(cs_sound_inst_t, node, playing_node); - cs_audio_source_t* audio = playing->audio; - if (audio) audio->playing_count = 0; - playing_node = next_node; - } while (playing_node != end_node); - } - - cs_inst_page_t* page = s_ctx->pages; - while (page) { - cs_inst_page_t* next = page->next; - CUTE_SOUND_FREE(page, s_mem_ctx); - page = next; - } - - for (int i = 0; i < s_ctx->audio_sources_to_free_size; ++i) { - cs_audio_source_t* audio = s_ctx->audio_sources_to_free[i]; - cs_free16(audio->channels[0]); - CUTE_SOUND_FREE(audio, s_mem_ctx); + for (int i = 0; i < s_ctx->audio_sources_to_free_size; ++i) { + cs_audio_source_t* audio = s_ctx->audio_sources_to_free[i]; + cs_free16(audio->channels[0]); + CUTE_SOUND_FREE(audio, s_mem_ctx); } CUTE_SOUND_FREE(s_ctx->audio_sources_to_free, s_mem_ctx); cs_free16(s_ctx->floatA); cs_free16(s_ctx->floatB); cs_free16(s_ctx->samples); - cs_hashtableterm(&s_ctx->instance_map); + cs_map_term(&s_ctx->instance_map); CUTE_SOUND_FREE(s_ctx, s_mem_ctx); s_ctx = NULL; } static float s_smoothstep(float x) { return x * x * (3.0f - 2.0f * x); } +static void cs_free_queued_audio_sources(); void cs_update(float dt) { - if (!s_ctx->separate_thread) cs_mix(); - switch (s_ctx->music_state) { case CUTE_SOUND_MUSIC_STATE_FADE_OUT: { @@ -1995,7 +1078,8 @@ void cs_update(float dt) s_ctx->music_playing->active = false; s_ctx->music_playing = NULL; } else { - s_ctx->music_playing->volume = s_smoothstep(((s_ctx->fade - s_ctx->t) / s_ctx->fade));; + float progress = s_smoothstep(s_ctx->t / s_ctx->fade); + s_ctx->music_playing->volume = s_ctx->fade_start_volume * (1.0f - progress); } } break; @@ -2004,9 +1088,11 @@ void cs_update(float dt) s_ctx->t += dt; if (s_ctx->t >= s_ctx->fade) { s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_PLAYING; - s_ctx->t = s_ctx->fade; + s_ctx->music_playing->volume = s_ctx->music_volume; + } else { + float progress = s_smoothstep(s_ctx->t / s_ctx->fade); + s_ctx->music_playing->volume = s_ctx->music_volume * progress; } - s_ctx->music_playing->volume = s_smoothstep(1.0f - ((s_ctx->fade - s_ctx->t) / s_ctx->fade)); } break; case CUTE_SOUND_MUSIC_STATE_SWITCH_TO_0: @@ -2021,7 +1107,8 @@ void cs_update(float dt) s_ctx->fade_switch_1 = 0; s_ctx->music_next->paused = false; } else { - s_ctx->music_playing->volume = s_smoothstep(((s_ctx->fade - s_ctx->t) / s_ctx->fade));; + float progress = s_smoothstep(s_ctx->t / s_ctx->fade); + s_ctx->music_playing->volume = s_ctx->fade_start_volume * (1.0f - progress); } } break; @@ -2030,14 +1117,12 @@ void cs_update(float dt) s_ctx->t += dt; if (s_ctx->t >= s_ctx->fade) { s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_PLAYING; - s_ctx->t = s_ctx->fade; - s_ctx->music_next->volume = 1.0f; + s_ctx->music_next->volume = s_ctx->music_volume; s_ctx->music_playing = s_ctx->music_next; s_ctx->music_next = NULL; } else { - float t = s_smoothstep(1.0f - ((s_ctx->fade - s_ctx->t) / s_ctx->fade)); - float volume = t; - s_ctx->music_next->volume = volume; + float progress = s_smoothstep(s_ctx->t / s_ctx->fade); + s_ctx->music_next->volume = s_ctx->music_volume * progress; } } break; @@ -2047,22 +1132,23 @@ void cs_update(float dt) if (s_ctx->t >= s_ctx->fade) { s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_PLAYING; s_ctx->music_playing->active = false; - s_ctx->music_next->volume = 1.0f; + s_ctx->music_next->volume = s_ctx->music_volume; s_ctx->music_playing = s_ctx->music_next; s_ctx->music_next = NULL; } else { - float t0 = s_smoothstep(((s_ctx->fade - s_ctx->t) / s_ctx->fade)); - float t1 = s_smoothstep(1.0f - ((s_ctx->fade - s_ctx->t) / s_ctx->fade)); - float v0 = t0; - float v1 = t1; - s_ctx->music_playing->volume = v0; - s_ctx->music_next->volume = v1; + float progress = s_smoothstep(s_ctx->t / s_ctx->fade); + s_ctx->music_playing->volume = s_ctx->fade_start_volume * (1.0f - progress); + s_ctx->music_next->volume = s_ctx->music_volume * progress; } } break; default: break; } + + SDL_LockMutex(s_ctx->mutex); + cs_free_queued_audio_sources(); + SDL_UnlockMutex(s_ctx->mutex); } void cs_set_global_volume(float volume_0_to_1) @@ -2083,456 +1169,329 @@ void cs_set_global_pause(bool true_for_paused) s_ctx->global_pause = true_for_paused; } -void cs_lock() +// Calculate volume for left/right channels with panning and global settings. +static void cs_calc_volume(cs_sound_inst_t* playing, float* out_vA, float* out_vB) { -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - EnterCriticalSection(&s_ctx->critical_section); -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE - pthread_mutex_lock(&s_ctx->mutex); -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL - SDL_LockMutex(s_ctx->mutex); -#endif -} - -void cs_unlock() -{ -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - LeaveCriticalSection(&s_ctx->critical_section); -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE - pthread_mutex_unlock(&s_ctx->mutex); -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL - SDL_UnlockMutex(s_ctx->mutex); -#endif + float gpan0 = 1.0f - s_ctx->global_pan; + float gpan1 = s_ctx->global_pan; + float vA = playing->volume * playing->pan0 * gpan0 * s_ctx->global_volume; + float vB = playing->volume * playing->pan1 * gpan1 * s_ctx->global_volume; + float type_vol = playing->is_music ? s_ctx->music_volume : s_ctx->sound_volume; + *out_vA = vA * type_vol; + *out_vB = vB * type_vol; } -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - -static void cs_dsound_get_bytes_to_fill(int* byte_to_lock, int* bytes_to_write) +// Stop a sound instance and move it to the free list. +static void cs_stop_sound_internal(cs_sound_inst_t* inst) { - DWORD play_cursor; - DWORD write_cursor; - DWORD lock; - DWORD target_cursor; - DWORD write; - DWORD status; + inst->sample_index = 0; + inst->active = false; - HRESULT hr = IDirectSoundBuffer_GetCurrentPosition(s_ctx->secondary, &play_cursor, &write_cursor); - if (hr != DS_OK) { - if (hr == DSERR_BUFFERLOST) { - hr = IDirectSoundBuffer_Restore(s_ctx->secondary); - } - *byte_to_lock = write_cursor; - *bytes_to_write = s_ctx->latency_samples * s_ctx->bps; - if (!SUCCEEDED(hr)) { - return; - } + if (inst->audio) { + inst->audio->playing_count -= s_ctx->running ? 1 : inst->audio->playing_count; + CUTE_SOUND_ASSERT(inst->audio->playing_count >= 0); } - s_ctx->last_cursor = write_cursor; + // Remove from playing list. + if (inst->prev) inst->prev->next = inst->next; + else s_ctx->playing_sounds = inst->next; + if (inst->next) inst->next->prev = inst->prev; - IDirectSoundBuffer_GetStatus(s_ctx->secondary, &status); - if (!(status & DSBSTATUS_PLAYING)) { - hr = IDirectSoundBuffer_Play(s_ctx->secondary, 0, 0, DSBPLAY_LOOPING); - if (!SUCCEEDED(hr)) { - return; - } - } + // Add to free list. + inst->next = s_ctx->free_sounds; + inst->prev = NULL; + if (s_ctx->free_sounds) s_ctx->free_sounds->prev = inst; + s_ctx->free_sounds = inst; - lock = (s_ctx->running_index * s_ctx->bps) % s_ctx->buffer_size; - target_cursor = (write_cursor + s_ctx->latency_samples * s_ctx->bps); - if (target_cursor > (DWORD)s_ctx->buffer_size) target_cursor %= s_ctx->buffer_size; - target_cursor = (DWORD)CUTE_SOUND_TRUNC(target_cursor, 16); + cs_map_remove(&s_ctx->instance_map, inst->id); - if (lock > target_cursor) { - write = (s_ctx->buffer_size - lock) + target_cursor; - } else { - write = target_cursor - lock; + if (s_ctx->on_finish && !inst->is_music) { + cs_playing_sound_t snd = { inst->id }; + s_ctx->on_finish(snd, s_ctx->on_finish_udata); + } else if (s_ctx->on_music_finish && inst->is_music) { + s_ctx->on_music_finish(s_ctx->on_music_finish_udata); } - - *byte_to_lock = lock; - *bytes_to_write = write; } -static void cs_dsound_memcpy_to_driver(int16_t* samples, int byte_to_lock, int bytes_to_write) -{ - // copy mixer buffers to direct sound - void* region1; - DWORD size1; - void* region2; - DWORD size2; - HRESULT hr = IDirectSoundBuffer_Lock(s_ctx->secondary, byte_to_lock, bytes_to_write, ®ion1, &size1, ®ion2, &size2, 0); - if (hr == DSERR_BUFFERLOST) { - IDirectSoundBuffer_Restore(s_ctx->secondary); - hr = IDirectSoundBuffer_Lock(s_ctx->secondary, byte_to_lock, bytes_to_write, ®ion1, &size1, ®ion2, &size2, 0); - } - if (!SUCCEEDED(hr)) { - return; +// Sample with bounds check - returns 0 for out of bounds (used for non-looping sounds). +#define CS_CLAMP_SAMPLE(buf, idx, count) ((unsigned)(idx) >= (unsigned)(count) ? 0.0f : ((float*)(buf))[idx]) + +// Sample with wraparound - wraps index modularly (used for looping sounds). +// Handles negative indices correctly: (-1) wraps to (count-1). +static inline int cs_wrap_index(int idx, int count) +{ + int r = idx % count; + return r < 0 ? r + count : r; +} +#define CS_WRAP_SAMPLE(buf, idx, count) (((float*)(buf))[cs_wrap_index(idx, count)]) + +// Floor function for SIMD - converts truncate-toward-zero to floor. +static inline cs__m128i cs_mm_floor_epi32(cs__m128 v) +{ + cs__m128i trunc = cs_mm_cvttps_epi32(v); + cs__m128 trunc_f = cs_mm_cvtepi32_ps(trunc); + // If v < trunc (i.e., v was negative with fractional part), subtract 1. + cs__m128 mask = cs_mm_cmplt_ps(v, trunc_f); + cs__m128i adjust = cs_mm_and_si128(cs_mm_castps_si128(mask), cs_mm_set1_epi32(1)); + return cs_mm_sub_epi32(trunc, adjust); +} + +// Mix with pitch shifting (resampling with linear interpolation). +static void cs_mix_pitched(cs_audio_source_t* audio, cs__m128 vA, cs__m128 vB, int write_offset_wide, int write_wide, float pitch, double sample_index, bool looped) +{ + cs__m128* floatA = s_ctx->floatA; + cs__m128* floatB = s_ctx->floatB; + cs__m128* cA = (cs__m128*)audio->channels[0]; + cs__m128* cB = (cs__m128*)audio->channels[1]; + cs__m128 pitch_v = cs_mm_set1_ps(pitch); + cs__m128 index_offset = cs_mm_set1_ps((float)sample_index); + int sample_count = audio->sample_count; + + for (int i = 0, j = 0; i < write_wide; ++i, j += 4) { + cs__m128 index = cs_mm_set_ps((float)j + 3, (float)j + 2, (float)j + 1, (float)j); + index = cs_mm_add_ps(cs_mm_mul_ps(index, pitch_v), index_offset); + cs__m128i index_int = cs_mm_floor_epi32(index); + cs__m128 index_frac = cs_mm_sub_ps(index, cs_mm_cvtepi32_ps(index_int)); + int i0 = cs_mm_extract_epi32(index_int, 3); + int i1 = cs_mm_extract_epi32(index_int, 2); + int i2 = cs_mm_extract_epi32(index_int, 1); + int i3 = cs_mm_extract_epi32(index_int, 0); + + cs__m128 loA, hiA; + if (looped) { + loA = cs_mm_set_ps( + CS_WRAP_SAMPLE(cA, i0, sample_count), + CS_WRAP_SAMPLE(cA, i1, sample_count), + CS_WRAP_SAMPLE(cA, i2, sample_count), + CS_WRAP_SAMPLE(cA, i3, sample_count) + ); + hiA = cs_mm_set_ps( + CS_WRAP_SAMPLE(cA, i0 + 1, sample_count), + CS_WRAP_SAMPLE(cA, i1 + 1, sample_count), + CS_WRAP_SAMPLE(cA, i2 + 1, sample_count), + CS_WRAP_SAMPLE(cA, i3 + 1, sample_count) + ); + } else { + loA = cs_mm_set_ps( + CS_CLAMP_SAMPLE(cA, i0, sample_count), + CS_CLAMP_SAMPLE(cA, i1, sample_count), + CS_CLAMP_SAMPLE(cA, i2, sample_count), + CS_CLAMP_SAMPLE(cA, i3, sample_count) + ); + hiA = cs_mm_set_ps( + CS_CLAMP_SAMPLE(cA, i0 + 1, sample_count), + CS_CLAMP_SAMPLE(cA, i1 + 1, sample_count), + CS_CLAMP_SAMPLE(cA, i2 + 1, sample_count), + CS_CLAMP_SAMPLE(cA, i3 + 1, sample_count) + ); + } + cs__m128 A = cs_mm_add_ps(loA, cs_mm_mul_ps(index_frac, cs_mm_sub_ps(hiA, loA))); + + if (audio->channel_count == 2) { + cs__m128 loB, hiB; + if (looped) { + loB = cs_mm_set_ps( + CS_WRAP_SAMPLE(cB, i0, sample_count), + CS_WRAP_SAMPLE(cB, i1, sample_count), + CS_WRAP_SAMPLE(cB, i2, sample_count), + CS_WRAP_SAMPLE(cB, i3, sample_count) + ); + hiB = cs_mm_set_ps( + CS_WRAP_SAMPLE(cB, i0 + 1, sample_count), + CS_WRAP_SAMPLE(cB, i1 + 1, sample_count), + CS_WRAP_SAMPLE(cB, i2 + 1, sample_count), + CS_WRAP_SAMPLE(cB, i3 + 1, sample_count) + ); + } else { + loB = cs_mm_set_ps( + CS_CLAMP_SAMPLE(cB, i0, sample_count), + CS_CLAMP_SAMPLE(cB, i1, sample_count), + CS_CLAMP_SAMPLE(cB, i2, sample_count), + CS_CLAMP_SAMPLE(cB, i3, sample_count) + ); + hiB = cs_mm_set_ps( + CS_CLAMP_SAMPLE(cB, i0 + 1, sample_count), + CS_CLAMP_SAMPLE(cB, i1 + 1, sample_count), + CS_CLAMP_SAMPLE(cB, i2 + 1, sample_count), + CS_CLAMP_SAMPLE(cB, i3 + 1, sample_count) + ); + } + cs__m128 B = cs_mm_add_ps(loB, cs_mm_mul_ps(index_frac, cs_mm_sub_ps(hiB, loB))); + A = cs_mm_mul_ps(A, vA); + B = cs_mm_mul_ps(B, vB); + floatA[i + write_offset_wide] = cs_mm_add_ps(floatA[i + write_offset_wide], A); + floatB[i + write_offset_wide] = cs_mm_add_ps(floatB[i + write_offset_wide], B); + } else { + cs__m128 B = cs_mm_mul_ps(A, vB); + A = cs_mm_mul_ps(A, vA); + floatA[i + write_offset_wide] = cs_mm_add_ps(floatA[i + write_offset_wide], A); + floatB[i + write_offset_wide] = cs_mm_add_ps(floatB[i + write_offset_wide], B); + } } - - unsigned running_index = s_ctx->running_index; - INT16* sample1 = (INT16*)region1; - DWORD sample1_count = size1 / s_ctx->bps; - CUTE_SOUND_MEMCPY(sample1, samples, sample1_count * sizeof(INT16) * 2); - samples += sample1_count * 2; - running_index += sample1_count; - - INT16* sample2 = (INT16*)region2; - DWORD sample2_count = size2 / s_ctx->bps; - CUTE_SOUND_MEMCPY(sample2, samples, sample2_count * sizeof(INT16) * 2); - samples += sample2_count * 2; - running_index += sample2_count; - - IDirectSoundBuffer_Unlock(s_ctx->secondary, region1, size1, region2, size2); - s_ctx->running_index = running_index; } -void cs_dsound_dont_run_too_fast() +#undef CS_CLAMP_SAMPLE +#undef CS_WRAP_SAMPLE + +// Mix without pitch shifting (direct copy with volume). +static void cs_mix_simple(cs_audio_source_t* audio, cs__m128 vA, cs__m128 vB, int write_offset_wide, int write_wide, int sample_index_wide) { - DWORD status; - DWORD cursor; - DWORD junk; - HRESULT hr; + cs__m128* floatA = s_ctx->floatA; + cs__m128* floatB = s_ctx->floatB; + cs__m128* cA = (cs__m128*)audio->channels[0]; + cs__m128* cB = (cs__m128*)audio->channels[1]; - hr = IDirectSoundBuffer_GetCurrentPosition(s_ctx->secondary, &junk, &cursor); - if (hr != DS_OK) { - if (hr == DSERR_BUFFERLOST) { - IDirectSoundBuffer_Restore(s_ctx->secondary); + if (audio->channel_count == 2) { + for (int i = 0; i < write_wide; ++i) { + cs__m128 A = cs_mm_mul_ps(cA[i + sample_index_wide], vA); + cs__m128 B = cs_mm_mul_ps(cB[i + sample_index_wide], vB); + floatA[i + write_offset_wide] = cs_mm_add_ps(floatA[i + write_offset_wide], A); + floatB[i + write_offset_wide] = cs_mm_add_ps(floatB[i + write_offset_wide], B); } - return; - } - - // Prevent mixing thread from sending samples too quickly. - while (cursor == s_ctx->last_cursor) { - cs_sleep(1); - - IDirectSoundBuffer_GetStatus(s_ctx->secondary, &status); - if ((status & DSBSTATUS_BUFFERLOST)) { - IDirectSoundBuffer_Restore(s_ctx->secondary); - IDirectSoundBuffer_GetStatus(s_ctx->secondary, &status); - if ((status & DSBSTATUS_BUFFERLOST)) { - break; - } + } else { + for (int i = 0; i < write_wide; ++i) { + cs__m128 A = cA[i + sample_index_wide]; + cs__m128 B = cs_mm_mul_ps(A, vB); + A = cs_mm_mul_ps(A, vA); + floatA[i + write_offset_wide] = cs_mm_add_ps(floatA[i + write_offset_wide], A); + floatB[i + write_offset_wide] = cs_mm_add_ps(floatB[i + write_offset_wide], B); } + } +} - hr = IDirectSoundBuffer_GetCurrentPosition(s_ctx->secondary, &junk, &cursor); - if (hr != DS_OK) { - // Eek! Not much to do here I guess. - return; +// Free queued audio sources with zero refcount. +static void cs_free_queued_audio_sources() +{ + for (int i = 0; i < s_ctx->audio_sources_to_free_size;) { + cs_audio_source_t* audio = s_ctx->audio_sources_to_free[i]; + if (audio->playing_count == 0) { + cs_free16(audio->channels[0]); + CUTE_SOUND_FREE(audio, s_mem_ctx); + s_ctx->audio_sources_to_free[i] = s_ctx->audio_sources_to_free[--s_ctx->audio_sources_to_free_size]; + } else { + ++i; } } } -#endif // CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - -void cs_mix() +static void cs_mix(int bytes_to_write) { - cs__m128i* samples; - cs__m128* floatA; - cs__m128* floatB; - int samples_needed; - int write_offset = 0; - - cs_lock(); - -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - - int byte_to_lock; - int bytes_to_write; - cs_dsound_get_bytes_to_fill(&byte_to_lock, &bytes_to_write); - - if (bytes_to_write < (int)s_ctx->latency_samples) goto unlock; - samples_needed = bytes_to_write / s_ctx->bps; - -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE || CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL - - int bytes_to_write; - samples_needed = cs_samples_to_mix(); - if (!samples_needed) goto unlock; - bytes_to_write = samples_needed * s_ctx->bps; + SDL_LockMutex(s_ctx->mutex); -#endif + int samples_needed = bytes_to_write / CUTE_SOUND_BYTES_PER_SAMPLE_FRAME; + if (!samples_needed) { + SDL_UnlockMutex(s_ctx->mutex); + return; + } // Clear mixer buffers. - floatA = s_ctx->floatA; - floatB = s_ctx->floatB; + cs__m128* floatA = s_ctx->floatA; + cs__m128* floatB = s_ctx->floatB; CUTE_SOUND_MEMSET(floatA, 0, sizeof(cs__m128) * s_ctx->wide_count); CUTE_SOUND_MEMSET(floatB, 0, sizeof(cs__m128) * s_ctx->wide_count); // Mix all playing sounds into the mixer buffers. - if (!s_ctx->global_pause && !cs_list_empty(&s_ctx->playing_sounds)) { - cs_list_node_t* playing_node = cs_list_begin(&s_ctx->playing_sounds); - cs_list_node_t* end_node = cs_list_end(&s_ctx->playing_sounds); - do { - cs_list_node_t* next_node = playing_node->next; - cs_sound_inst_t* playing = CUTE_SOUND_LIST_HOST(cs_sound_inst_t, node, playing_node); + if (!s_ctx->global_pause) { + for (cs_sound_inst_t* playing = s_ctx->playing_sounds; playing; ) { + cs_sound_inst_t* next = playing->next; cs_audio_source_t* audio = playing->audio; - if (!audio) goto remove; - if (!playing->active || !s_ctx->running) goto remove; - if (playing->paused) goto get_next_playing_sound; - if (s_ctx->cull_duplicates) { - for (int i = 0; i < s_ctx->duplicate_count; ++i) { - if (s_ctx->duplicates[i] == (void*)audio) { - goto remove; - } - } - if (s_ctx->duplicate_count == s_ctx->duplicate_capacity) { - int new_capacity = s_ctx->duplicate_capacity ? s_ctx->duplicate_capacity * 2 : 1024; - void* duplicates = CUTE_SOUND_ALLOC(sizeof(void*) * new_capacity, s_ctx->mem_ctx); - CUTE_SOUND_MEMCPY(duplicates, s_ctx->duplicates, sizeof(void*) * s_ctx->duplicate_count); - CUTE_SOUND_FREE(s_ctx->duplicates, s_ctx->mem_ctx); - s_ctx->duplicates = (void**)duplicates; - s_ctx->duplicate_capacity = new_capacity; - } - s_ctx->duplicates[s_ctx->duplicate_count++] = (void*)audio; + // Check if sound should be removed. + if (!audio || !playing->active || !s_ctx->running) { + cs_stop_sound_internal(playing); + playing = next; + continue; } - // Jump here for looping sounds if we need to wrap-around the audio source - // and continue mixing more samples. - mix_more: - - { - cs__m128* cA = (cs__m128*)audio->channels[0]; - cs__m128* cB = (cs__m128*)audio->channels[1]; - - // Attempted to play a sound with no audio. - // Make sure the audio file was loaded properly. - CUTE_SOUND_ASSERT(cA); - - float gpan0 = 1.0f - s_ctx->global_pan; - float gpan1 = s_ctx->global_pan; - float vA0 = playing->volume * playing->pan0 * gpan0 * s_ctx->global_volume; - float vB0 = playing->volume * playing->pan1 * gpan1 * s_ctx->global_volume; - if (!playing->is_music) { - vA0 *= s_ctx->sound_volume; - vB0 *= s_ctx->sound_volume; - } else { - vA0 *= s_ctx->music_volume; - vB0 *= s_ctx->music_volume; - } - cs__m128 vA = cs_mm_set1_ps(vA0); - cs__m128 vB = cs_mm_set1_ps(vB0); - - int prev_playing_sample_index = playing->sample_index; - int sample_index_wide = (int)CUTE_SOUND_TRUNC(playing->sample_index, 4) / 4; - int samples_to_read = (int)(samples_needed * playing->pitch); - if (samples_to_read + playing->sample_index > audio->sample_count) { - samples_to_read = audio->sample_count - playing->sample_index; - } else if (samples_to_read + playing->sample_index < 0) { - // When pitch shifting is negative, samples_to_read is also negative so that offset needs to - // be accounted for otherwise the sample index cursor gets stuck at sample count. - playing->sample_index = audio->sample_count + samples_to_read + playing->sample_index; - } - int samples_to_write = (int)(samples_to_read / playing->pitch); - int write_wide = CUTE_SOUND_ALIGN(samples_to_write, 4) / 4; - int write_offset_wide = (int)CUTE_SOUND_ALIGN(write_offset, 4) / 4; - static int written_so_far = 0; - written_so_far += samples_to_read; - - // Do the actual mixing: Apply volume, load samples into float buffers. - if (playing->pitch != 1.0f) { - // Pitch shifting -- We read in samples at a resampled rate (multiply by pitch). These samples - // are read in one at a time in scalar mode, but then mixed together via SIMD. - cs__m128 pitch = cs_mm_set1_ps(playing->pitch); - cs__m128 index_offset = cs_mm_set1_ps((float)playing->sample_index); - switch (audio->channel_count) { - case 1: - { - for (int i = 0, j = 0; i < write_wide; ++i, j += 4) { - cs__m128 index = cs_mm_set_ps((float)j + 3, (float)j + 2, (float)j + 1, (float)j); - index = cs_mm_add_ps(cs_mm_mul_ps(index, pitch), index_offset); - cs__m128i index_int = cs_mm_cvttps_epi32(index); - cs__m128 index_frac = cs_mm_sub_ps(index, cs_mm_cvtepi32_ps(index_int)); - int i0 = cs_mm_extract_epi32(index_int, 3); - int i1 = cs_mm_extract_epi32(index_int, 2); - int i2 = cs_mm_extract_epi32(index_int, 1); - int i3 = cs_mm_extract_epi32(index_int, 0); - - cs__m128 loA = cs_mm_set_ps( - i0 > audio->sample_count ? 0 : i0 < 0 ? audio->sample_count : ((float*)cA)[i0], - i1 > audio->sample_count ? 0 : i1 < 0 ? audio->sample_count : ((float*)cA)[i1], - i2 > audio->sample_count ? 0 : i2 < 0 ? audio->sample_count : ((float*)cA)[i2], - i3 > audio->sample_count ? 0 : i3 < 0 ? audio->sample_count : ((float*)cA)[i3] - ); - cs__m128 hiA = cs_mm_set_ps( - i0 + 1 > audio->sample_count ? 0 : i0 + 1 < 0 ? audio->sample_count : ((float*)cA)[i0 + 1], - i1 + 1 > audio->sample_count ? 0 : i1 + 1 < 0 ? audio->sample_count : ((float*)cA)[i1 + 1], - i2 + 1 > audio->sample_count ? 0 : i2 + 1 < 0 ? audio->sample_count : ((float*)cA)[i2 + 1], - i3 + 1 > audio->sample_count ? 0 : i3 + 1 < 0 ? audio->sample_count : ((float*)cA)[i3 + 1] - ); - - cs__m128 A = cs_mm_add_ps(loA, cs_mm_mul_ps(index_frac, cs_mm_sub_ps(hiA, loA))); - cs__m128 B = cs_mm_mul_ps(A, vB); - A = cs_mm_mul_ps(A, vA); - floatA[i + write_offset_wide] = cs_mm_add_ps(floatA[i + write_offset_wide], A); - floatB[i + write_offset_wide] = cs_mm_add_ps(floatB[i + write_offset_wide], B); - } - break; - } - case 2: - { - for (int i = 0, j = 0; i < write_wide; ++i, j += 4) { - cs__m128 index = cs_mm_set_ps((float)j + 3, (float)j + 2, (float)j + 1, (float)j); - index = cs_mm_add_ps(cs_mm_mul_ps(index, pitch), index_offset); - cs__m128i index_int = cs_mm_cvttps_epi32(index); - cs__m128 index_frac = cs_mm_sub_ps(index, cs_mm_cvtepi32_ps(index_int)); - int i0 = cs_mm_extract_epi32(index_int, 3); - int i1 = cs_mm_extract_epi32(index_int, 2); - int i2 = cs_mm_extract_epi32(index_int, 1); - int i3 = cs_mm_extract_epi32(index_int, 0); - - cs__m128 loA = cs_mm_set_ps( - i0 > audio->sample_count ? 0 : i0 < 0 ? audio->sample_count : ((float*)cA)[i0], - i1 > audio->sample_count ? 0 : i1 < 0 ? audio->sample_count : ((float*)cA)[i1], - i2 > audio->sample_count ? 0 : i2 < 0 ? audio->sample_count : ((float*)cA)[i2], - i3 > audio->sample_count ? 0 : i3 < 0 ? audio->sample_count : ((float*)cA)[i3] - ); - cs__m128 hiA = cs_mm_set_ps( - i0 + 1 > audio->sample_count ? 0 : i0 + 1 < 0 ? audio->sample_count : ((float*)cA)[i0 + 1], - i1 + 1 > audio->sample_count ? 0 : i1 + 1 < 0 ? audio->sample_count : ((float*)cA)[i1 + 1], - i2 + 1 > audio->sample_count ? 0 : i2 + 1 < 0 ? audio->sample_count : ((float*)cA)[i2 + 1], - i3 + 1 > audio->sample_count ? 0 : i3 + 1 < 0 ? audio->sample_count : ((float*)cA)[i3 + 1] - ); - - cs__m128 loB = cs_mm_set_ps( - i0 > audio->sample_count ? 0 : i0 < 0 ? audio->sample_count : ((float*)cB)[i0], - i1 > audio->sample_count ? 0 : i1 < 0 ? audio->sample_count : ((float*)cB)[i1], - i2 > audio->sample_count ? 0 : i2 < 0 ? audio->sample_count : ((float*)cB)[i2], - i3 > audio->sample_count ? 0 : i3 < 0 ? audio->sample_count : ((float*)cB)[i3] - ); - cs__m128 hiB = cs_mm_set_ps( - i0 + 1 > audio->sample_count ? 0 : i0 + 1 < 0 ? audio->sample_count : ((float*)cB)[i0 + 1], - i1 + 1 > audio->sample_count ? 0 : i1 + 1 < 0 ? audio->sample_count : ((float*)cB)[i1 + 1], - i2 + 1 > audio->sample_count ? 0 : i2 + 1 < 0 ? audio->sample_count : ((float*)cB)[i2 + 1], - i3 + 1 > audio->sample_count ? 0 : i3 + 1 < 0 ? audio->sample_count : ((float*)cB)[i3 + 1] - ); - - cs__m128 A = cs_mm_add_ps(loA, cs_mm_mul_ps(index_frac, cs_mm_sub_ps(hiA, loA))); - cs__m128 B = cs_mm_add_ps(loB, cs_mm_mul_ps(index_frac, cs_mm_sub_ps(hiB, loB))); - - A = cs_mm_mul_ps(A, vA); - B = cs_mm_mul_ps(B, vB); - floatA[i + write_offset_wide] = cs_mm_add_ps(floatA[i + write_offset_wide], A); - floatB[i + write_offset_wide] = cs_mm_add_ps(floatB[i + write_offset_wide], B); - } - } break; - } - } else { - // No pitch shifting, just add samples together. - switch (audio->channel_count) { - case 1: - { - for (int i = 0; i < write_wide; ++i) { - cs__m128 A = cA[i + sample_index_wide]; - cs__m128 B = cs_mm_mul_ps(A, vB); - A = cs_mm_mul_ps(A, vA); - floatA[i + write_offset_wide] = cs_mm_add_ps(floatA[i + write_offset_wide], A); - floatB[i + write_offset_wide] = cs_mm_add_ps(floatB[i + write_offset_wide], B); + // Check if sound should be skipped this frame. + if (playing->paused || playing->pitch == 0.0f) { + playing = next; + continue; + } + + CUTE_SOUND_ASSERT(audio->channels[0]); + + // Calculate volume. + float vA0, vB0; + cs_calc_volume(playing, &vA0, &vB0); + cs__m128 vA = cs_mm_set1_ps(vA0); + cs__m128 vB = cs_mm_set1_ps(vB0); + + // Mix samples, handling looping. + int samples_remaining = samples_needed; + int write_offset = 0; + bool pitched = playing->pitch != 1.0f; + + while (samples_remaining > 0) { + // Check for end of sound before mixing. + bool at_end = (playing->pitch >= 0) + ? (playing->sample_index >= (double)audio->sample_count) + : (playing->sample_index <= 0.0); + + if (at_end) { + if (playing->looped) { + // Wrap sample_index back into valid range. + if (playing->pitch >= 0) { + while (playing->sample_index >= (double)audio->sample_count) { + playing->sample_index -= (double)audio->sample_count; + } + } else { + while (playing->sample_index < 0.0) { + playing->sample_index += (double)audio->sample_count; + } } + } else { + cs_stop_sound_internal(playing); break; } - case 2: - { - for (int i = 0; i < write_wide; ++i) { - cs__m128 A = cA[i + sample_index_wide]; - cs__m128 B = cB[i + sample_index_wide]; - - A = cs_mm_mul_ps(A, vA); - B = cs_mm_mul_ps(B, vB); - floatA[i + write_offset_wide] = cs_mm_add_ps(floatA[i + write_offset_wide], A); - floatB[i + write_offset_wide] = cs_mm_add_ps(floatB[i + write_offset_wide], B); - } - } break; - } } - // playing list logic - playing->sample_index += samples_to_read; - CUTE_SOUND_ASSERT(playing->sample_index <= audio->sample_count); - if (playing->pitch < 0) { - // When pitch shifting is negative adjust the timing a bit further back from sample count to avoid any clipping. - if (prev_playing_sample_index - playing->sample_index < 0) { - if (playing->looped) { - playing->sample_index = audio->sample_count - samples_needed; - write_offset += samples_to_write; - samples_needed -= samples_to_write; - CUTE_SOUND_ASSERT(samples_needed >= 0); - if (samples_needed == 0) break; - goto mix_more; + // Calculate how many output samples we can write. + int samples_to_write = samples_remaining; + + // For looped pitched sounds, CS_WRAP_SAMPLE handles wraparound so we can + // mix the full buffer. For non-looped or non-pitched sounds, we must clamp. + if (!playing->looped || !pitched) { + if (playing->pitch >= 0) { + double input_remaining = (double)audio->sample_count - playing->sample_index; + int max_output = (int)(input_remaining / playing->pitch); + if (samples_to_write > max_output) { + samples_to_write = max_output; + } + } else { + double input_remaining = playing->sample_index; + int max_output = (int)(input_remaining / -playing->pitch); + if (samples_to_write > max_output) { + samples_to_write = max_output; } - - goto remove; - } - } else if (playing->sample_index == audio->sample_count) { - if (playing->looped) { - playing->sample_index = 0; - write_offset += samples_to_write; - samples_needed -= samples_to_write; - CUTE_SOUND_ASSERT(samples_needed >= 0); - if (samples_needed == 0) break; - goto mix_more; } - - goto remove; } - } - get_next_playing_sound: - playing_node = next_node; - write_offset = 0; - continue; + if (samples_to_write <= 0) break; - remove: - playing->sample_index = 0; - playing->active = false; + int write_wide = CUTE_SOUND_ALIGN(samples_to_write, 4) / 4; + int write_offset_wide = (int)CUTE_SOUND_ALIGN(write_offset, 4) / 4; + int sample_index_wide = (int)CUTE_SOUND_TRUNC((int)playing->sample_index, 4) / 4; - if (playing->audio) { - if (s_ctx->running) { - playing->audio->playing_count -= 1; + // Mix the samples. + if (pitched) { + cs_mix_pitched(audio, vA, vB, write_offset_wide, write_wide, playing->pitch, playing->sample_index, playing->looped); } else { - playing->audio->playing_count = 0; + cs_mix_simple(audio, vA, vB, write_offset_wide, write_wide, sample_index_wide); } - CUTE_SOUND_ASSERT(playing->audio->playing_count >= 0); - } - cs_list_remove(playing_node); - cs_list_push_front(&s_ctx->free_sounds, playing_node); - cs_hashtableremove(&s_ctx->instance_map, playing->id); - playing_node = next_node; - write_offset = 0; - if (s_ctx->on_finish && !playing->is_music) { - cs_playing_sound_t snd = { playing->id }; - s_ctx->on_finish(snd, s_ctx->on_finish_udata); - } else if (s_ctx->on_music_finish && playing->is_music) { - s_ctx->on_music_finish(s_ctx->on_music_finish_udata); + // Advance by exact fractional amount. + playing->sample_index += (double)samples_to_write * (double)playing->pitch; + write_offset += samples_to_write; + samples_remaining -= samples_to_write; } - continue; - } while (playing_node != end_node); - } - - s_ctx->duplicate_count = 0; - // load all floats into 16 bit packed interleaved samples -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - - samples = s_ctx->samples; - for (int i = 0; i < s_ctx->wide_count; ++i) { - cs__m128i a = cs_mm_cvtps_epi32(floatA[i]); - cs__m128i b = cs_mm_cvtps_epi32(floatB[i]); - cs__m128i a0b0a1b1 = cs_mm_unpacklo_epi32(a, b); - cs__m128i a2b2a3b3 = cs_mm_unpackhi_epi32(a, b); - samples[i] = cs_mm_packs_epi32(a0b0a1b1, a2b2a3b3); + playing = next; + } } - cs_dsound_memcpy_to_driver((int16_t*)samples, byte_to_lock, bytes_to_write); - cs_dsound_dont_run_too_fast(); -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE || CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL - - // Since the ctx->samples array is already in use as a ring buffer - // reusing floatA to store output is a good way to temporarly store - // the final samples. Then a single ring buffer push can be used - // afterwards. Pretty hacky, but whatever :) - samples = (cs__m128i*)floatA; + // Convert floats to 16-bit packed interleaved samples. + cs__m128i* samples = s_ctx->samples; for (int i = 0; i < s_ctx->wide_count; ++i) { cs__m128i a = cs_mm_cvtps_epi32(floatA[i]); cs__m128i b = cs_mm_cvtps_epi32(floatB[i]); @@ -2541,42 +1500,7 @@ void cs_mix() samples[i] = cs_mm_packs_epi32(a0b0a1b1, a2b2a3b3); } - cs_push_bytes(samples, bytes_to_write); - -#endif - - // Free up any queue'd free's for audio sources at zero refcount. - for (int i = 0; i < s_ctx->audio_sources_to_free_size;) { - cs_audio_source_t* audio = s_ctx->audio_sources_to_free[i]; - if (audio->playing_count == 0) { - cs_free16(audio->channels[0]); - CUTE_SOUND_FREE(audio, s_mem_ctx); - s_ctx->audio_sources_to_free[i] = s_ctx->audio_sources_to_free[--s_ctx->audio_sources_to_free_size]; - } else { - ++i; - } - } - - unlock: - cs_unlock(); -} - -void cs_spawn_mix_thread() -{ - if (s_ctx->separate_thread) return; - s_ctx->separate_thread = true; -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_WINDOWS - CreateThread(0, 0, cs_ctx_thread, s_ctx, 0, 0); -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_APPLE - pthread_create(&s_ctx->thread, 0, cs_ctx_thread, s_ctx); -#elif CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL - s_ctx->thread = SDL_CreateThread(&cs_ctx_thread, "CuteSoundThread", s_ctx); -#endif -} - -void cs_mix_thread_sleep_delay(int milliseconds) -{ - s_ctx->sleep_milliseconds = milliseconds; + SDL_UnlockMutex(s_ctx->mutex); } void* cs_get_context_ptr() @@ -2611,7 +1535,7 @@ static void* cs_read_file_to_memory(const char* path, int* size) return data; } -static int cs_four_cc(const char* CC, void* memory) +static int cs_four_cc(const char* CC, const void* memory) { if (!CUTE_SOUND_MEMCMP(CC, memory, 4)) return 1; return 0; @@ -2624,27 +1548,28 @@ static char* cs_next(char* data) return data + 8 + size; } -static void cs_last_element(cs__m128* a, int i, int j, int16_t* samples, int offset) +static void cs_last_element(cs__m128* a, int i, float s0, float s1, float s2, float s3, int offset) { switch (offset) { - case 1: - a[i] = cs_mm_set_ps(samples[j], 0.0f, 0.0f, 0.0f); - break; - - case 2: - a[i] = cs_mm_set_ps(samples[j], samples[j + 1], 0.0f, 0.0f); - break; - - case 3: - a[i] = cs_mm_set_ps(samples[j], samples[j + 1], samples[j + 2], 0.0f); - break; - - case 0: - a[i] = cs_mm_set_ps(samples[j], samples[j + 1], samples[j + 2], samples[j + 3]); - break; + case 1: a[i] = cs_mm_set_ps(0.0f, 0.0f, 0.0f, s0); break; + case 2: a[i] = cs_mm_set_ps(0.0f, 0.0f, s1, s0); break; + case 3: a[i] = cs_mm_set_ps(0.0f, s2, s1, s0); break; + case 0: a[i] = cs_mm_set_ps(s3, s2, s1, s0); break; } } +// Sample conversion helpers. +static inline float cs_pcm8_to_float(uint8_t s) { return ((float)s - 128.0f) * (1.0f / 128.0f); } +static inline float cs_pcm16_to_float(int16_t s) { return (float)s; } +static inline float cs_pcm24_to_float(const uint8_t* p) { + int32_t s = (int32_t)p[0] | ((int32_t)p[1] << 8) | ((int32_t)p[2] << 16); + if (s & 0x800000) s |= 0xFF000000; // Sign extend. + return (float)s * (1.0f / 256.0f); // Scale to 16-bit range. +} +static inline float cs_pcm32_to_float(int32_t s) { return (float)s * (1.0f / 65536.0f); } // Scale to 16-bit range. +static inline float cs_float32_to_float(float s) { return s * 32767.0f; } // Scale to 16-bit range. +static inline float cs_float64_to_float(double s) { return (float)s * 32767.0f; } // Scale to 16-bit range. + cs_audio_source_t* cs_load_wav(const char* path, cs_error_t* err /* = NULL */) { int size; @@ -2655,108 +1580,139 @@ cs_audio_source_t* cs_load_wav(const char* path, cs_error_t* err /* = NULL */) return audio; } +// Helper to read a sample at a given index based on format. +typedef float (*cs_sample_fn)(const uint8_t* data, int idx, int stride); +static float cs_sample_pcm8(const uint8_t* data, int idx, int stride) { return cs_pcm8_to_float(data[idx * stride]); } +static float cs_sample_pcm16(const uint8_t* data, int idx, int stride) { return cs_pcm16_to_float(((int16_t*)data)[idx * stride]); } +static float cs_sample_pcm24(const uint8_t* data, int idx, int stride) { return cs_pcm24_to_float(data + idx * stride * 3); } +static float cs_sample_pcm32(const uint8_t* data, int idx, int stride) { return cs_pcm32_to_float(((int32_t*)data)[idx * stride]); } +static float cs_sample_float32(const uint8_t* data, int idx, int stride) { return cs_float32_to_float(((float*)data)[idx * stride]); } +static float cs_sample_float64(const uint8_t* data, int idx, int stride) { return cs_float64_to_float(((double*)data)[idx * stride]); } + cs_audio_source_t* cs_read_mem_wav(const void* memory, size_t size, cs_error_t* err) { + #define CS_WAV_FAIL(e) do { if (err) *err = e; return NULL; } while (0) if (err) *err = CUTE_SOUND_ERROR_NONE; - if (!memory) { if (err) *err = CUTE_SOUND_ERROR_FILE_NOT_FOUND; return NULL; } + if (!memory) CS_WAV_FAIL(CUTE_SOUND_ERROR_FILE_NOT_FOUND); + if (size < 12) CS_WAV_FAIL(CUTE_SOUND_ERROR_THE_FILE_IS_NOT_A_WAV_FILE); - #pragma pack(push, 1) - typedef struct - { - uint16_t wFormatTag; - uint16_t nChannels; - uint32_t nSamplesPerSec; - uint32_t nAvgBytesPerSec; - uint16_t nBlockAlign; - uint16_t wBitsPerSample; - uint16_t cbSize; - uint16_t wValidBitsPerSample; - uint32_t dwChannelMask; - uint8_t SubFormat[18]; - } Fmt; - #pragma pack(pop) - - cs_audio_source_t* audio = NULL; - char* data = (char*)memory; - char* end = data + size; - if (!cs_four_cc("RIFF", data)) { if (err) *err = CUTE_SOUND_ERROR_THE_FILE_IS_NOT_A_WAV_FILE; return NULL; } - if (!cs_four_cc("WAVE", data + 8)) { if (err) *err = CUTE_SOUND_ERROR_THE_FILE_IS_NOT_A_WAV_FILE; return NULL; } + const uint8_t* data = (const uint8_t*)memory; + const uint8_t* end = data + size; + // Validate RIFF/WAVE header. + if (!cs_four_cc("RIFF", data)) CS_WAV_FAIL(CUTE_SOUND_ERROR_THE_FILE_IS_NOT_A_WAV_FILE); + if (!cs_four_cc("WAVE", data + 8)) CS_WAV_FAIL(CUTE_SOUND_ERROR_THE_FILE_IS_NOT_A_WAV_FILE); data += 12; + // Find fmt chunk. while (1) { - if (!(end > data)) { if (err) *err = CUTE_SOUND_ERROR_WAV_FILE_FORMAT_CHUNK_NOT_FOUND; return NULL; } + if (end - data < 8) CS_WAV_FAIL(CUTE_SOUND_ERROR_WAV_FILE_FORMAT_CHUNK_NOT_FOUND); if (cs_four_cc("fmt ", data)) break; - data = cs_next(data); + data = (const uint8_t*)cs_next((char*)data); + } + uint32_t fmt_size = *(uint32_t*)(data + 4); + if (end - data < 8 + fmt_size || fmt_size < 16) CS_WAV_FAIL(CUTE_SOUND_ERROR_WAV_FILE_FORMAT_CHUNK_NOT_FOUND); + + // Parse fmt chunk. + uint16_t format_tag = *(uint16_t*)(data + 8); + uint16_t channels = *(uint16_t*)(data + 10); + uint32_t sample_rate = *(uint32_t*)(data + 12); + uint16_t bits_per_sample = *(uint16_t*)(data + 22); + + // Validate format. + if (!(channels == 1 || channels == 2)) CS_WAV_FAIL(CUTE_SOUND_ERROR_WAV_ONLY_MONO_OR_STEREO_IS_SUPPORTED); + + cs_sample_fn sample_fn = NULL; + int bytes_per_sample = 0; + if (format_tag == 1) { + // PCM + switch (bits_per_sample) { + case 8: sample_fn = cs_sample_pcm8; bytes_per_sample = 1; break; + case 16: sample_fn = cs_sample_pcm16; bytes_per_sample = 2; break; + case 24: sample_fn = cs_sample_pcm24; bytes_per_sample = 3; break; + case 32: sample_fn = cs_sample_pcm32; bytes_per_sample = 4; break; + default: CS_WAV_FAIL(CUTE_SOUND_ERROR_WAV_UNSUPPORTED_FORMAT); + } + } else if (format_tag == 3) { + // IEEE float + switch (bits_per_sample) { + case 32: sample_fn = cs_sample_float32; bytes_per_sample = 4; break; + case 64: sample_fn = cs_sample_float64; bytes_per_sample = 8; break; + default: CS_WAV_FAIL(CUTE_SOUND_ERROR_WAV_UNSUPPORTED_FORMAT); + } + } else { + CS_WAV_FAIL(CUTE_SOUND_ERROR_WAV_UNSUPPORTED_FORMAT); } - Fmt fmt; - fmt = *(Fmt*)(data + 8); - if (fmt.wFormatTag != 1) { if (err) *err = CUTE_SOUND_ERROR_WAV_FILE_FORMAT_CHUNK_NOT_FOUND; return NULL; } - if (!(fmt.nChannels == 1 || fmt.nChannels == 2)) { if (err) *err = CUTE_SOUND_ERROR_WAV_ONLY_MONO_OR_STEREO_IS_SUPPORTED; return NULL; } - if (!(fmt.wBitsPerSample == 16)) { if (err) *err = CUTE_SOUND_ERROR_WAV_ONLY_16_BITS_PER_SAMPLE_SUPPORTED; return NULL; } - if (!(fmt.nBlockAlign == fmt.nChannels * 2)) { if (err) *err = CUTE_SOUND_ERROR_IMPLEMENTATION_ERROR_PLEASE_REPORT_THIS_ON_GITHUB; return NULL; } - + // Find data chunk. while (1) { - if (!(end > data)) { if (err) *err = CUTE_SOUND_ERROR_WAV_DATA_CHUNK_NOT_FOUND; return NULL; } + if (end - data < 8) CS_WAV_FAIL(CUTE_SOUND_ERROR_WAV_DATA_CHUNK_NOT_FOUND); if (cs_four_cc("data", data)) break; - data = cs_next(data); + data = (const uint8_t*)cs_next((char*)data); } + uint32_t data_size = *(uint32_t*)(data + 4); + const uint8_t* samples = data + 8; + if (end - samples < data_size) CS_WAV_FAIL(CUTE_SOUND_ERROR_WAV_DATA_CHUNK_NOT_FOUND); - audio = (cs_audio_source_t*)CUTE_SOUND_ALLOC(sizeof(cs_audio_source_t), s_mem_ctx); - CUTE_SOUND_MEMSET(audio, 0, sizeof(*audio)); - audio->sample_rate = (int)fmt.nSamplesPerSec; - - { - int sample_size = *((uint32_t*)(data + 4)); - int sample_count = sample_size / (fmt.nChannels * sizeof(uint16_t)); - audio->sample_count = sample_count; - audio->channel_count = fmt.nChannels; - - int wide_count = (int)CUTE_SOUND_ALIGN(sample_count, 4) / 4; - int wide_offset = sample_count & 3; - int16_t* samples = (int16_t*)(data + 8); - - switch (audio->channel_count) { - case 1: - { - audio->channels[0] = cs_malloc16(wide_count * sizeof(cs__m128)); - audio->channels[1] = 0; - cs__m128* a = (cs__m128*)audio->channels[0]; - for (int i = 0, j = 0; i < wide_count - 1; ++i, j += 4) { - a[i] = cs_mm_set_ps((float)samples[j+3], (float)samples[j+2], (float)samples[j+1], (float)samples[j]); - } - cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, samples, wide_offset); - } break; - - case 2: - { - cs__m128* a = (cs__m128*)cs_malloc16(wide_count * sizeof(cs__m128) * 2); - cs__m128* b = a + wide_count; - for (int i = 0, j = 0; i < wide_count - 1; ++i, j += 8){ - a[i] = cs_mm_set_ps((float)samples[j+6], (float)samples[j+4], (float)samples[j+2], (float)samples[j]); - b[i] = cs_mm_set_ps((float)samples[j+7], (float)samples[j+5], (float)samples[j+3], (float)samples[j+1]); - } - cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, samples, wide_offset); - cs_last_element(b, wide_count - 1, (wide_count - 1) * 4 + 4, samples, wide_offset); - audio->channels[0] = a; - audio->channels[1] = b; - } break; + // Calculate sample count. + int64_t frame_size = channels * bytes_per_sample; + int64_t sample_count = data_size / frame_size; - default: - if (err) *err = CUTE_SOUND_ERROR_WAV_ONLY_MONO_OR_STEREO_IS_SUPPORTED; - CUTE_SOUND_ASSERT(false); + // Allocate audio source. + cs_audio_source_t* audio = (cs_audio_source_t*)CUTE_SOUND_ALLOC(sizeof(cs_audio_source_t), s_mem_ctx); + CUTE_SOUND_MEMSET(audio, 0, sizeof(*audio)); + audio->sample_rate = (int)sample_rate; + audio->sample_count = (int)sample_count; + audio->channel_count = channels; + + int wide_count = (int)CUTE_SOUND_ALIGN(sample_count, 4) / 4; + int wide_offset = (int)(sample_count & 3); + + if (channels == 1) { + audio->channels[0] = cs_malloc16(wide_count * sizeof(cs__m128)); + audio->channels[1] = NULL; + cs__m128* a = (cs__m128*)audio->channels[0]; + for (int i = 0; i < wide_count - 1; ++i) { + int j = i * 4; + a[i] = cs_mm_set_ps(sample_fn(samples, j+3, 1), sample_fn(samples, j+2, 1), sample_fn(samples, j+1, 1), sample_fn(samples, j, 1)); + } + int j = (wide_count - 1) * 4; + float s0 = sample_fn(samples, j, 1); + float s1 = wide_offset > 1 ? sample_fn(samples, j+1, 1) : 0; + float s2 = wide_offset > 2 ? sample_fn(samples, j+2, 1) : 0; + float s3 = wide_offset > 3 ? sample_fn(samples, j+3, 1) : 0; + cs_last_element(a, wide_count - 1, s0, s1, s2, s3, wide_offset); + } else { + cs__m128* a = (cs__m128*)cs_malloc16(wide_count * sizeof(cs__m128) * 2); + cs__m128* b = a + wide_count; + for (int i = 0; i < wide_count - 1; ++i) { + int j = i * 4; + a[i] = cs_mm_set_ps(sample_fn(samples, (j+3)*2, 1), sample_fn(samples, (j+2)*2, 1), sample_fn(samples, (j+1)*2, 1), sample_fn(samples, j*2, 1)); + b[i] = cs_mm_set_ps(sample_fn(samples, (j+3)*2+1, 1), sample_fn(samples, (j+2)*2+1, 1), sample_fn(samples, (j+1)*2+1, 1), sample_fn(samples, j*2+1, 1)); } + int j = (wide_count - 1) * 4; + float a0 = sample_fn(samples, j*2, 1); + float a1 = wide_offset > 1 ? sample_fn(samples, (j+1)*2, 1) : 0; + float a2 = wide_offset > 2 ? sample_fn(samples, (j+2)*2, 1) : 0; + float a3 = wide_offset > 3 ? sample_fn(samples, (j+3)*2, 1) : 0; + float b0 = sample_fn(samples, j*2+1, 1); + float b1 = wide_offset > 1 ? sample_fn(samples, (j+1)*2+1, 1) : 0; + float b2 = wide_offset > 2 ? sample_fn(samples, (j+2)*2+1, 1) : 0; + float b3 = wide_offset > 3 ? sample_fn(samples, (j+3)*2+1, 1) : 0; + cs_last_element(a, wide_count - 1, a0, a1, a2, a3, wide_offset); + cs_last_element(b, wide_count - 1, b0, b1, b2, b3, wide_offset); + audio->channels[0] = a; + audio->channels[1] = b; } - if (err) *err = CUTE_SOUND_ERROR_NONE; + #undef CS_WAV_FAIL return audio; } void cs_free_audio_source(cs_audio_source_t* audio) { if (s_ctx) { - cs_lock(); + SDL_LockMutex(s_ctx->mutex); if (audio->playing_count == 0) { cs_free16(audio->channels[0]); CUTE_SOUND_FREE(audio, s_mem_ctx); @@ -2771,7 +1727,7 @@ void cs_free_audio_source(cs_audio_source_t* audio) } s_ctx->audio_sources_to_free[s_ctx->audio_sources_to_free_size++] = audio; } - cs_unlock(); + SDL_UnlockMutex(s_ctx->mutex); } else { CUTE_SOUND_ASSERT(audio->playing_count == 0); cs_free16(audio->channels[0]); @@ -2794,7 +1750,7 @@ int cs_get_channel_count(const cs_audio_source_t* audio) return audio->channel_count; } -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL && defined(SDL_rwops_h_) && defined(CUTE_SOUND_SDL_RWOPS) +#if defined(SDL_rwops_h_) && defined(CUTE_SOUND_SDL_RWOPS) // Load an SDL_RWops object's data into memory. // Ripped straight from: https://wiki.libsdl.org/SDL_RWread @@ -2828,8 +1784,8 @@ int cs_get_channel_count(const cs_audio_source_t* audio) { int size; char* wav = (char*)cs_read_rw_to_memory(context, &size); - if (!memory) return NULL; - cs_audio_source_t* audio = cs_read_mem_wav(wav, length, err); + if (!wav) return NULL; + cs_audio_source_t* audio = cs_read_mem_wav(wav, size, err); CUTE_SOUND_FREE(wav, s_mem_ctx); return audio; } @@ -2866,19 +1822,34 @@ cs_audio_source_t* cs_read_mem_ogg(const void* memory, size_t length, cs_error_t for (int i = 0, j = 0; i < wide_count - 1; ++i, j += 4) { a[i] = cs_mm_set_ps((float)samples[j+3], (float)samples[j+2], (float)samples[j+1], (float)samples[j]); } - cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, samples, wide_offset); + int j = (wide_count - 1) * 4; + float s0 = (float)samples[j]; + float s1 = wide_offset > 1 ? (float)samples[j+1] : 0; + float s2 = wide_offset > 2 ? (float)samples[j+2] : 0; + float s3 = wide_offset > 3 ? (float)samples[j+3] : 0; + cs_last_element(a, wide_count - 1, s0, s1, s2, s3, wide_offset); } break; case 2: + { a = (cs__m128*)cs_malloc16(wide_count * sizeof(cs__m128) * 2); b = a + wide_count; for (int i = 0, j = 0; i < wide_count - 1; ++i, j += 8) { a[i] = cs_mm_set_ps((float)samples[j+6], (float)samples[j+4], (float)samples[j+2], (float)samples[j]); b[i] = cs_mm_set_ps((float)samples[j+7], (float)samples[j+5], (float)samples[j+3], (float)samples[j+1]); } - cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, samples, wide_offset); - cs_last_element(b, wide_count - 1, (wide_count - 1) * 4 + 4, samples, wide_offset); - break; + int j = (wide_count - 1) * 8; + float a0 = (float)samples[j]; + float a1 = wide_offset > 1 ? (float)samples[j+2] : 0; + float a2 = wide_offset > 2 ? (float)samples[j+4] : 0; + float a3 = wide_offset > 3 ? (float)samples[j+6] : 0; + float b0 = (float)samples[j+1]; + float b1 = wide_offset > 1 ? (float)samples[j+3] : 0; + float b2 = wide_offset > 2 ? (float)samples[j+5] : 0; + float b3 = wide_offset > 3 ? (float)samples[j+7] : 0; + cs_last_element(a, wide_count - 1, a0, a1, a2, a3, wide_offset); + cs_last_element(b, wide_count - 1, b0, b1, b2, b3, wide_offset); + } break; default: if (err) *err = CUTE_SOUND_ERROR_OGG_UNSUPPORTED_CHANNEL_COUNT; @@ -2908,14 +1879,14 @@ cs_audio_source_t* cs_load_ogg(const char* path, cs_error_t* err) return audio; } -#if CUTE_SOUND_PLATFORM == CUTE_SOUND_SDL && defined(SDL_rwops_h_) && defined(CUTE_SOUND_SDL_RWOPS) +#if defined(SDL_rwops_h_) && defined(CUTE_SOUND_SDL_RWOPS) cs_audio_source_t* cs_load_ogg_rw(SDL_RWops* rw, cs_error_t* err) { int length; void* memory = cs_read_rw_to_memory(rw, &length); if (!memory) return NULL; - cs_audio_source_t* audio = cs_read_ogg_wav(memory, length, err); + cs_audio_source_t* audio = cs_read_mem_ogg(memory, length, err); CUTE_SOUND_FREE(memory, s_mem_ctx); return audio; } @@ -2926,24 +1897,36 @@ cs_audio_source_t* cs_load_ogg(const char* path, cs_error_t* err) // ------------------------------------------------------------------------------------------------- // Music sounds. +static cs_sound_inst_t* s_alloc_inst() +{ + if (!s_ctx->free_sounds) { + s_add_page(); + } + CUTE_SOUND_ASSERT(s_ctx->free_sounds); + cs_sound_inst_t* inst = s_ctx->free_sounds; + s_ctx->free_sounds = inst->next; + if (s_ctx->free_sounds) s_ctx->free_sounds->prev = NULL; + return inst; +} + static void s_insert(cs_sound_inst_t* inst) { - cs_lock(); - cs_list_push_back(&s_ctx->playing_sounds, &inst->node); + SDL_LockMutex(s_ctx->mutex); + // Add to playing list. + inst->next = s_ctx->playing_sounds; + inst->prev = NULL; + if (s_ctx->playing_sounds) s_ctx->playing_sounds->prev = inst; + s_ctx->playing_sounds = inst; inst->audio->playing_count += 1; inst->active = true; inst->id = s_ctx->instance_id_gen++; - cs_hashtableinsert(&s_ctx->instance_map, inst->id, &inst); - cs_unlock(); + cs_map_insert(&s_ctx->instance_map, inst->id, inst); + SDL_UnlockMutex(s_ctx->mutex); } static cs_sound_inst_t* s_inst_music(cs_audio_source_t* src, float volume) { - if (cs_list_empty(&s_ctx->free_sounds)) { - s_add_page(); - } - CUTE_SOUND_ASSERT(!cs_list_empty(&s_ctx->free_sounds)); - cs_sound_inst_t* inst = CUTE_SOUND_LIST_HOST(cs_sound_inst_t, node, cs_list_pop_back(&s_ctx->free_sounds)); + cs_sound_inst_t* inst = s_alloc_inst(); inst->is_music = true; inst->looped = s_ctx->music_looped; if (!s_ctx->music_paused) inst->paused = false; @@ -2953,18 +1936,13 @@ static cs_sound_inst_t* s_inst_music(cs_audio_source_t* src, float volume) inst->pitch = 1.0f; inst->audio = src; inst->sample_index = 0; - cs_list_init_node(&inst->node); s_insert(inst); return inst; } static cs_sound_inst_t* s_inst(cs_audio_source_t* src, cs_sound_params_t params) { - if (cs_list_empty(&s_ctx->free_sounds)) { - s_add_page(); - } - CUTE_SOUND_ASSERT(!cs_list_empty(&s_ctx->free_sounds)); - cs_sound_inst_t* inst = CUTE_SOUND_LIST_HOST(cs_sound_inst_t, node, cs_list_pop_back(&s_ctx->free_sounds)); + cs_sound_inst_t* inst = s_alloc_inst(); float pan = params.pan; if (pan > 1.0f) pan = 1.0f; else if (pan < 0.0f) pan = 0.0f; @@ -2978,9 +1956,8 @@ static cs_sound_inst_t* s_inst(cs_audio_source_t* src, cs_sound_params_t params) inst->pan1 = panr; inst->pitch = params.pitch; inst->audio = src; - inst->sample_index = params.sample_index; - CUTE_SOUND_ASSERT(inst->sample_index < src->sample_count); - cs_list_init_node(&inst->node); + inst->sample_index = params.start_time * (double)src->sample_rate; + CUTE_SOUND_ASSERT(inst->sample_index < (double)src->sample_count); s_insert(inst); return inst; } @@ -3028,6 +2005,7 @@ void cs_music_stop(float fade_out_time) break; case CUTE_SOUND_MUSIC_STATE_PLAYING: + s_ctx->fade_start_volume = s_ctx->music_playing->volume; s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_FADE_OUT; s_ctx->fade = fade_out_time; s_ctx->t = 0; @@ -3037,34 +2015,37 @@ void cs_music_stop(float fade_out_time) break; case CUTE_SOUND_MUSIC_STATE_FADE_IN: - { + s_ctx->fade_start_volume = s_ctx->music_playing->volume; s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_FADE_OUT; - s_ctx->t = s_smoothstep(((s_ctx->fade - s_ctx->t) / s_ctx->fade)); s_ctx->fade = fade_out_time; - } break; + s_ctx->t = 0; + break; case CUTE_SOUND_MUSIC_STATE_SWITCH_TO_0: - { + s_ctx->fade_start_volume = s_ctx->music_playing->volume; s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_FADE_OUT; - s_ctx->t = s_smoothstep(((s_ctx->fade - s_ctx->t) / s_ctx->fade)); s_ctx->fade = fade_out_time; + s_ctx->t = 0; + s_ctx->music_next->active = false; s_ctx->music_next = NULL; - } break; + break; case CUTE_SOUND_MUSIC_STATE_SWITCH_TO_1: // Fall-through. - case CUTE_SOUND_MUSIC_STATE_CROSSFADE: - { + // Fade out the incoming track (which is louder). + s_ctx->fade_start_volume = s_ctx->music_next->volume; s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_FADE_OUT; - s_ctx->t = s_smoothstep(((s_ctx->fade - s_ctx->t) / s_ctx->fade)); s_ctx->fade = fade_out_time; + s_ctx->t = 0; + s_ctx->music_playing->active = false; s_ctx->music_playing = s_ctx->music_next; s_ctx->music_next = NULL; - } break; + break; case CUTE_SOUND_MUSIC_STATE_PAUSED: cs_music_stop(0); + break; } } } @@ -3124,7 +2105,7 @@ void cs_music_switch_to(cs_audio_source_t* audio_source, float fade_out_time, fl CUTE_SOUND_ASSERT(s_ctx->music_next == NULL); cs_sound_inst_t* inst = s_inst_music(audio_source, fade_in_time == 0 ? 1.0f : 0); s_ctx->music_next = inst; - + s_ctx->fade_start_volume = s_ctx->music_playing->volume; s_ctx->fade = fade_out_time; s_ctx->fade_switch_1 = fade_in_time; s_ctx->t = 0; @@ -3133,10 +2114,10 @@ void cs_music_switch_to(cs_audio_source_t* audio_source, float fade_out_time, fl case CUTE_SOUND_MUSIC_STATE_FADE_OUT: { + // Continue existing fade out, just add the incoming track. CUTE_SOUND_ASSERT(s_ctx->music_next == NULL); cs_sound_inst_t* inst = s_inst_music(audio_source, fade_in_time == 0 ? 1.0f : 0); s_ctx->music_next = inst; - s_ctx->fade_switch_1 = fade_in_time; s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_SWITCH_TO_0; } break; @@ -3146,14 +2127,16 @@ void cs_music_switch_to(cs_audio_source_t* audio_source, float fade_out_time, fl CUTE_SOUND_ASSERT(s_ctx->music_next == NULL); cs_sound_inst_t* inst = s_inst_music(audio_source, fade_in_time == 0 ? 1.0f : 0); s_ctx->music_next = inst; - + s_ctx->fade_start_volume = s_ctx->music_playing->volume; + s_ctx->fade = fade_out_time; s_ctx->fade_switch_1 = fade_in_time; - s_ctx->t = s_smoothstep(((s_ctx->fade - s_ctx->t) / s_ctx->fade)); + s_ctx->t = 0; s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_SWITCH_TO_0; } break; case CUTE_SOUND_MUSIC_STATE_SWITCH_TO_0: { + // Replace pending incoming track with new one. CUTE_SOUND_ASSERT(s_ctx->music_next != NULL); cs_sound_inst_t* inst = s_inst_music(audio_source, fade_in_time == 0 ? 1.0f : 0); s_ctx->music_next->active = false; @@ -3161,17 +2144,19 @@ void cs_music_switch_to(cs_audio_source_t* audio_source, float fade_out_time, fl s_ctx->fade_switch_1 = fade_in_time; } break; - case CUTE_SOUND_MUSIC_STATE_CROSSFADE: // Fall-through. + case CUTE_SOUND_MUSIC_STATE_CROSSFADE: case CUTE_SOUND_MUSIC_STATE_SWITCH_TO_1: { + // Incoming track becomes the one to fade out. CUTE_SOUND_ASSERT(s_ctx->music_next != NULL); cs_sound_inst_t* inst = s_inst_music(audio_source, fade_in_time == 0 ? 1.0f : 0); + s_ctx->music_playing->active = false; s_ctx->music_playing = s_ctx->music_next; s_ctx->music_next = inst; - - s_ctx->t = s_smoothstep(((s_ctx->fade - s_ctx->t) / s_ctx->fade)); - s_ctx->fade_switch_1 = fade_in_time; + s_ctx->fade_start_volume = s_ctx->music_playing->volume; s_ctx->fade = fade_out_time; + s_ctx->fade_switch_1 = fade_in_time; + s_ctx->t = 0; s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_SWITCH_TO_0; } break; @@ -3189,18 +2174,18 @@ void cs_music_crossfade(cs_audio_source_t* audio_source, float cross_fade_time) switch (s_ctx->music_state) { case CUTE_SOUND_MUSIC_STATE_NONE: cs_music_play(audio_source, cross_fade_time); + break; case CUTE_SOUND_MUSIC_STATE_PLAYING: { CUTE_SOUND_ASSERT(s_ctx->music_next == NULL); - s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_CROSSFADE; - cs_sound_inst_t* inst = s_inst_music(audio_source, cross_fade_time == 0 ? 1.0f : 0); inst->paused = false; s_ctx->music_next = inst; - + s_ctx->fade_start_volume = s_ctx->music_playing->volume; s_ctx->fade = cross_fade_time; s_ctx->t = 0; + s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_CROSSFADE; } break; case CUTE_SOUND_MUSIC_STATE_FADE_OUT: @@ -3209,57 +2194,61 @@ void cs_music_crossfade(cs_audio_source_t* audio_source, float cross_fade_time) case CUTE_SOUND_MUSIC_STATE_FADE_IN: { - s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_CROSSFADE; - cs_sound_inst_t* inst = s_inst_music(audio_source, cross_fade_time == 0 ? 1.0f : 0); inst->paused = false; s_ctx->music_next = inst; - + s_ctx->fade_start_volume = s_ctx->music_playing->volume; s_ctx->fade = cross_fade_time; + s_ctx->t = 0; + s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_CROSSFADE; } break; case CUTE_SOUND_MUSIC_STATE_SWITCH_TO_0: { - s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_CROSSFADE; s_ctx->music_next->active = false; - cs_sound_inst_t* inst = s_inst_music(audio_source, cross_fade_time == 0 ? 1.0f : 0); inst->paused = false; s_ctx->music_next = inst; - + s_ctx->fade_start_volume = s_ctx->music_playing->volume; s_ctx->fade = cross_fade_time; + s_ctx->t = 0; + s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_CROSSFADE; } break; - case CUTE_SOUND_MUSIC_STATE_SWITCH_TO_1: // Fall-through. + case CUTE_SOUND_MUSIC_STATE_SWITCH_TO_1: + // Fall-through. + case CUTE_SOUND_MUSIC_STATE_CROSSFADE: { - s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_CROSSFADE; s_ctx->music_playing->active = false; s_ctx->music_playing = s_ctx->music_next; - cs_sound_inst_t* inst = s_inst_music(audio_source, cross_fade_time == 0 ? 1.0f : 0); inst->paused = false; s_ctx->music_next = inst; - + s_ctx->fade_start_volume = s_ctx->music_playing->volume; s_ctx->fade = cross_fade_time; + s_ctx->t = 0; + s_ctx->music_state = CUTE_SOUND_MUSIC_STATE_CROSSFADE; } break; case CUTE_SOUND_MUSIC_STATE_PAUSED: cs_music_stop(0); cs_music_crossfade(audio_source, cross_fade_time); + break; } } -int cs_music_get_sample_index() +double cs_music_get_time() { - if (s_ctx->music_playing) return 0; - else return s_ctx->music_playing->sample_index; + if (!s_ctx->music_playing) return 0.0; + return s_ctx->music_playing->sample_index / (double)s_ctx->music_playing->audio->sample_rate; } -cs_error_t cs_music_set_sample_index(int sample_index) +cs_error_t cs_music_set_time(double time_in_seconds) { - if (s_ctx->music_playing) return CUTE_SOUND_ERROR_INVALID_SOUND; - if (sample_index > s_ctx->music_playing->audio->sample_count) return CUTE_SOUND_ERROR_TRIED_TO_SET_SAMPLE_INDEX_BEYOND_THE_AUDIO_SOURCES_SAMPLE_COUNT; + if (!s_ctx->music_playing) return CUTE_SOUND_ERROR_INVALID_SOUND; + double sample_index = time_in_seconds * (double)s_ctx->music_playing->audio->sample_rate; + if (sample_index > (double)s_ctx->music_playing->audio->sample_count) return CUTE_SOUND_ERROR_TRIED_TO_SET_SAMPLE_INDEX_BEYOND_THE_AUDIO_SOURCES_SAMPLE_COUNT; s_ctx->music_playing->sample_index = sample_index; return CUTE_SOUND_ERROR_NONE; } @@ -3275,15 +2264,13 @@ cs_sound_params_t cs_sound_params_default() params.volume = 1.0f; params.pan = 0.5f; params.pitch = 1.0f; - params.sample_index = 0; + params.start_time = 0.0; return params; } static cs_sound_inst_t* s_get_inst(cs_playing_sound_t sound) { - cs_sound_inst_t** inst = (cs_sound_inst_t**)cs_hashtablefind(&s_ctx->instance_map, sound.id); - if (inst) return *inst; - return NULL; + return (cs_sound_inst_t*)cs_map_find(&s_ctx->instance_map, sound.id); } cs_playing_sound_t cs_play_sound(cs_audio_source_t* audio, cs_sound_params_t params) @@ -3347,11 +2334,11 @@ float cs_sound_get_pan(cs_playing_sound_t sound) return inst->pan1; } -int cs_sound_get_sample_index(cs_playing_sound_t sound) +double cs_sound_get_time(cs_playing_sound_t sound) { cs_sound_inst_t* inst = s_get_inst(sound); - if (!inst) return 0; - return inst->sample_index; + if (!inst) return 0.0; + return inst->sample_index / (double)inst->audio->sample_rate; } void cs_sound_set_is_paused(cs_playing_sound_t sound, bool true_for_paused) @@ -3393,11 +2380,12 @@ void cs_sound_set_pan(cs_playing_sound_t sound, float pan_0_to_1) inst->pan1 = pan_0_to_1; } -cs_error_t cs_sound_set_sample_index(cs_playing_sound_t sound, int sample_index) +cs_error_t cs_sound_set_time(cs_playing_sound_t sound, double time_in_seconds) { cs_sound_inst_t* inst = s_get_inst(sound); if (!inst) return CUTE_SOUND_ERROR_INVALID_SOUND; - if (sample_index > inst->audio->sample_count) return CUTE_SOUND_ERROR_TRIED_TO_SET_SAMPLE_INDEX_BEYOND_THE_AUDIO_SOURCES_SAMPLE_COUNT; + double sample_index = time_in_seconds * (double)inst->audio->sample_rate; + if (sample_index > (double)inst->audio->sample_count) return CUTE_SOUND_ERROR_TRIED_TO_SET_SAMPLE_INDEX_BEYOND_THE_AUDIO_SOURCES_SAMPLE_COUNT; inst->sample_index = sample_index; return CUTE_SOUND_ERROR_NONE; } @@ -3417,31 +2405,14 @@ void cs_set_playing_sounds_volume(float volume_0_to_1) void cs_stop_all_playing_sounds() { - cs_lock(); - + SDL_LockMutex(s_ctx->mutex); // Set all playing sounds (that aren't music) active to false. - if (cs_list_empty(&s_ctx->playing_sounds)) { - cs_unlock(); - return; - } - cs_list_node_t* playing_sound = cs_list_begin(&s_ctx->playing_sounds); - cs_list_node_t* end = cs_list_end(&s_ctx->playing_sounds); - - do { - cs_sound_inst_t* inst = CUTE_SOUND_LIST_HOST(cs_sound_inst_t, node, playing_sound); - cs_list_node_t* next = playing_sound->next; + for (cs_sound_inst_t* inst = s_ctx->playing_sounds; inst; inst = inst->next) { if (inst != s_ctx->music_playing && inst != s_ctx->music_next) { inst->active = false; // Let cs_mix handle cleaning this up. } - playing_sound = next; - } while (playing_sound != end); - - cs_unlock(); -} - -void cs_cull_duplicates(bool true_to_enable) -{ - s_ctx->cull_duplicates = true_to_enable; + } + SDL_UnlockMutex(s_ctx->mutex); } void* cs_get_global_context() @@ -3472,7 +2443,7 @@ void cs_set_global_user_allocator_context(void* user_allocator_context) This software is available under 2 licenses - you may choose the one you like. ------------------------------------------------------------------------------ ALTERNATIVE A - zlib license - Copyright (c) 2023 Randy Gaul https://randygaul.github.io/ + Copyright (c) 2026 Randy Gaul https://randygaul.github.io/ 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. diff --git a/libraries/cute/cute_spritebatch.h b/libraries/cute/cute_spritebatch.h index 5a3d134a5..638d401b2 100644 --- a/libraries/cute/cute_spritebatch.h +++ b/libraries/cute/cute_spritebatch.h @@ -3,7 +3,7 @@ Licensing information can be found at the end of the file. ------------------------------------------------------------------------------ - cute_spritebatch.h - v1.06 + cute_spritebatch.h - v1.08 To create implementation (the function definitions) #define SPRITEBATCH_IMPLEMENTATION @@ -146,6 +146,8 @@ 1.06 (03/24/2023) added `spritebatch_invalidate`, useful for updating pixels NOW 1.07 (10/01/2024) Removed public `sort_bits` since it's not actually useful, and reused it internally to implement stable sorting (bugfix). + 1.08 (02/01/2026) Replaced external hashtable.h with simpler internal spritebatch_map_t, + fixed memory context bugs in spritebatch_term and get_pixels. */ /* @@ -376,89 +378,28 @@ struct spritebatch_config_t #if !defined(SPRITE_BATCH_INTERNAL_H) -// hashtable.h implementation by Mattias Gustavsson -// See: http://www.mattiasgustavsson.com/ and https://github.com/mattiasgustavsson/libs/blob/master/hashtable.h -// begin hashtable.h +// spritebatch_map_t - A simple hash map for SPRITEBATCH_U64 keys to fixed-size values. +// Uses open addressing with linear probing and maintains a dense item array for fast iteration. -/* ------------------------------------------------------------------------------- - Licensing information can be found at the end of the file. ------------------------------------------------------------------------------- - -hashtable.h - v1.1 - Cache efficient hash table implementation for C/C++. - -Do this: - #define HASHTABLE_IMPLEMENTATION -before you include this file in *one* C/C++ file to create the implementation. -*/ - -#ifndef hashtable_h -#define hashtable_h - -#ifndef HASHTABLE_U64 - #define HASHTABLE_U64 unsigned long long -#endif - -typedef struct hashtable_t hashtable_t; - -void hashtable_init( hashtable_t* table, int item_size, int initial_capacity, void* memctx ); -void hashtable_term( hashtable_t* table ); - -void* hashtable_insert( hashtable_t* table, HASHTABLE_U64 key, void const* item ); -void hashtable_remove( hashtable_t* table, HASHTABLE_U64 key ); -void hashtable_clear( hashtable_t* table ); - -void* hashtable_find( hashtable_t const* table, HASHTABLE_U64 key ); - -int hashtable_count( hashtable_t const* table ); -void* hashtable_items( hashtable_t const* table ); -HASHTABLE_U64 const* hashtable_keys( hashtable_t const* table ); - -void hashtable_swap( hashtable_t* table, int index_a, int index_b ); - - -#endif /* hashtable_h */ - -/* ----------------------- - IMPLEMENTATION ----------------------- -*/ - -#ifndef hashtable_t_h -#define hashtable_t_h - -#ifndef HASHTABLE_U32 - #define HASHTABLE_U32 unsigned int -#endif - -struct hashtable_internal_slot_t - { - HASHTABLE_U32 key_hash; - int item_index; - int base_count; - }; - -struct hashtable_t - { - void* memctx; - int count; - int item_size; - - struct hashtable_internal_slot_t* slots; - int slot_capacity; - - HASHTABLE_U64* items_key; - int* items_slot; - void* items_data; - int item_capacity; - - void* swap_temp; - }; - -#endif /* hashtable_t_h */ +typedef struct spritebatch_map_slot_t +{ + unsigned key_hash; + int item_index; + int base_count; +} spritebatch_map_slot_t; -// end hashtable.h (more later) +typedef struct spritebatch_map_t +{ + void* mem_ctx; + int count; + int item_size; + spritebatch_map_slot_t* slots; + int slot_capacity; + SPRITEBATCH_U64* keys; + int* item_slots; + void* items; + int item_capacity; +} spritebatch_map_t; typedef struct spritebatch_internal_sprite_t { @@ -486,7 +427,7 @@ typedef struct spritebatch_internal_atlas_t { SPRITEBATCH_U64 texture_id; float volume_ratio; - hashtable_t sprites_to_textures; + spritebatch_map_t sprites_to_textures; struct spritebatch_internal_atlas_t* next; struct spritebatch_internal_atlas_t* prev; } spritebatch_internal_atlas_t; @@ -525,9 +466,9 @@ struct spritebatch_t int pixel_buffer_size; // number of pixels void* pixel_buffer; - hashtable_t sprites_to_premades; - hashtable_t sprites_to_lonely_textures; - hashtable_t sprites_to_atlases; + spritebatch_map_t sprites_to_premades; + spritebatch_map_t sprites_to_lonely_textures; + spritebatch_map_t sprites_to_atlases; spritebatch_internal_atlas_t* atlases; @@ -606,26 +547,6 @@ struct spritebatch_t #endif #endif -#ifndef HASHTABLE_MEMSET - #define HASHTABLE_MEMSET(ptr, val, n) SPRITEBATCH_MEMSET(ptr, val, n) -#endif - -#ifndef HASHTABLE_MEMCPY - #define HASHTABLE_MEMCPY(dst, src, n) SPRITEBATCH_MEMCPY(dst, src, n) -#endif - -#ifndef HASHTABLE_MALLOC - #define HASHTABLE_MALLOC(ctx, size) SPRITEBATCH_MALLOC(size, ctx) -#endif - -#ifndef HASHTABLE_FREE - #define HASHTABLE_FREE(ctx, ptr) SPRITEBATCH_FREE(ptr, ctx) -#endif - -#ifndef HASHTABLE_ASSERT - #define HASHTABLE_ASSERT SPRITEBATCH_ASSERT -#endif - #define SPRITE_BATCH_INTERNAL_H #endif @@ -633,393 +554,245 @@ struct spritebatch_t #ifndef SPRITEBATCH_IMPLEMENTATION_ONCE #define SPRITEBATCH_IMPLEMENTATION_ONCE -#define HASHTABLE_IMPLEMENTATION - -#ifdef HASHTABLE_IMPLEMENTATION -#ifndef HASHTABLE_IMPLEMENTATION_ONCE -#define HASHTABLE_IMPLEMENTATION_ONCE - -// hashtable.h implementation by Mattias Gustavsson -// See: http://www.mattiasgustavsson.com/ and https://github.com/mattiasgustavsson/libs/blob/master/hashtable.h -// begin hashtable.h (continuing from first time) - -#ifndef HASHTABLE_SIZE_T - #include - #define HASHTABLE_SIZE_T size_t -#endif - -#ifndef HASHTABLE_ASSERT - #include - #define HASHTABLE_ASSERT( x ) assert( x ) -#endif - -#ifndef HASHTABLE_MEMSET - #include - #define HASHTABLE_MEMSET( ptr, val, cnt ) ( memset( ptr, val, cnt ) ) -#endif - -#ifndef HASHTABLE_MEMCPY - #include - #define HASHTABLE_MEMCPY( dst, src, cnt ) ( memcpy( dst, src, cnt ) ) -#endif - -#ifndef HASHTABLE_MALLOC - #include - #define HASHTABLE_MALLOC( ctx, size ) ( malloc( size ) ) - #define HASHTABLE_FREE( ctx, ptr ) ( free( ptr ) ) -#endif - - -static HASHTABLE_U32 hashtable_internal_pow2ceil( HASHTABLE_U32 v ) - { - --v; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - ++v; - v += ( v == 0 ); - return v; - } - - -void hashtable_init( hashtable_t* table, int item_size, int initial_capacity, void* memctx ) - { - initial_capacity = (int)hashtable_internal_pow2ceil( initial_capacity >=0 ? (HASHTABLE_U32) initial_capacity : 32U ); - table->memctx = memctx; - table->count = 0; - table->item_size = item_size; - table->slot_capacity = (int) hashtable_internal_pow2ceil( (HASHTABLE_U32) ( initial_capacity + initial_capacity / 2 ) ); - int slots_size = (int)( table->slot_capacity * sizeof( *table->slots ) ); - table->slots = (struct hashtable_internal_slot_t*) HASHTABLE_MALLOC( table->memctx, (HASHTABLE_SIZE_T) slots_size ); - HASHTABLE_ASSERT( table->slots ); - HASHTABLE_MEMSET( table->slots, 0, (HASHTABLE_SIZE_T) slots_size ); - table->item_capacity = (int) hashtable_internal_pow2ceil( (HASHTABLE_U32) initial_capacity ); - table->items_key = (HASHTABLE_U64*) HASHTABLE_MALLOC( table->memctx, - table->item_capacity * ( sizeof( *table->items_key ) + sizeof( *table->items_slot ) + table->item_size ) + table->item_size ); - HASHTABLE_ASSERT( table->items_key ); - table->items_slot = (int*)( table->items_key + table->item_capacity ); - table->items_data = (void*)( table->items_slot + table->item_capacity ); - table->swap_temp = (void*)( ( (uintptr_t) table->items_data ) + table->item_size * table->item_capacity ); - } - - -void hashtable_term( hashtable_t* table ) - { - HASHTABLE_FREE( table->memctx, table->items_key ); - HASHTABLE_FREE( table->memctx, table->slots ); - } - - -// from https://gist.github.com/badboy/6267743 -static HASHTABLE_U32 hashtable_internal_calculate_hash( HASHTABLE_U64 key ) - { - key = ( ~key ) + ( key << 18 ); - key = key ^ ( key >> 31 ); - key = key * 21; - key = key ^ ( key >> 11 ); - key = key + ( key << 6 ); - key = key ^ ( key >> 22 ); - HASHTABLE_ASSERT( key ); - return (HASHTABLE_U32) key; - } - - -static int hashtable_internal_find_slot( hashtable_t const* table, HASHTABLE_U64 key ) - { - int const slot_mask = table->slot_capacity - 1; - HASHTABLE_U32 const hash = hashtable_internal_calculate_hash( key ); - - int const base_slot = (int)( hash & (HASHTABLE_U32)slot_mask ); - int base_count = table->slots[ base_slot ].base_count; - int slot = base_slot; - - while( base_count > 0 ) - { - HASHTABLE_U32 slot_hash = table->slots[ slot ].key_hash; - if( slot_hash ) - { - int slot_base = (int)( slot_hash & (HASHTABLE_U32)slot_mask ); - if( slot_base == base_slot ) - { - HASHTABLE_ASSERT( base_count > 0 ); - --base_count; - if( slot_hash == hash && table->items_key[ table->slots[ slot ].item_index ] == key ) - return slot; - } - } - slot = ( slot + 1 ) & slot_mask; - } - - return -1; - } - - -static void hashtable_internal_expand_slots( hashtable_t* table ) - { - int const old_capacity = table->slot_capacity; - struct hashtable_internal_slot_t* old_slots = table->slots; - - table->slot_capacity *= 2; - int const slot_mask = table->slot_capacity - 1; - - int const size = (int)( table->slot_capacity * sizeof( *table->slots ) ); - table->slots = (struct hashtable_internal_slot_t*) HASHTABLE_MALLOC( table->memctx, (HASHTABLE_SIZE_T) size ); - HASHTABLE_ASSERT( table->slots ); - HASHTABLE_MEMSET( table->slots, 0, (HASHTABLE_SIZE_T) size ); - - for( int i = 0; i < old_capacity; ++i ) - { - HASHTABLE_U32 const hash = old_slots[ i ].key_hash; - if( hash ) - { - int const base_slot = (int)( hash & (HASHTABLE_U32)slot_mask ); - int slot = base_slot; - while( table->slots[ slot ].key_hash ) - slot = ( slot + 1 ) & slot_mask; - table->slots[ slot ].key_hash = hash; - int item_index = old_slots[ i ].item_index; - table->slots[ slot ].item_index = item_index; - table->items_slot[ item_index ] = slot; - ++table->slots[ base_slot ].base_count; - } - } - - HASHTABLE_FREE( table->memctx, old_slots ); - } - - -static void hashtable_internal_expand_items( hashtable_t* table ) - { - table->item_capacity *= 2; - HASHTABLE_U64* const new_items_key = (HASHTABLE_U64*) HASHTABLE_MALLOC( table->memctx, - table->item_capacity * ( sizeof( *table->items_key ) + sizeof( *table->items_slot ) + table->item_size ) + table->item_size); - HASHTABLE_ASSERT( new_items_key ); - - int* const new_items_slot = (int*)( new_items_key + table->item_capacity ); - void* const new_items_data = (void*)( new_items_slot + table->item_capacity ); - void* const new_swap_temp = (void*)( ( (uintptr_t) new_items_data ) + table->item_size * table->item_capacity ); - - HASHTABLE_MEMCPY( new_items_key, table->items_key, table->count * sizeof( *table->items_key ) ); - HASHTABLE_MEMCPY( new_items_slot, table->items_slot, table->count * sizeof( *table->items_key ) ); - HASHTABLE_MEMCPY( new_items_data, table->items_data, (HASHTABLE_SIZE_T) table->count * table->item_size ); - - HASHTABLE_FREE( table->memctx, table->items_key ); - - table->items_key = new_items_key; - table->items_slot = new_items_slot; - table->items_data = new_items_data; - table->swap_temp = new_swap_temp; - } - - -void* hashtable_insert( hashtable_t* table, HASHTABLE_U64 key, void const* item ) - { - HASHTABLE_ASSERT( hashtable_internal_find_slot( table, key ) < 0 ); - - if( table->count >= ( table->slot_capacity - table->slot_capacity / 3 ) ) - hashtable_internal_expand_slots( table ); - - int const slot_mask = table->slot_capacity - 1; - HASHTABLE_U32 const hash = hashtable_internal_calculate_hash( key ); - - int const base_slot = (int)( hash & (HASHTABLE_U32)slot_mask ); - int base_count = table->slots[ base_slot ].base_count; - int slot = base_slot; - int first_free = slot; - while( base_count ) - { - HASHTABLE_U32 const slot_hash = table->slots[ slot ].key_hash; - if( slot_hash == 0 && table->slots[ first_free ].key_hash != 0 ) first_free = slot; - int slot_base = (int)( slot_hash & (HASHTABLE_U32)slot_mask ); - if( slot_base == base_slot ) - --base_count; - slot = ( slot + 1 ) & slot_mask; - } - - slot = first_free; - while( table->slots[ slot ].key_hash ) - slot = ( slot + 1 ) & slot_mask; - - if( table->count >= table->item_capacity ) - hashtable_internal_expand_items( table ); - - HASHTABLE_ASSERT( !table->slots[ slot ].key_hash && ( hash & (HASHTABLE_U32) slot_mask ) == (HASHTABLE_U32) base_slot ); - HASHTABLE_ASSERT( hash ); - table->slots[ slot ].key_hash = hash; - table->slots[ slot ].item_index = table->count; - ++table->slots[ base_slot ].base_count; - - - void* dest_item = (void*)( ( (uintptr_t) table->items_data ) + table->count * table->item_size ); - memcpy( dest_item, item, (HASHTABLE_SIZE_T) table->item_size ); - table->items_key[ table->count ] = key; - table->items_slot[ table->count ] = slot; - ++table->count; - return dest_item; - } - - -void hashtable_remove( hashtable_t* table, HASHTABLE_U64 key ) - { - int const slot = hashtable_internal_find_slot( table, key ); - HASHTABLE_ASSERT( slot >= 0 ); - - int const slot_mask = table->slot_capacity - 1; - HASHTABLE_U32 const hash = table->slots[ slot ].key_hash; - int const base_slot = (int)( hash & (HASHTABLE_U32) slot_mask ); - HASHTABLE_ASSERT( hash ); - --table->slots[ base_slot ].base_count; - table->slots[ slot ].key_hash = 0; - - int index = table->slots[ slot ].item_index; - int last_index = table->count - 1; - if( index != last_index ) - { - table->items_key[ index ] = table->items_key[ last_index ]; - table->items_slot[ index ] = table->items_slot[ last_index ]; - void* dst_item = (void*)( ( (uintptr_t) table->items_data ) + index * table->item_size ); - void* src_item = (void*)( ( (uintptr_t) table->items_data ) + last_index * table->item_size ); - HASHTABLE_MEMCPY( dst_item, src_item, (HASHTABLE_SIZE_T) table->item_size ); - table->slots[ table->items_slot[ last_index ] ].item_index = index; - } - --table->count; - } - - -void hashtable_clear( hashtable_t* table ) - { - table->count = 0; - HASHTABLE_MEMSET( table->slots, 0, table->slot_capacity * sizeof( *table->slots ) ); - } - - -void* hashtable_find( hashtable_t const* table, HASHTABLE_U64 key ) - { - int const slot = hashtable_internal_find_slot( table, key ); - if( slot < 0 ) return 0; - - int const index = table->slots[ slot ].item_index; - void* const item = (void*)( ( (uintptr_t) table->items_data ) + index * table->item_size ); - return item; - } - - -int hashtable_count( hashtable_t const* table ) - { - return table->count; - } - - -void* hashtable_items( hashtable_t const* table ) - { - return table->items_data; - } - - -HASHTABLE_U64 const* hashtable_keys( hashtable_t const* table ) - { - return table->items_key; - } - - -void hashtable_swap( hashtable_t* table, int index_a, int index_b ) - { - if( index_a < 0 || index_a >= table->count || index_b < 0 || index_b >= table->count ) return; - - int slot_a = table->items_slot[ index_a ]; - int slot_b = table->items_slot[ index_b ]; - - table->items_slot[ index_a ] = slot_b; - table->items_slot[ index_b ] = slot_a; +// spritebatch_map implementation - HASHTABLE_U64 temp_key = table->items_key[ index_a ]; - table->items_key[ index_a ] = table->items_key[ index_b ]; - table->items_key[ index_b ] = temp_key; - - void* item_a = (void*)( ( (uintptr_t) table->items_data ) + index_a * table->item_size ); - void* item_b = (void*)( ( (uintptr_t) table->items_data ) + index_b * table->item_size ); - HASHTABLE_MEMCPY( table->swap_temp, item_a, table->item_size ); - HASHTABLE_MEMCPY( item_a, item_b, table->item_size ); - HASHTABLE_MEMCPY( item_b, table->swap_temp, table->item_size ); +static unsigned spritebatch_map_pow2ceil(unsigned v) +{ + --v; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + ++v; + v += (v == 0); + return v; +} - table->slots[ slot_a ].item_index = index_b; - table->slots[ slot_b ].item_index = index_a; - } +static unsigned spritebatch_map_hash(SPRITEBATCH_U64 key) +{ + key = (~key) + (key << 18); + key = key ^ (key >> 31); + key = key * 21; + key = key ^ (key >> 11); + key = key + (key << 6); + key = key ^ (key >> 22); + SPRITEBATCH_ASSERT(key); + return (unsigned)key; +} +static void spritebatch_map_init(spritebatch_map_t* map, int item_size, int initial_capacity, void* mem_ctx) +{ + initial_capacity = (int)spritebatch_map_pow2ceil(initial_capacity >= 0 ? (unsigned)initial_capacity : 32U); + map->mem_ctx = mem_ctx; + map->count = 0; + map->item_size = item_size; + map->slot_capacity = (int)spritebatch_map_pow2ceil((unsigned)(initial_capacity + initial_capacity / 2)); + int slots_size = (int)(map->slot_capacity * sizeof(*map->slots)); + map->slots = (spritebatch_map_slot_t*)SPRITEBATCH_MALLOC(slots_size, mem_ctx); + SPRITEBATCH_ASSERT(map->slots); + SPRITEBATCH_MEMSET(map->slots, 0, slots_size); + map->item_capacity = (int)spritebatch_map_pow2ceil((unsigned)initial_capacity); + map->keys = (SPRITEBATCH_U64*)SPRITEBATCH_MALLOC(map->item_capacity * (sizeof(*map->keys) + sizeof(*map->item_slots) + map->item_size), mem_ctx); + SPRITEBATCH_ASSERT(map->keys); + map->item_slots = (int*)(map->keys + map->item_capacity); + map->items = (void*)(map->item_slots + map->item_capacity); +} -#endif /* HASHTABLE_IMPLEMENTATION */ -#endif // HASHTABLE_IMPLEMENTATION_ONCE -/* +static void spritebatch_map_term(spritebatch_map_t* map) +{ + SPRITEBATCH_FREE(map->keys, map->mem_ctx); + SPRITEBATCH_FREE(map->slots, map->mem_ctx); +} -contributors: - Randy Gaul (hashtable_clear, hashtable_swap ) +static int spritebatch_map_find_slot(spritebatch_map_t const* map, SPRITEBATCH_U64 key) +{ + int slot_mask = map->slot_capacity - 1; + unsigned hash = spritebatch_map_hash(key); + int base_slot = (int)(hash & (unsigned)slot_mask); + int base_count = map->slots[base_slot].base_count; + int slot = base_slot; + + while (base_count > 0) { + unsigned slot_hash = map->slots[slot].key_hash; + if (slot_hash) { + int slot_base = (int)(slot_hash & (unsigned)slot_mask); + if (slot_base == base_slot) { + --base_count; + if (slot_hash == hash && map->keys[map->slots[slot].item_index] == key) + return slot; + } + } + slot = (slot + 1) & slot_mask; + } + return -1; +} -revision history: - 1.1 added hashtable_clear, hashtable_swap - 1.0 first released version +static void spritebatch_map_expand_slots(spritebatch_map_t* map) +{ + int old_capacity = map->slot_capacity; + spritebatch_map_slot_t* old_slots = map->slots; + + map->slot_capacity *= 2; + int slot_mask = map->slot_capacity - 1; + int size = (int)(map->slot_capacity * sizeof(*map->slots)); + map->slots = (spritebatch_map_slot_t*)SPRITEBATCH_MALLOC(size, map->mem_ctx); + SPRITEBATCH_ASSERT(map->slots); + SPRITEBATCH_MEMSET(map->slots, 0, size); + + for (int i = 0; i < old_capacity; ++i) { + unsigned hash = old_slots[i].key_hash; + if (hash) { + int base_slot = (int)(hash & (unsigned)slot_mask); + int slot = base_slot; + while (map->slots[slot].key_hash) + slot = (slot + 1) & slot_mask; + map->slots[slot].key_hash = hash; + int item_index = old_slots[i].item_index; + map->slots[slot].item_index = item_index; + map->item_slots[item_index] = slot; + ++map->slots[base_slot].base_count; + } + } + SPRITEBATCH_FREE(old_slots, map->mem_ctx); +} -*/ +static void spritebatch_map_expand_items(spritebatch_map_t* map) +{ + map->item_capacity *= 2; + SPRITEBATCH_U64* new_keys = (SPRITEBATCH_U64*)SPRITEBATCH_MALLOC( + map->item_capacity * (sizeof(*map->keys) + sizeof(*map->item_slots) + map->item_size), map->mem_ctx); + SPRITEBATCH_ASSERT(new_keys); + + int* new_item_slots = (int*)(new_keys + map->item_capacity); + void* new_items = (void*)(new_item_slots + map->item_capacity); + + SPRITEBATCH_MEMCPY(new_keys, map->keys, map->count * sizeof(*map->keys)); + SPRITEBATCH_MEMCPY(new_item_slots, map->item_slots, map->count * sizeof(*map->item_slots)); + SPRITEBATCH_MEMCPY(new_items, map->items, (size_t)map->count * map->item_size); + + SPRITEBATCH_FREE(map->keys, map->mem_ctx); + map->keys = new_keys; + map->item_slots = new_item_slots; + map->items = new_items; +} -/* ------------------------------------------------------------------------------- +static void* spritebatch_map_insert(spritebatch_map_t* map, SPRITEBATCH_U64 key, void const* item) +{ + SPRITEBATCH_ASSERT(spritebatch_map_find_slot(map, key) < 0); + + if (map->count >= (map->slot_capacity - map->slot_capacity / 3)) + spritebatch_map_expand_slots(map); + + int slot_mask = map->slot_capacity - 1; + unsigned hash = spritebatch_map_hash(key); + int base_slot = (int)(hash & (unsigned)slot_mask); + int base_count = map->slots[base_slot].base_count; + int slot = base_slot; + int first_free = slot; + + while (base_count) { + unsigned slot_hash = map->slots[slot].key_hash; + if (slot_hash == 0 && map->slots[first_free].key_hash != 0) first_free = slot; + int slot_base = (int)(slot_hash & (unsigned)slot_mask); + if (slot_base == base_slot) --base_count; + slot = (slot + 1) & slot_mask; + } -This software is available under 2 licenses - you may choose the one you like. + slot = first_free; + while (map->slots[slot].key_hash) + slot = (slot + 1) & slot_mask; ------------------------------------------------------------------------------- + if (map->count >= map->item_capacity) + spritebatch_map_expand_items(map); -ALTERNATIVE A - MIT License + map->slots[slot].key_hash = hash; + map->slots[slot].item_index = map->count; + ++map->slots[base_slot].base_count; -Copyright (c) 2015 Mattias Gustavsson + void* dest = (void*)((uintptr_t)map->items + map->count * map->item_size); + SPRITEBATCH_MEMCPY(dest, item, map->item_size); + map->keys[map->count] = key; + map->item_slots[map->count] = slot; + ++map->count; + return dest; +} -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: +static void spritebatch_map_remove(spritebatch_map_t* map, SPRITEBATCH_U64 key) +{ + int slot = spritebatch_map_find_slot(map, key); + SPRITEBATCH_ASSERT(slot >= 0); + + int slot_mask = map->slot_capacity - 1; + unsigned hash = map->slots[slot].key_hash; + int base_slot = (int)(hash & (unsigned)slot_mask); + --map->slots[base_slot].base_count; + map->slots[slot].key_hash = 0; + + int index = map->slots[slot].item_index; + int last_index = map->count - 1; + if (index != last_index) { + map->keys[index] = map->keys[last_index]; + map->item_slots[index] = map->item_slots[last_index]; + void* dst = (void*)((uintptr_t)map->items + index * map->item_size); + void* src = (void*)((uintptr_t)map->items + last_index * map->item_size); + SPRITEBATCH_MEMCPY(dst, src, map->item_size); + map->slots[map->item_slots[last_index]].item_index = index; + } + --map->count; +} -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +static void spritebatch_map_clear(spritebatch_map_t* map) +{ + map->count = 0; + SPRITEBATCH_MEMSET(map->slots, 0, map->slot_capacity * sizeof(*map->slots)); +} -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. +static void* spritebatch_map_find(spritebatch_map_t const* map, SPRITEBATCH_U64 key) +{ + int slot = spritebatch_map_find_slot(map, key); + if (slot < 0) return 0; + int index = map->slots[slot].item_index; + return (void*)((uintptr_t)map->items + index * map->item_size); +} ------------------------------------------------------------------------------- +static int spritebatch_map_count(spritebatch_map_t const* map) +{ + return map->count; +} -ALTERNATIVE B - Public Domain (www.unlicense.org) +static void* spritebatch_map_items(spritebatch_map_t const* map) +{ + return map->items; +} -This is free and unencumbered software released into the public domain. +static void spritebatch_map_swap(spritebatch_map_t* map, int index_a, int index_b) +{ + if (index_a < 0 || index_a >= map->count || index_b < 0 || index_b >= map->count) return; -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. + int slot_a = map->item_slots[index_a]; + int slot_b = map->item_slots[index_b]; + map->item_slots[index_a] = slot_b; + map->item_slots[index_b] = slot_a; -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. + SPRITEBATCH_U64 temp_key = map->keys[index_a]; + map->keys[index_a] = map->keys[index_b]; + map->keys[index_b] = temp_key; -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. + void* item_a = (void*)((uintptr_t)map->items + index_a * map->item_size); + void* item_b = (void*)((uintptr_t)map->items + index_b * map->item_size); ------------------------------------------------------------------------------- -*/ + // Use stack buffer for swap (item_size is typically small) + char swap_temp[256]; + SPRITEBATCH_ASSERT(map->item_size <= (int)sizeof(swap_temp)); + SPRITEBATCH_MEMCPY(swap_temp, item_a, map->item_size); + SPRITEBATCH_MEMCPY(item_a, item_b, map->item_size); + SPRITEBATCH_MEMCPY(item_b, swap_temp, map->item_size); -// end of hashtable.h + map->slots[slot_a].item_index = index_b; + map->slots[slot_b].item_index = index_a; +} #include @@ -1093,9 +866,9 @@ int spritebatch_init(spritebatch_t* sb, spritebatch_config_t* config, void* udat sb->pixel_buffer = SPRITEBATCH_MALLOC(sb->pixel_buffer_size * sb->pixel_stride, sb->mem_ctx); // setup tables - hashtable_init(&sb->sprites_to_lonely_textures, sizeof(spritebatch_internal_lonely_texture_t), 1024, sb->mem_ctx); - hashtable_init(&sb->sprites_to_premades, sizeof(spritebatch_internal_premade_sprite_t), 1024 * 10, sb->mem_ctx); - hashtable_init(&sb->sprites_to_atlases, sizeof(spritebatch_internal_atlas_t*), 16, sb->mem_ctx); + spritebatch_map_init(&sb->sprites_to_lonely_textures, sizeof(spritebatch_internal_lonely_texture_t), 1024, sb->mem_ctx); + spritebatch_map_init(&sb->sprites_to_premades, sizeof(spritebatch_internal_premade_sprite_t), 1024 * 10, sb->mem_ctx); + spritebatch_map_init(&sb->sprites_to_atlases, sizeof(spritebatch_internal_atlas_t*), 16, sb->mem_ctx); sb->atlases = 0; @@ -1111,10 +884,18 @@ void spritebatch_term(spritebatch_t* sb) SPRITEBATCH_FREE(sb->sprites_scratch, sb->mem_ctx); } SPRITEBATCH_FREE(sb->key_buffer, sb->mem_ctx); - SPRITEBATCH_FREE(sb->pixel_buffer, ctx->mem_ctx); - hashtable_term(&sb->sprites_to_lonely_textures); - hashtable_term(&sb->sprites_to_premades); - hashtable_term(&sb->sprites_to_atlases); + SPRITEBATCH_FREE(sb->pixel_buffer, sb->mem_ctx); + { + int count = spritebatch_map_count(&sb->sprites_to_lonely_textures); + spritebatch_internal_lonely_texture_t* lonely = (spritebatch_internal_lonely_texture_t*)spritebatch_map_items(&sb->sprites_to_lonely_textures); + for (int i = 0; i < count; ++i) { + if (lonely[i].texture_id != ~0) + sb->delete_texture_callback(lonely[i].texture_id, sb->udata); + } + } + spritebatch_map_term(&sb->sprites_to_lonely_textures); + spritebatch_map_term(&sb->sprites_to_premades); + spritebatch_map_term(&sb->sprites_to_atlases); if (sb->atlases) { @@ -1122,7 +903,8 @@ void spritebatch_term(spritebatch_t* sb) spritebatch_internal_atlas_t* sentinel = sb->atlases; do { - hashtable_term(&atlas->sprites_to_textures); + spritebatch_map_term(&atlas->sprites_to_textures); + sb->delete_texture_callback(atlas->texture_id, sb->udata); spritebatch_internal_atlas_t* next = atlas->next; SPRITEBATCH_FREE(atlas, sb->mem_ctx); atlas = next; @@ -1225,11 +1007,11 @@ void spritebatch_register_premade_atlas(spritebatch_t* sb, SPRITEBATCH_U64 textu premade.maxx = sprites[i].maxx; premade.maxy = sprites[i].maxy; premade.texture_id = texture_id; - void* find = hashtable_find(&sb->sprites_to_premades, sprites[i].image_id); + void* find = spritebatch_map_find(&sb->sprites_to_premades, sprites[i].image_id); if (find) { SPRITEBATCH_MEMCPY(find, &premade, sizeof(premade)); } else { - hashtable_insert(&sb->sprites_to_premades, sprites[i].image_id, &premade); + spritebatch_map_insert(&sb->sprites_to_premades, sprites[i].image_id, &premade); } } } @@ -1241,7 +1023,7 @@ void spritebatch_prefetch(spritebatch_t* sb, SPRITEBATCH_U64 image_id, int w, in { spritebatch_internal_premade_sprite_t* premade = spritebatch_internal_premade_sprite(sb, image_id, NULL); if(!premade) { - void* atlas_ptr = hashtable_find(&sb->sprites_to_atlases, image_id); + void* atlas_ptr = spritebatch_map_find(&sb->sprites_to_atlases, image_id); if (!atlas_ptr) spritebatch_internal_lonely_sprite(sb, image_id, w, h, NULL, 0); } } @@ -1251,15 +1033,15 @@ spritebatch_sprite_t spritebatch_fetch(spritebatch_t* sb, SPRITEBATCH_U64 image_ spritebatch_sprite_t s; SPRITEBATCH_MEMSET(&s, 0, sizeof(s)); - spritebatch_internal_premade_sprite_t* premade = (spritebatch_internal_premade_sprite_t*)hashtable_find(&sb->sprites_to_premades, image_id); + spritebatch_internal_premade_sprite_t* premade = (spritebatch_internal_premade_sprite_t*)spritebatch_map_find(&sb->sprites_to_premades, image_id); if(!premade) { - void* atlas_ptr = hashtable_find(&sb->sprites_to_atlases, image_id); + void* atlas_ptr = spritebatch_map_find(&sb->sprites_to_atlases, image_id); if (atlas_ptr) { spritebatch_internal_atlas_t* atlas = *(spritebatch_internal_atlas_t**)atlas_ptr; s.texture_id = atlas->texture_id; - spritebatch_internal_texture_t* tex = (spritebatch_internal_texture_t*)hashtable_find(&atlas->sprites_to_textures, image_id); + spritebatch_internal_texture_t* tex = (spritebatch_internal_texture_t*)spritebatch_map_find(&atlas->sprites_to_textures, image_id); if (tex) { s.maxx = tex->maxx; s.maxy = tex->maxy; @@ -1324,9 +1106,9 @@ static inline void spritebatch_internal_get_pixels(spritebatch_t* sb, SPRITEBATC int size = sb->atlas_use_border_pixels ? sb->pixel_stride * (w + 2) * (h + 2) : sb->pixel_stride * w * h; if (size > sb->pixel_buffer_size) { - SPRITEBATCH_FREE(sb->pixel_buffer, ctx->mem_ctx); + SPRITEBATCH_FREE(sb->pixel_buffer, sb->mem_ctx); sb->pixel_buffer_size = size; - sb->pixel_buffer = SPRITEBATCH_MALLOC(sb->pixel_buffer_size, ctx->mem_ctx); + sb->pixel_buffer = SPRITEBATCH_MALLOC(sb->pixel_buffer_size, sb->mem_ctx); if (!sb->pixel_buffer) return; } @@ -1382,12 +1164,12 @@ spritebatch_internal_lonely_texture_t* spritebatch_internal_lonelybuffer_push(sp texture.h = h; texture.image_id = image_id; texture.texture_id = make_tex ? spritebatch_internal_generate_texture_handle(sb, image_id, w, h) : ~0; - return (spritebatch_internal_lonely_texture_t*)hashtable_insert(&sb->sprites_to_lonely_textures, image_id, &texture); + return (spritebatch_internal_lonely_texture_t*)spritebatch_map_insert(&sb->sprites_to_lonely_textures, image_id, &texture); } int spritebatch_internal_lonely_sprite(spritebatch_t* sb, SPRITEBATCH_U64 image_id, int w, int h, spritebatch_sprite_t* sprite_out, int skip_missing_textures) { - spritebatch_internal_lonely_texture_t* tex = (spritebatch_internal_lonely_texture_t*)hashtable_find(&sb->sprites_to_lonely_textures, image_id); + spritebatch_internal_lonely_texture_t* tex = (spritebatch_internal_lonely_texture_t*)spritebatch_map_find(&sb->sprites_to_lonely_textures, image_id); if (skip_missing_textures) { @@ -1420,7 +1202,7 @@ int spritebatch_internal_lonely_sprite(spritebatch_t* sb, SPRITEBATCH_U64 image_ spritebatch_internal_premade_sprite_t* spritebatch_internal_premade_sprite(spritebatch_t* sb, SPRITEBATCH_U64 image_id, spritebatch_sprite_t* sprite_out) { - spritebatch_internal_premade_sprite_t* tex = (spritebatch_internal_premade_sprite_t*)hashtable_find(&sb->sprites_to_premades, image_id); + spritebatch_internal_premade_sprite_t* tex = (spritebatch_internal_premade_sprite_t*)spritebatch_map_find(&sb->sprites_to_premades, image_id); if (!tex) return NULL; SPRITEBATCH_ASSERT(tex->texture_id != ~0); if (sprite_out) { @@ -1452,13 +1234,13 @@ int spritebatch_internal_push_sprite(spritebatch_t* sb, spritebatch_internal_spr if(!premade) { - void* atlas_ptr = hashtable_find(&sb->sprites_to_atlases, s->image_id); + void* atlas_ptr = spritebatch_map_find(&sb->sprites_to_atlases, s->image_id); if (atlas_ptr) { spritebatch_internal_atlas_t* atlas = *(spritebatch_internal_atlas_t**)atlas_ptr; sprite.texture_id = atlas->texture_id; - spritebatch_internal_texture_t* tex = (spritebatch_internal_texture_t*)hashtable_find(&atlas->sprites_to_textures, s->image_id); + spritebatch_internal_texture_t* tex = (spritebatch_internal_texture_t*)spritebatch_map_find(&atlas->sprites_to_textures, s->image_id); SPRITEBATCH_ASSERT(tex); tex->timestamp = 0; sprite.w = tex->w; @@ -1532,30 +1314,29 @@ void spritebatch_tick(spritebatch_t* sb) spritebatch_internal_atlas_t* sentinel = atlas; do { - int texture_count = hashtable_count(&atlas->sprites_to_textures); - spritebatch_internal_texture_t* textures = (spritebatch_internal_texture_t*)hashtable_items(&atlas->sprites_to_textures); + int texture_count = spritebatch_map_count(&atlas->sprites_to_textures); + spritebatch_internal_texture_t* textures = (spritebatch_internal_texture_t*)spritebatch_map_items(&atlas->sprites_to_textures); for (int i = 0; i < texture_count; ++i) textures[i].timestamp += 1; atlas = atlas->next; } while (atlas != sentinel); } - int texture_count = hashtable_count(&sb->sprites_to_lonely_textures); - spritebatch_internal_lonely_texture_t* lonely_textures = (spritebatch_internal_lonely_texture_t*)hashtable_items(&sb->sprites_to_lonely_textures); + int texture_count = spritebatch_map_count(&sb->sprites_to_lonely_textures); + spritebatch_internal_lonely_texture_t* lonely_textures = (spritebatch_internal_lonely_texture_t*)spritebatch_map_items(&sb->sprites_to_lonely_textures); for (int i = 0; i < texture_count; ++i) lonely_textures[i].timestamp += 1; } int spritebatch_flush(spritebatch_t* sb) { - // process input buffer, make any necessary lonely textures // convert user sprites to internal format // lookup uv coordinates spritebatch_internal_process_input(sb, 0); // patchup any missing lonely textures that may have come from atlases decaying and whatnot - int texture_count = hashtable_count(&sb->sprites_to_lonely_textures); - spritebatch_internal_lonely_texture_t* lonely_textures = (spritebatch_internal_lonely_texture_t*)hashtable_items(&sb->sprites_to_lonely_textures); + int texture_count = spritebatch_map_count(&sb->sprites_to_lonely_textures); + spritebatch_internal_lonely_texture_t* lonely_textures = (spritebatch_internal_lonely_texture_t*)spritebatch_map_items(&sb->sprites_to_lonely_textures); for (int i = 0; i < texture_count; ++i) { spritebatch_internal_lonely_texture_t* lonely = lonely_textures + i; @@ -1597,10 +1378,10 @@ int spritebatch_flush(spritebatch_t* sb) if (batch_count) { int w, h; - spritebatch_internal_premade_sprite_t* premade = (spritebatch_internal_premade_sprite_t*)hashtable_find(&sb->sprites_to_premades, image_id); + spritebatch_internal_premade_sprite_t* premade = (spritebatch_internal_premade_sprite_t*)spritebatch_map_find(&sb->sprites_to_premades, image_id); if (!premade) { - void* atlas_ptr = hashtable_find(&sb->sprites_to_atlases, image_id); + void* atlas_ptr = spritebatch_map_find(&sb->sprites_to_atlases, image_id); if (atlas_ptr) { @@ -1610,7 +1391,7 @@ int spritebatch_flush(spritebatch_t* sb) else { - spritebatch_internal_lonely_texture_t* tex = (spritebatch_internal_lonely_texture_t*)hashtable_find(&sb->sprites_to_lonely_textures, image_id); + spritebatch_internal_lonely_texture_t* tex = (spritebatch_internal_lonely_texture_t*)spritebatch_map_find(&sb->sprites_to_lonely_textures, image_id); SPRITEBATCH_ASSERT(tex); w = tex->w; h = tex->h; @@ -1904,7 +1685,7 @@ void spritebatch_make_atlas(spritebatch_t* sb, spritebatch_internal_atlas_t* atl } } - hashtable_init(&atlas_out->sprites_to_textures, sizeof(spritebatch_internal_texture_t), img_count, sb->mem_ctx); + spritebatch_map_init(&atlas_out->sprites_to_textures, sizeof(spritebatch_internal_texture_t), img_count, sb->mem_ctx); atlas_out->texture_id = sb->generate_texture_callback(atlas_pixels, atlas_width, atlas_height, sb->udata); iw = 1.0f / (float)(atlas_width); @@ -1948,7 +1729,7 @@ void spritebatch_make_atlas(spritebatch_t* sb, spritebatch_internal_atlas_t* atl SPRITEBATCH_ASSERT(!(min_y < 0)); SPRITEBATCH_ASSERT(!(max_y < 0)); texture.image_id = imgs[img->img_index].image_id; - hashtable_insert(&atlas_out->sprites_to_textures, texture.image_id, &texture); + spritebatch_map_insert(&atlas_out->sprites_to_textures, texture.image_id, &texture); } } @@ -1974,7 +1755,7 @@ static int spritebatch_internal_lonely_pred(spritebatch_internal_lonely_texture_ return a->timestamp < b->timestamp; } -static void spritebatch_internal_qsort_lonely(hashtable_t* lonely_table, spritebatch_internal_lonely_texture_t* items, int count) +static void spritebatch_internal_qsort_lonely(spritebatch_map_t* lonely_table, spritebatch_internal_lonely_texture_t* items, int count) { if (count <= 1) return; @@ -1984,12 +1765,12 @@ static void spritebatch_internal_qsort_lonely(hashtable_t* lonely_table, spriteb { if (spritebatch_internal_lonely_pred(items + i, &pivot)) { - hashtable_swap(lonely_table, i, low); + spritebatch_map_swap(lonely_table, i, low); low++; } } - hashtable_swap(lonely_table, low, count - 1); + spritebatch_map_swap(lonely_table, low, count - 1); spritebatch_internal_qsort_lonely(lonely_table, items, low); spritebatch_internal_qsort_lonely(lonely_table, items + low + 1, count - 1 - low); } @@ -2001,17 +1782,17 @@ int spritebatch_internal_buffer_key(spritebatch_t* sb, SPRITEBATCH_U64 key) return 0; } -void spritebatch_internal_remove_table_entries(spritebatch_t* sb, hashtable_t* table) +void spritebatch_internal_remove_table_entries(spritebatch_t* sb, spritebatch_map_t* table) { - for (int i = 0; i < sb->key_buffer_count; ++i) hashtable_remove(table, sb->key_buffer[i]); + for (int i = 0; i < sb->key_buffer_count; ++i) spritebatch_map_remove(table, sb->key_buffer[i]); sb->key_buffer_count = 0; } void spritebatch_internal_flush_atlas(spritebatch_t* sb, spritebatch_internal_atlas_t* atlas, spritebatch_internal_atlas_t** sentinel, spritebatch_internal_atlas_t** next) { int ticks_to_decay_texture = sb->ticks_to_decay_texture; - int texture_count = hashtable_count(&atlas->sprites_to_textures); - spritebatch_internal_texture_t* textures = (spritebatch_internal_texture_t*)hashtable_items(&atlas->sprites_to_textures); + int texture_count = spritebatch_map_count(&atlas->sprites_to_textures); + spritebatch_internal_texture_t* textures = (spritebatch_internal_texture_t*)spritebatch_map_items(&atlas->sprites_to_textures); for (int i = 0; i < texture_count; ++i) { @@ -2028,7 +1809,7 @@ void spritebatch_internal_flush_atlas(spritebatch_t* sb, spritebatch_internal_at spritebatch_internal_lonely_texture_t* lonely_texture = spritebatch_internal_lonelybuffer_push(sb, atlas_texture->image_id, w, h, 0); lonely_texture->timestamp = atlas_texture->timestamp; } - hashtable_remove(&sb->sprites_to_atlases, atlas_texture->image_id); + spritebatch_map_remove(&sb->sprites_to_atlases, atlas_texture->image_id); } if (sb->atlases == atlas) @@ -2057,20 +1838,20 @@ void spritebatch_internal_flush_atlas(spritebatch_t* sb, spritebatch_internal_at atlas->next->prev = atlas->prev; atlas->prev->next = atlas->next; - hashtable_term(&atlas->sprites_to_textures); + spritebatch_map_term(&atlas->sprites_to_textures); sb->delete_texture_callback(atlas->texture_id, sb->udata); SPRITEBATCH_FREE(atlas, sb->mem_ctx); } void spritebatch_invalidate(spritebatch_t* sb, SPRITEBATCH_U64 image_id) { - void* atlas_ptr = hashtable_find(&sb->sprites_to_atlases, image_id); + void* atlas_ptr = spritebatch_map_find(&sb->sprites_to_atlases, image_id); if (atlas_ptr) { spritebatch_internal_atlas_t* atlas = *(spritebatch_internal_atlas_t**)atlas_ptr; spritebatch_internal_flush_atlas(sb, atlas, 0, 0); } - if (hashtable_find(&sb->sprites_to_lonely_textures, image_id)) { - hashtable_remove(&sb->sprites_to_lonely_textures, image_id); + if (spritebatch_map_find(&sb->sprites_to_lonely_textures, image_id)) { + spritebatch_map_remove(&sb->sprites_to_lonely_textures, image_id); } } @@ -2104,8 +1885,8 @@ int spritebatch_defrag(spritebatch_t* sb) do { spritebatch_internal_atlas_t* next = atlas->next; - int texture_count = hashtable_count(&atlas->sprites_to_textures); - spritebatch_internal_texture_t* textures = (spritebatch_internal_texture_t*)hashtable_items(&atlas->sprites_to_textures); + int texture_count = spritebatch_map_count(&atlas->sprites_to_textures); + spritebatch_internal_texture_t* textures = (spritebatch_internal_texture_t*)spritebatch_map_items(&atlas->sprites_to_textures); int decayed_texture_count = 0; for (int i = 0; i < texture_count; ++i) if (textures[i].timestamp >= ticks_to_decay_texture) decayed_texture_count++; @@ -2161,8 +1942,8 @@ int spritebatch_defrag(spritebatch_t* sb) // remove decayed textures from the lonely buffer int lonely_buffer_count_till_decay = sb->lonely_buffer_count_till_decay; - int lonely_count = hashtable_count(&sb->sprites_to_lonely_textures); - spritebatch_internal_lonely_texture_t* lonely_textures = (spritebatch_internal_lonely_texture_t*)hashtable_items(&sb->sprites_to_lonely_textures); + int lonely_count = spritebatch_map_count(&sb->sprites_to_lonely_textures); + spritebatch_internal_lonely_texture_t* lonely_textures = (spritebatch_internal_lonely_texture_t*)spritebatch_map_items(&sb->sprites_to_lonely_textures); if (lonely_count >= lonely_buffer_count_till_decay) { spritebatch_internal_qsort_lonely(&sb->sprites_to_lonely_textures, lonely_textures, lonely_count); @@ -2182,12 +1963,12 @@ int spritebatch_defrag(spritebatch_t* sb) } spritebatch_internal_remove_table_entries(sb, &sb->sprites_to_lonely_textures); lonely_count -= lonely_count - index; - SPRITEBATCH_ASSERT(lonely_count == hashtable_count(&sb->sprites_to_lonely_textures)); + SPRITEBATCH_ASSERT(lonely_count == spritebatch_map_count(&sb->sprites_to_lonely_textures)); } // process input, but don't make textures just yet spritebatch_internal_process_input(sb, 1); - lonely_count = hashtable_count(&sb->sprites_to_lonely_textures); + lonely_count = spritebatch_map_count(&sb->sprites_to_lonely_textures); // while greater than lonely_buffer_count_till_flush elements in lonely buffer // grab lonely_buffer_count_till_flush of them and make an atlas @@ -2214,19 +1995,19 @@ int spritebatch_defrag(spritebatch_t* sb) spritebatch_make_atlas(sb, atlas, lonely_textures, lonely_count); SPRITEBATCH_LOG("making atlas\n"); - int tex_count_in_atlas = hashtable_count(&atlas->sprites_to_textures); + int tex_count_in_atlas = spritebatch_map_count(&atlas->sprites_to_textures); if (tex_count_in_atlas != lonely_count) { int hit_count = 0; for (int i = 0; i < lonely_count; ++i) { SPRITEBATCH_U64 key = lonely_textures[i].image_id; - if (hashtable_find(&atlas->sprites_to_textures, key)) + if (spritebatch_map_find(&atlas->sprites_to_textures, key)) { spritebatch_internal_buffer_key(sb, key); SPRITEBATCH_U64 texture_id = lonely_textures[i].texture_id; if (texture_id != ~0) sb->delete_texture_callback(texture_id, sb->udata); - hashtable_insert(&sb->sprites_to_atlases, key, &atlas); + spritebatch_map_insert(&sb->sprites_to_atlases, key, &atlas); SPRITEBATCH_LOG("removing lonely texture for atlas%s\n", texture_id != ~0 ? "" : " (tex was ~0)" ); } else @@ -2238,7 +2019,7 @@ int spritebatch_defrag(spritebatch_t* sb) } spritebatch_internal_remove_table_entries(sb, &sb->sprites_to_lonely_textures); - lonely_count = hashtable_count(&sb->sprites_to_lonely_textures); + lonely_count = spritebatch_map_count(&sb->sprites_to_lonely_textures); if (!hit_count) { @@ -2256,10 +2037,10 @@ int spritebatch_defrag(spritebatch_t* sb) SPRITEBATCH_U64 key = lonely_textures[i].image_id; SPRITEBATCH_U64 texture_id = lonely_textures[i].texture_id; if (texture_id != ~0) sb->delete_texture_callback(texture_id, sb->udata); - hashtable_insert(&sb->sprites_to_atlases, key, &atlas); + spritebatch_map_insert(&sb->sprites_to_atlases, key, &atlas); SPRITEBATCH_LOG("(fast path) removing lonely texture for atlas%s\n", texture_id != ~0 ? "" : " (tex was ~0)" ); } - hashtable_clear(&sb->sprites_to_lonely_textures); + spritebatch_map_clear(&sb->sprites_to_lonely_textures); lonely_count = 0; break; } @@ -2276,7 +2057,7 @@ int spritebatch_defrag(spritebatch_t* sb) This software is available under 2 licenses - you may choose the one you like. ------------------------------------------------------------------------------ ALTERNATIVE A - zlib license - Copyright (c) 2023 Randy Gaul https://randygaul.github.io/ + Copyright (c) 2026 Randy Gaul https://randygaul.github.io/ 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. diff --git a/libraries/cute/cute_sync.h b/libraries/cute/cute_sync.h index db7da0fa9..b9dcfc0e3 100644 --- a/libraries/cute/cute_sync.h +++ b/libraries/cute/cute_sync.h @@ -3,7 +3,7 @@ Licensing information can be found at the end of the file. ------------------------------------------------------------------------------ - cute_sync.h - v1.01 + cute_sync.h - v1.02 To create implementation (the function definitions) #define CUTE_SYNC_IMPLEMENTATION @@ -12,7 +12,7 @@ SUMMARY - Collection of practical syncronization primitives for Windows/Posix/SDL2. + Collection of practical syncronization primitives for Windows/Posix/SDL3. Here is a list of all supported primitives. @@ -26,7 +26,7 @@ Here are some slides I wrote for those interested in learning prequisite knowledge for utilizing this header: - http://www.randygaul.net/2014/09/24/multi-threading-best-practices-for-gamedev/ + https://randygaul.github.io/2014/09/24/multi-threading-best-practices-for-gamedev/ A good chunk of this code came from Mattias Gustavsson's thread.h header. It really is quite a good header, and worth considering! @@ -35,18 +35,33 @@ PLATFORMS - The current supported platforms are Windows/Posix/SDL. Here are the macros for + The current supported platforms are Windows/Posix/SDL3. Here are the macros for picking each implementation. * CUTE_SYNC_WINDOWS * CUTE_SYNC_POSIX * CUTE_SYNC_SDL + Note: CUTE_SYNC_SDL requires SDL3 (not SDL2). SDL3 must be included before + cute_sync.h when using CUTE_SYNC_IMPLEMENTATION. + REVISION HISTORY 1.0 (05/31/2018) initial release 1.01 (08/25/2019) Windows and pthreads port + 1.02 (02/01/2026) Bug fixes: + - Fixed POSIX cute_atomic_set/cute_atomic_ptr_set zeroing out value + - Fixed Windows cute_atomic_cas/cute_atomic_ptr_cas wrong comparison + - Fixed Windows cute_trylock returning inverted result + - Fixed SDL cute_cv_wait passing wrong pointer + - Fixed Apple semaphore waiting_count not being incremented + - Fixed cute_thread_wait not returning thread exit code (Windows/POSIX) + - Fixed POSIX thread function signature mismatch (int vs void*) + - Fixed threadpool worker blocking after each task instead of draining queue + - Fixed race conditions in cute_threadpool_kick and cute_threadpool_kick_and_wait + - Fixed resource leaks in cute_threadpool_destroy (mutex/semaphore not freed) + - Removed unused sem_mutex field from threadpool */ #if !defined(CUTE_SYNC_H) @@ -64,8 +79,15 @@ typedef int (cute_thread_fn)(void *udata); */ cute_mutex_t cute_mutex_create(); -void cute_lock(cute_mutex_t* mutex); -void cute_unlock(cute_mutex_t* mutex); +/** + * Returns 1 on success, zero otherwise. + */ +int cute_lock(cute_mutex_t* mutex); + +/** + * Returns 1 on success, zero otherwise. + */ +int cute_unlock(cute_mutex_t* mutex); /** * Attempts to lock the mutex without blocking. Returns one if lock was acquired, @@ -132,7 +154,7 @@ cute_thread_t* cute_thread_create(cute_thread_fn func, const char* name, void* u * Useful for certain long-lived threads. * It is invalid to call `cute_thread_wait` on a detached thread. * It is invalid to call `cute_thread_wait` on a thread more than once. - * Please see this link for a longer description: https://wiki.libsdl.org/SDL_DetachThread + * Please see this link for a longer description: https://wiki.libsdl.org/SDL3/SDL_DetachThread */ void cute_thread_detach(cute_thread_t* thread); cute_thread_id_t cute_thread_get_id(cute_thread_t* thread); @@ -310,10 +332,6 @@ struct cute_rw_lock_t #define CUTE_SYNC_IMPLEMENTATION_ONCE #if defined(CUTE_SYNC_SDL) -#include -#include -#include -#include #elif defined(CUTE_SYNC_WINDOWS) #define WIN32_LEAN_AND_MEAN // To use GetThreadId and other methods we must require Windows Vista minimum. @@ -432,7 +450,9 @@ int cute_atomic_get(cute_atomic_int_t* atomic) int cute_atomic_cas(cute_atomic_int_t* atomic, int expected, int value) { - return (int)_InterlockedCompareExchange(&atomic->i, value, expected) == value; + // _InterlockedCompareExchange returns the original value. CAS succeeds when + // the original value equals expected (meaning the swap occurred). + return (int)_InterlockedCompareExchange(&atomic->i, value, expected) == expected; } void* cute_atomic_ptr_set(void** atomic, void* value) @@ -447,7 +467,9 @@ void* cute_atomic_ptr_get(void** atomic) int cute_atomic_ptr_cas(void** atomic, void* expected, void* value) { - return _InterlockedCompareExchangePointer(atomic, value, expected) == value; + // _InterlockedCompareExchangePointer returns the original value. CAS succeeds when + // the original value equals expected (meaning the swap occurred). + return _InterlockedCompareExchangePointer(atomic, value, expected) == expected; } #elif defined(CUTE_SYNC_POSIX) @@ -464,9 +486,14 @@ int cute_atomic_add(cute_atomic_int_t* atomic, int addend) int cute_atomic_set(cute_atomic_int_t* atomic, int value) { - int result = (int)__sync_lock_test_and_set(&atomic->i, value); - __sync_lock_release(&atomic->i); - return result; + // Use a CAS loop to atomically exchange the value. + // __sync_lock_test_and_set has acquire semantics only and __sync_lock_release + // would zero out the value, so we use CAS instead for a proper atomic set. + long old_val; + do { + old_val = atomic->i; + } while (!__sync_bool_compare_and_swap(&atomic->i, old_val, value)); + return (int)old_val; } int cute_atomic_get(cute_atomic_int_t* atomic) @@ -481,9 +508,14 @@ int cute_atomic_cas(cute_atomic_int_t* atomic, int expected, int value) void* cute_atomic_ptr_set(void** atomic, void* value) { - void* result = __sync_lock_test_and_set(atomic, value); - __sync_lock_release(atomic); - return result; + // Use a CAS loop to atomically exchange the value. + // __sync_lock_test_and_set has acquire semantics only and __sync_lock_release + // would zero out the pointer, so we use CAS instead for a proper atomic set. + void* old_val; + do { + old_val = *atomic; + } while (!__sync_bool_compare_and_swap(atomic, old_val, value)); + return old_val; } void* cute_atomic_ptr_get(void** atomic) @@ -507,19 +539,24 @@ cute_mutex_t cute_mutex_create() return mutex; } -void cute_lock(cute_mutex_t* mutex) +int cute_lock(cute_mutex_t* mutex) { - return SDL_LockMutex((SDL_Mutex*)mutex->align); + // SDL3: SDL_LockMutex returns void (always succeeds or aborts). + SDL_LockMutex((SDL_Mutex*)mutex->align); + return 1; } -void cute_unlock(cute_mutex_t* mutex) +int cute_unlock(cute_mutex_t* mutex) { - return SDL_UnlockMutex((SDL_Mutex*)mutex->align); + // SDL3: SDL_UnlockMutex returns void (always succeeds or aborts). + SDL_UnlockMutex((SDL_Mutex*)mutex->align); + return 1; } int cute_trylock(cute_mutex_t* mutex) { - return !SDL_TryLockMutex((SDL_Mutex*)mutex->align); + // SDL3: SDL_TryLockMutex returns bool (true on success). + return SDL_TryLockMutex((SDL_Mutex*)mutex->align) ? 1 : 0; } void cute_mutex_destroy(cute_mutex_t* mutex) @@ -536,19 +573,22 @@ cute_cv_t cute_cv_create() int cute_cv_wake_all(cute_cv_t* cv) { + // SDL3: SDL_BroadcastCondition returns void. SDL_BroadcastCondition((SDL_Condition*)cv->align); return 1; } int cute_cv_wake_one(cute_cv_t* cv) { + // SDL3: SDL_SignalCondition returns void. SDL_SignalCondition((SDL_Condition*)cv->align); return 1; } int cute_cv_wait(cute_cv_t* cv, cute_mutex_t* mutex) { - SDL_WaitCondition((SDL_Condition*)cv, (SDL_Mutex*)mutex->align); + // SDL3: SDL_WaitCondition returns void (always succeeds or aborts). + SDL_WaitCondition((SDL_Condition*)cv->align, (SDL_Mutex*)mutex->align); return 1; } @@ -567,24 +607,27 @@ cute_semaphore_t cute_semaphore_create(int initial_count) int cute_semaphore_post(cute_semaphore_t* semaphore) { + // SDL3: SDL_SignalSemaphore returns void. SDL_SignalSemaphore((SDL_Semaphore*)semaphore->id); return 1; } int cute_semaphore_try(cute_semaphore_t* semaphore) { - return !SDL_TryWaitSemaphore((SDL_Semaphore*)semaphore->id); + // SDL3: SDL_TryWaitSemaphore returns bool (true on success). + return SDL_TryWaitSemaphore((SDL_Semaphore*)semaphore->id) ? 1 : 0; } int cute_semaphore_wait(cute_semaphore_t* semaphore) { + // SDL3: SDL_WaitSemaphore returns void. SDL_WaitSemaphore((SDL_Semaphore*)semaphore->id); return 1; } int cute_semaphore_value(cute_semaphore_t* semaphore) { - return SDL_GetSemaphoreValue((SDL_Semaphore*)semaphore->id); + return (int)SDL_GetSemaphoreValue((SDL_Semaphore*)semaphore->id); } void cute_semaphore_destroy(cute_semaphore_t* semaphore) @@ -644,19 +687,22 @@ cute_mutex_t cute_mutex_create() return mutex; } -void cute_lock(cute_mutex_t* mutex) +int cute_lock(cute_mutex_t* mutex) { EnterCriticalSection((CRITICAL_SECTION*)mutex); + return 1; } -void cute_unlock(cute_mutex_t* mutex) +int cute_unlock(cute_mutex_t* mutex) { LeaveCriticalSection((CRITICAL_SECTION*)mutex); + return 1; } int cute_trylock(cute_mutex_t* mutex) { - return !TryEnterCriticalSection((CRITICAL_SECTION*)mutex); + // TryEnterCriticalSection returns non-zero on success, which matches our API. + return TryEnterCriticalSection((CRITICAL_SECTION*)mutex) != 0; } void cute_mutex_destroy(cute_mutex_t* mutex) @@ -769,8 +815,10 @@ cute_thread_id_t cute_thread_id() int cute_thread_wait(cute_thread_t* thread) { WaitForSingleObject((HANDLE)thread, INFINITE); + DWORD exit_code = 0; + GetExitCodeThread((HANDLE)thread, &exit_code); CloseHandle((HANDLE)thread); - return 1; + return (int)exit_code; } int cute_core_count() @@ -823,14 +871,16 @@ cute_mutex_t cute_mutex_create() return mutex; } -void cute_lock(cute_mutex_t* mutex) +int cute_lock(cute_mutex_t* mutex) { pthread_mutex_lock((pthread_mutex_t*)mutex); + return 1; } -void cute_unlock(cute_mutex_t* mutex) +int cute_unlock(cute_mutex_t* mutex) { pthread_mutex_unlock((pthread_mutex_t*)mutex); + return 1; } int cute_trylock(cute_mutex_t* mutex) @@ -878,7 +928,7 @@ void cute_cv_destroy(cute_cv_t* cv) cute_semaphore_t cute_semaphore_create(int initial_count) { cute_semaphore_t semaphore; - semaphore.id = CUTE_ALLOC(sizeof(sem_t), NULL); + semaphore.id = CUTE_SYNC_ALLOC(sizeof(sem_t), NULL); sem_init((sem_t*)semaphore.id, 0, (unsigned)initial_count); semaphore.count.i = initial_count; return semaphore; @@ -909,7 +959,7 @@ int cute_semaphore_value(cute_semaphore_t* semaphore) void cute_semaphore_destroy(cute_semaphore_t* semaphore) { sem_destroy((sem_t*)semaphore->id); - CUTE_FREE(semaphore->id); + CUTE_SYNC_FREE(semaphore->id, NULL); } #elif defined(__APPLE__) @@ -967,6 +1017,8 @@ int cute_semaphore_wait(cute_semaphore_t* semaphore) cute_apple_sem_t* apple_sem = (cute_apple_sem_t*)semaphore->id; int result = 1; cute_lock(&apple_sem->lock); + // Increment waiting_count before potentially waiting on the CV. + apple_sem->waiting_count += 1; while (apple_sem->count == 0 && result) { result = cute_cv_wait(&apple_sem->cv, &apple_sem->lock); } @@ -1004,10 +1056,32 @@ void cute_semaphore_destroy(cute_semaphore_t* semaphore) #endif +// Wrapper to adapt cute_thread_fn (returns int) to pthread's void* return type. +typedef struct cute_pthread_wrapper_t +{ + cute_thread_fn* fn; + void* udata; +} cute_pthread_wrapper_t; + +static void* cute_pthread_wrapper_internal(void* wrapper_ptr) +{ + cute_pthread_wrapper_t wrapper = *(cute_pthread_wrapper_t*)wrapper_ptr; + CUTE_SYNC_FREE(wrapper_ptr, NULL); + int result = wrapper.fn(wrapper.udata); + return (void*)(intptr_t)result; +} + cute_thread_t* cute_thread_create(cute_thread_fn fn, const char* name, void* udata) { + cute_pthread_wrapper_t* wrapper = (cute_pthread_wrapper_t*)CUTE_SYNC_ALLOC(sizeof(cute_pthread_wrapper_t), NULL); + if (!wrapper) return NULL; + wrapper->fn = fn; + wrapper->udata = udata; pthread_t thread; - pthread_create(&thread, NULL, (void* (*)(void*))fn, udata); + if (pthread_create(&thread, NULL, cute_pthread_wrapper_internal, wrapper) != 0) { + CUTE_SYNC_FREE(wrapper, NULL); + return NULL; + } #if !defined(__APPLE__) if (name) pthread_setname_np(thread, name); #else @@ -1033,8 +1107,10 @@ cute_thread_id_t cute_thread_id() int cute_thread_wait(cute_thread_t* thread) { - pthread_join((pthread_t)thread, NULL); - return 1; + void* retval = NULL; + pthread_join((pthread_t)thread, &retval); + // Thread functions return int, which was cast to void* by pthread. + return (int)(intptr_t)retval; } int cute_core_count() @@ -1166,13 +1242,11 @@ typedef struct cute_threadpool_t int task_count; cute_task_t* tasks; cute_mutex_t task_mutex; - cute_atomic_int_t tasks_running; int thread_count; cute_thread_t** threads; cute_atomic_int_t running; - cute_mutex_t sem_mutex; cute_semaphore_t semaphore; void* mem_ctx; } cute_threadpool_t; @@ -1195,13 +1269,15 @@ int cute_worker_thread_internal(void* udata) { cute_threadpool_t* pool = (cute_threadpool_t*)udata; while (cute_atomic_get(&pool->running)) { + // Wait for a signal that there's work to do (or shutdown). + cute_semaphore_wait(&pool->semaphore); + + // Process tasks until the queue is empty. This ensures all tasks get + // processed even if more tasks exist than semaphore posts were made. cute_task_t task; - if (cute_try_pop_task_internal(pool, &task)) { + while (cute_try_pop_task_internal(pool, &task)) { task.do_work(task.param); - cute_atomic_add(&pool->tasks_running, -1); } - - cute_semaphore_wait(&pool->semaphore); } return 0; } @@ -1214,12 +1290,10 @@ cute_threadpool_t* cute_threadpool_create(int thread_count, void* mem_ctx) pool->task_capacity = 64; pool->task_count = 0; pool->tasks = (cute_task_t*)cute_malloc_aligned(sizeof(cute_task_t) * pool->task_capacity, CUTE_SYNC_CACHELINE_SIZE, mem_ctx); - cute_atomic_set(&pool->tasks_running, 0); pool->task_mutex = cute_mutex_create(); pool->thread_count = thread_count; pool->threads = (cute_thread_t**)cute_malloc_aligned(sizeof(cute_thread_t*) * thread_count, CUTE_SYNC_CACHELINE_SIZE, mem_ctx); cute_atomic_set(&pool->running, 1); - pool->sem_mutex = cute_mutex_create(); pool->semaphore = cute_semaphore_create(0); pool->mem_ctx = mem_ctx; @@ -1247,7 +1321,6 @@ void cute_threadpool_add_task(cute_threadpool_t* pool, void (*func)(void*), void task.do_work = func; task.param = param; pool->tasks[pool->task_count++] = task; - cute_atomic_add(&pool->tasks_running, 1); cute_unlock(&pool->task_mutex); } @@ -1256,25 +1329,31 @@ void cute_threadpool_kick_and_wait(cute_threadpool_t* pool) { cute_threadpool_kick(pool); - while (pool->task_count) { - cute_task_t task; - if (cute_try_pop_task_internal(pool, &task)) { - cute_semaphore_try(&pool->semaphore); - task.do_work(task.param); - cute_atomic_add(&pool->tasks_running, -1); - } - CUTE_SYNC_YIELD(); + // Help process tasks on the calling thread while waiting. + cute_task_t task; + while (cute_try_pop_task_internal(pool, &task)) { + task.do_work(task.param); } - while (cute_atomic_get(&pool->tasks_running)) { - CUTE_SYNC_YIELD(); - } + // Spin until all tasks are complete (workers may still be processing). + int pending; + do { + cute_lock(&pool->task_mutex); + pending = pool->task_count; + cute_unlock(&pool->task_mutex); + if (pending) CUTE_SYNC_YIELD(); + } while (pending); } void cute_threadpool_kick(cute_threadpool_t* pool) { - if (pool->task_count) { - int count = pool->task_count < pool->thread_count ? pool->task_count : pool->thread_count; + cute_lock(&pool->task_mutex); + int task_count = pool->task_count; + cute_unlock(&pool->task_mutex); + + if (task_count) { + // Wake enough threads to handle all tasks (up to thread_count). + int count = task_count < pool->thread_count ? task_count : pool->thread_count; for (int i = 0; i < count; ++i) { cute_semaphore_post(&pool->semaphore); } @@ -1285,6 +1364,7 @@ void cute_threadpool_destroy(cute_threadpool_t* pool) { cute_atomic_set(&pool->running, 0); + // Wake all worker threads so they can see running==0 and exit. for (int i = 0; i < pool->thread_count; ++i) { cute_semaphore_post(&pool->semaphore); } @@ -1293,9 +1373,10 @@ void cute_threadpool_destroy(cute_threadpool_t* pool) cute_thread_wait(pool->threads[i]); } - cute_semaphore_destroy(&pool->semaphore); - cute_mutex_destroy(&pool->sem_mutex); + // Clean up synchronization primitives. cute_mutex_destroy(&pool->task_mutex); + cute_semaphore_destroy(&pool->semaphore); + cute_free_aligned(pool->tasks, pool->mem_ctx); cute_free_aligned(pool->threads, pool->mem_ctx); void* mem_ctx = pool->mem_ctx; @@ -1311,7 +1392,7 @@ void cute_threadpool_destroy(cute_threadpool_t* pool) This software is available under 2 licenses - you may choose the one you like. ------------------------------------------------------------------------------ ALTERNATIVE A - zlib license - Copyright (c) 2019 Randy Gaul http://www.randygaul.net + Copyright (c) 2026 Randy Gaul https://randygaul.github.io 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. diff --git a/libraries/cute/cute_tls.h b/libraries/cute/cute_tls.h index 4d7cef9b5..94c475e40 100644 --- a/libraries/cute/cute_tls.h +++ b/libraries/cute/cute_tls.h @@ -3,7 +3,7 @@ Licensing information can be found at the end of the file. ------------------------------------------------------------------------------ - cute_tls.h - v1.01 + cute_tls.h - v1.02 To create implementation (the function definitions) #define CUTE_TLS_IMPLEMENTATION @@ -13,7 +13,7 @@ SUMMARY cute_tls.h is a single-file header that implements some functions to - make a connection over TLS 1.2 and send some data back and forth over a + make a connection over TLS 1.2/1.3 and send some data back and forth over a TCP socket. It's meant mainly for making some simple HTTPS requsts to a web server, but nothing heavy-duty requiring extreme performance. It uses native APIs on Windows and Apple machines to get access to highly robust TLS @@ -21,8 +21,9 @@ through a TCP socket. On Windows Secure Channel is used. On Apple machines the Network.framework is used. - For *Nix s2n can be used a third-party solution, since no good OS-level TLS - handshake is currently available on Linux. + On Android, Java's SSLSocket is called via JNI. For *Nix s2n can be used as a + third-party solution, since no good OS-level TLS handshake is currently available + on Linux. GENERAL INFORMATION ABOUT HTTPS @@ -49,12 +50,10 @@ LIMITATIONS - Emscripten/Android platforms are not currently supported. + Emscripten is not currently supported. Client credentials are not supported. - IPv6 is not supported, though this is totally possible, just not initially in v1.00. - The server side of the connection is *not* supported. This is a client-only implementation. @@ -93,11 +92,24 @@ doing, so follow along on the s2n docs as you see fit: https://github.com/aws/s2n-tls - OTHER OPERATING SYSTEMS (e.g. ANDROID/EMSCRIPTEN) + BUILDING ON ANDROID + + On Android, cute_tls uses JNI to call Java's SSLSocket API. You must call tls_init() + once at startup with your JavaVM pointer: + + // In your JNI_OnLoad or native init: + extern "C" void tls_init(void* platform_data); + + JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { + tls_init(vm); + return JNI_VERSION_1_6; + } + + Link against the Android NDK. No additional Java code is required - cute_tls calls + the system SSLSocket directly via JNI. + - Android is a bit of another story. I don't have too much Android dev experience, but the best - option may be to call from C into Java and use Android's on SSL socket, or their HTTPS APi - directly from Java: https://developer.android.com/training/articles/security-ssl + OTHER OPERATING SYSTEMS (e.g. EMSCRIPTEN) For Emscripten it looks like they have a C++ websocket wrapper that might work, though I have yet to try it: https://emscripten.org/docs/porting/networking.html#emscripten-websockets-api @@ -129,6 +141,7 @@ Revision history: 1.00 (06/17/2023) initial release 1.01 (06/19/2023) s2n implementation for apple/linux + 1.02 (02/02/2026) TLS 1.3 on Windows, IPv6 support, fixed leaks and bugs, Apple callback lifetime fix, Android support via JNI */ /* @@ -230,7 +243,7 @@ typedef struct TLS_Connection { unsigned long long id; } TLS_Connection; -// Initiates a new TLS 1.2 connection. +// Initiates a new TLS 1.2/1.3 connection. TLS_Connection tls_connect(const char* hostname, int port); // Frees up all resources associated with the connection. @@ -267,6 +280,9 @@ int tls_read(TLS_Connection connection, void* data, int size); // Returns 0 on disconnect or -1 on error. int tls_send(TLS_Connection connection, const void* data, int size); +// Call once at startup on Android with your JavaVM* pointer. No-op on other platforms. +void tls_init(void* platform_data); + #define TLS_1_KB 1024 #define TLS_MAX_RECORD_SIZE (16 * TLS_1_KB) // TLS defines records to be up to 16kb. #define TLS_MAX_PACKET_SIZE (TLS_MAX_RECORD_SIZE + TLS_1_KB) // Some extra rooms for records split over two packets. @@ -296,6 +312,8 @@ int tls_send(TLS_Connection connection, const void* data, int size); # define TLS_WINDOWS # elif defined(__APPLE__) # define TLS_APPLE +# elif defined(__ANDROID__) +# define TLS_ANDROID # elif defined(__linux__) || defined(__unix__) && !defined(__APPLE__) && !defined(__EMSCRIPTEN__) # define TLS_S2N # else @@ -325,9 +343,17 @@ int tls_send(TLS_Connection connection, const void* data, int size); # pragma comment (lib, "ws2_32.lib") # pragma comment (lib, "secur32.lib") # pragma comment (lib, "shlwapi.lib") +#elif defined(TLS_ANDROID) +# include +# include +# include +# include +# include +# include #elif defined(TLS_APPLE) # include # include +# include #elif defined(TLS_S2N) # include # include @@ -449,9 +475,20 @@ typedef struct TLS_Context bool tcp_connect_pending; int received; char incoming[TLS_MAX_PACKET_SIZE]; +#elif defined(TLS_ANDROID) + jobject socket; // SSLSocket (global ref) + jobject input; // InputStream (global ref) + jobject output; // OutputStream (global ref) + jobject connect_socket; // Socket being connected (global ref), for cancellation + pthread_t connect_thread; + int connect_done; + int cancel_requested; + int port; #elif defined(TLS_APPLE) dispatch_queue_t dispatch; nw_connection_t connection; + int refcount; + int disconnecting; #endif } TLS_Context; @@ -459,6 +496,241 @@ typedef struct TLS_Context int tls_s2n_init = 0; #endif +#ifdef TLS_ANDROID +static JavaVM* g_tls_jvm = NULL; + +#define TLS_ANDROID_TIMEOUT_MS 30000 + +// JNI C API compatibility macros - allows using C-style JNI in both C and C++ builds. +// In C: JNIEnv is a pointer to the function table, so (*env)->Method(env, ...) works. +// In C++: JNIEnv is a wrapper class, access C table via env->functions->Method(env, ...). +#ifdef __cplusplus +#define TLS_JNI(env) ((env)->functions) +#define TLS_JVM(vm) ((vm)->functions) +#else +#define TLS_JNI(env) (*(env)) +#define TLS_JVM(vm) (*(vm)) +#endif + +// Returns JNIEnv and sets *attached=1 if we attached (caller must detach). +static JNIEnv* tls_get_env(int* attached) { + JNIEnv* env = NULL; + jint status = TLS_JVM(g_tls_jvm)->GetEnv(g_tls_jvm, (void**)&env, JNI_VERSION_1_6); + if (status == JNI_EDETACHED) { + TLS_JVM(g_tls_jvm)->AttachCurrentThread(g_tls_jvm, &env, NULL); + *attached = 1; + } else { + *attached = 0; + } + return env; +} + +static void tls_detach_if_needed(int attached) { + if (attached) { + TLS_JVM(g_tls_jvm)->DetachCurrentThread(g_tls_jvm); + } +} + +// Map Java SSL exception to TLS_State. Returns TLS_STATE_UNKNOWN_ERROR if no match. +static TLS_State tls_map_exception(JNIEnv* env, jthrowable ex) { + // Check exception class hierarchy for specific SSL errors. + jclass cert_expired = TLS_JNI(env)->FindClass(env, "java/security/cert/CertificateExpiredException"); + jclass cert_not_yet_valid = TLS_JNI(env)->FindClass(env, "java/security/cert/CertificateNotYetValidException"); + jclass cert_exception = TLS_JNI(env)->FindClass(env, "java/security/cert/CertificateException"); + jclass unknown_host = TLS_JNI(env)->FindClass(env, "java/net/UnknownHostException"); + jclass ssl_peer_unverified = TLS_JNI(env)->FindClass(env, "javax/net/ssl/SSLPeerUnverifiedException"); + jclass ssl_handshake = TLS_JNI(env)->FindClass(env, "javax/net/ssl/SSLHandshakeException"); + jclass socket_timeout = TLS_JNI(env)->FindClass(env, "java/net/SocketTimeoutException"); + jclass connect_exception = TLS_JNI(env)->FindClass(env, "java/net/ConnectException"); + + TLS_State result = TLS_STATE_UNKNOWN_ERROR; + + // Check the exception and its cause chain. + jthrowable current = ex; + jclass throwable_class = TLS_JNI(env)->FindClass(env, "java/lang/Throwable"); + jmethodID get_cause = TLS_JNI(env)->GetMethodID(env, throwable_class, "getCause", "()Ljava/lang/Throwable;"); + + for (int depth = 0; depth < 5 && current; depth++) { + if (TLS_JNI(env)->IsInstanceOf(env, current, cert_expired) || + TLS_JNI(env)->IsInstanceOf(env, current, cert_not_yet_valid)) { + result = TLS_STATE_CERTIFICATE_EXPIRED; + break; + } + if (TLS_JNI(env)->IsInstanceOf(env, current, ssl_peer_unverified)) { + result = TLS_STATE_CANNOT_VERIFY_CA_CHAIN; + break; + } + if (TLS_JNI(env)->IsInstanceOf(env, current, cert_exception)) { + result = TLS_STATE_BAD_CERTIFICATE; + break; + } + if (TLS_JNI(env)->IsInstanceOf(env, current, unknown_host)) { + result = TLS_STATE_BAD_HOSTNAME; + break; + } + if (TLS_JNI(env)->IsInstanceOf(env, current, socket_timeout) || + TLS_JNI(env)->IsInstanceOf(env, current, connect_exception)) { + result = TLS_STATE_INVALID_SOCKET; + break; + } + // Walk to cause. + jthrowable cause = (jthrowable)TLS_JNI(env)->CallObjectMethod(env, current, get_cause); + if (depth > 0) TLS_JNI(env)->DeleteLocalRef(env, current); + current = cause; + } + if (current && current != ex) TLS_JNI(env)->DeleteLocalRef(env, current); + + TLS_JNI(env)->DeleteLocalRef(env, cert_expired); + TLS_JNI(env)->DeleteLocalRef(env, cert_not_yet_valid); + TLS_JNI(env)->DeleteLocalRef(env, cert_exception); + TLS_JNI(env)->DeleteLocalRef(env, unknown_host); + TLS_JNI(env)->DeleteLocalRef(env, ssl_peer_unverified); + TLS_JNI(env)->DeleteLocalRef(env, ssl_handshake); + TLS_JNI(env)->DeleteLocalRef(env, socket_timeout); + TLS_JNI(env)->DeleteLocalRef(env, connect_exception); + TLS_JNI(env)->DeleteLocalRef(env, throwable_class); + + return result; +} + +// Background thread for non-blocking connect. +static void* tls_connect_thread(void* arg) { + TLS_Context* ctx = (TLS_Context*)arg; + JNIEnv* env; + TLS_JVM(g_tls_jvm)->AttachCurrentThread(g_tls_jvm, &env, NULL); + + // Create plain socket and store immediately for cancellation support. + jclass socket_class = TLS_JNI(env)->FindClass(env, "java/net/Socket"); + jmethodID socket_ctor = TLS_JNI(env)->GetMethodID(env, socket_class, "", "()V"); + jobject plain_socket = TLS_JNI(env)->NewObject(env, socket_class, socket_ctor); + ctx->connect_socket = TLS_JNI(env)->NewGlobalRef(env, plain_socket); + + // Create InetSocketAddress(hostname, port) + jclass addr_class = TLS_JNI(env)->FindClass(env, "java/net/InetSocketAddress"); + jmethodID addr_ctor = TLS_JNI(env)->GetMethodID(env, addr_class, "", "(Ljava/lang/String;I)V"); + jstring jhost = TLS_JNI(env)->NewStringUTF(env, ctx->hostname); + jobject addr = TLS_JNI(env)->NewObject(env, addr_class, addr_ctor, jhost, ctx->port); + + // Connect with timeout: socket.connect(addr, timeout) + jmethodID connect_method = TLS_JNI(env)->GetMethodID(env, socket_class, "connect", "(Ljava/net/SocketAddress;I)V"); + TLS_JNI(env)->CallVoidMethod(env, plain_socket, connect_method, addr, (jint)TLS_ANDROID_TIMEOUT_MS); + TLS_JNI(env)->DeleteLocalRef(env, addr); + TLS_JNI(env)->DeleteLocalRef(env, addr_class); + + if (TLS_JNI(env)->ExceptionCheck(env)) { + TLS_JNI(env)->ExceptionClear(env); + if (!ctx->cancel_requested) ctx->state = TLS_STATE_INVALID_SOCKET; + goto cleanup; + } + + // Wrap with SSL: SSLSocketFactory.getDefault().createSocket(socket, host, port, autoClose) + { + jclass factory_class = TLS_JNI(env)->FindClass(env, "javax/net/ssl/SSLSocketFactory"); + jmethodID get_default = TLS_JNI(env)->GetStaticMethodID(env, factory_class, "getDefault", "()Ljavax/net/SocketFactory;"); + jobject factory = TLS_JNI(env)->CallStaticObjectMethod(env, factory_class, get_default); + jmethodID create_socket = TLS_JNI(env)->GetMethodID(env, factory_class, "createSocket", "(Ljava/net/Socket;Ljava/lang/String;IZ)Ljava/net/Socket;"); + jobject ssl_socket = TLS_JNI(env)->CallObjectMethod(env, factory, create_socket, plain_socket, jhost, ctx->port, (jboolean)1); + TLS_JNI(env)->DeleteLocalRef(env, factory); + TLS_JNI(env)->DeleteLocalRef(env, factory_class); + + if (TLS_JNI(env)->ExceptionCheck(env)) { + jthrowable ex = TLS_JNI(env)->ExceptionOccurred(env); + if (!ctx->cancel_requested) ctx->state = tls_map_exception(env, ex); + TLS_JNI(env)->DeleteLocalRef(env, ex); + TLS_JNI(env)->ExceptionClear(env); + goto cleanup; + } + + // Clear connect_socket - SSL socket now owns it (autoClose=true). + TLS_JNI(env)->DeleteGlobalRef(env, ctx->connect_socket); + ctx->connect_socket = NULL; + + // Set read timeout and start handshake. + jclass ssl_socket_class = TLS_JNI(env)->FindClass(env, "javax/net/ssl/SSLSocket"); + jmethodID set_timeout = TLS_JNI(env)->GetMethodID(env, ssl_socket_class, "setSoTimeout", "(I)V"); + TLS_JNI(env)->CallVoidMethod(env, ssl_socket, set_timeout, (jint)TLS_ANDROID_TIMEOUT_MS); + + jmethodID start_handshake = TLS_JNI(env)->GetMethodID(env, ssl_socket_class, "startHandshake", "()V"); + TLS_JNI(env)->CallVoidMethod(env, ssl_socket, start_handshake); + + if (TLS_JNI(env)->ExceptionCheck(env)) { + jthrowable ex = TLS_JNI(env)->ExceptionOccurred(env); + if (!ctx->cancel_requested) ctx->state = tls_map_exception(env, ex); + TLS_JNI(env)->DeleteLocalRef(env, ex); + TLS_JNI(env)->ExceptionClear(env); + jmethodID close_method = TLS_JNI(env)->GetMethodID(env, ssl_socket_class, "close", "()V"); + TLS_JNI(env)->CallVoidMethod(env, ssl_socket, close_method); + TLS_JNI(env)->ExceptionClear(env); + TLS_JNI(env)->DeleteLocalRef(env, ssl_socket); + TLS_JNI(env)->DeleteLocalRef(env, ssl_socket_class); + TLS_JNI(env)->DeleteLocalRef(env, plain_socket); + TLS_JNI(env)->DeleteLocalRef(env, socket_class); + TLS_JNI(env)->DeleteLocalRef(env, jhost); + ctx->connect_done = 1; + TLS_JVM(g_tls_jvm)->DetachCurrentThread(g_tls_jvm); + return NULL; + } + + // Get input/output streams and store as global refs. + jmethodID get_input = TLS_JNI(env)->GetMethodID(env, ssl_socket_class, "getInputStream", "()Ljava/io/InputStream;"); + jmethodID get_output = TLS_JNI(env)->GetMethodID(env, ssl_socket_class, "getOutputStream", "()Ljava/io/OutputStream;"); + jobject input = TLS_JNI(env)->CallObjectMethod(env, ssl_socket, get_input); + jobject output = TLS_JNI(env)->CallObjectMethod(env, ssl_socket, get_output); + TLS_JNI(env)->DeleteLocalRef(env, ssl_socket_class); + + ctx->socket = TLS_JNI(env)->NewGlobalRef(env, ssl_socket); + ctx->input = TLS_JNI(env)->NewGlobalRef(env, input); + ctx->output = TLS_JNI(env)->NewGlobalRef(env, output); + TLS_JNI(env)->DeleteLocalRef(env, ssl_socket); + TLS_JNI(env)->DeleteLocalRef(env, input); + TLS_JNI(env)->DeleteLocalRef(env, output); + } + TLS_JNI(env)->DeleteLocalRef(env, plain_socket); + TLS_JNI(env)->DeleteLocalRef(env, socket_class); + TLS_JNI(env)->DeleteLocalRef(env, jhost); + + ctx->state = TLS_STATE_CONNECTED; + ctx->connect_done = 1; + TLS_JVM(g_tls_jvm)->DetachCurrentThread(g_tls_jvm); + return NULL; + +cleanup: + if (ctx->connect_socket) { + jmethodID close_method = TLS_JNI(env)->GetMethodID(env, socket_class, "close", "()V"); + TLS_JNI(env)->CallVoidMethod(env, ctx->connect_socket, close_method); + TLS_JNI(env)->ExceptionClear(env); + TLS_JNI(env)->DeleteGlobalRef(env, ctx->connect_socket); + ctx->connect_socket = NULL; + } + TLS_JNI(env)->DeleteLocalRef(env, plain_socket); + TLS_JNI(env)->DeleteLocalRef(env, socket_class); + TLS_JNI(env)->DeleteLocalRef(env, jhost); + ctx->connect_done = 1; + TLS_JVM(g_tls_jvm)->DetachCurrentThread(g_tls_jvm); + return NULL; +} +#endif + +#ifdef TLS_APPLE +// Release a reference to the context. Frees when refcount hits 0. +// Uses OSAtomicDecrement32 for atomic decrement-and-test. +static void tls_ctx_release(TLS_Context* ctx) +{ + if (OSAtomicDecrement32(&ctx->refcount) == 0) { + nw_release(ctx->connection); + dispatch_release(ctx->dispatch); + while (ctx->q.count) { + void* packet; + int size; + tls_packet_queue_pop(&ctx->q, &packet, &size); + TLS_FREE(packet); + } + pthread_mutex_destroy(&ctx->q.lock); + TLS_FREE(ctx); + } +} +#endif + // Called in a poll-style manner on Windows. // For Apple we call this once on init to setup a tail-end recursive callback loop. static void tls_recv(TLS_Context* ctx) @@ -486,7 +758,13 @@ static void tls_recv(TLS_Context* ctx) #ifdef TLS_APPLE // Queue up an asynchronous receive block loop. - nw_connection_receive(ctx->connection, 1, TLS_MAX_PACKET_SIZE, ^(dispatch_data_t content, nw_content_context_t context, bool is_complete, nw_error_t receive_error) { + // Prevent use-after-free: hold a reference for the pending callback. + OSAtomicIncrement32(&ctx->refcount); + nw_connection_receive(ctx->connection, 1, TLS_MAX_PACKET_SIZE, ^(dispatch_data_t content, nw_content_context_t context, bool is_complete, nw_error_t receive_error) { + if (ctx->disconnecting) { + tls_ctx_release(ctx); + return; + } if (content != NULL) { // What a horrid API design... So over-engineered to simply memcpy a buffer. int size = (int)dispatch_data_get_size(content); @@ -499,25 +777,28 @@ static void tls_recv(TLS_Context* ctx) } if (is_complete && !receive_error) { ctx->state = TLS_STATE_DISCONNECTED_BUT_PACKETS_STILL_REMAIN; + tls_ctx_release(ctx); } else if (receive_error) { ctx->state = TLS_STATE_UNKNOWN_ERROR; + tls_ctx_release(ctx); } else { // Queue up another call to this function to receive the next packet. + // tls_recv increments refcount for the new callback, then we release ours. tls_recv(ctx); + tls_ctx_release(ctx); } }); #endif // TLS_APPLE #ifdef TLS_S2N - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - int bytes_read = 0; - while (bytes_read < sizeof(ctx->incoming)) { - int r = s2n_recv(ctx->connection, ctx->incoming + bytes_read, sizeof(ctx->incoming) - bytes_read, &blocked); + while (ctx->received < sizeof(ctx->incoming)) { + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + int r = s2n_recv(ctx->connection, ctx->incoming + ctx->received, sizeof(ctx->incoming) - ctx->received, &blocked); s2n_error_type etype = (s2n_error_type)s2n_error_get_type(s2n_errno); - if (r == 0) { + if (r == 0 || blocked != S2N_NOT_BLOCKED) { break; } else if (r > 0) { - bytes_read += r; + ctx->received += r; } else if (etype == S2N_ERR_T_CLOSED) { ctx->state = TLS_STATE_DISCONNECTED; break; @@ -529,7 +810,6 @@ static void tls_recv(TLS_Context* ctx) break; } } - ctx->received = bytes_read; #endif // TLS_S2N } @@ -574,13 +854,12 @@ TLS_Connection tls_connect(const char* hostname, int port) hints.ai_protocol = IPPROTO_TCP; struct addrinfo* addri = NULL; if (getaddrinfo(hostname, sport, &hints, &addri) != 0) { - freeaddrinfo(addri); TLS_FREE(ctx); return result; } - // Create a TCP IPv4 socket. - ctx->sock = socket(AF_INET, SOCK_STREAM, 0); + // Create a TCP socket. + ctx->sock = socket(addri->ai_family, SOCK_STREAM, 0); if (ctx->sock == -1) { freeaddrinfo(addri); TLS_FREE(ctx); @@ -609,26 +888,24 @@ TLS_Connection tls_connect(const char* hostname, int port) } // Startup the TCP connection. - if (connect(ctx->sock, addri->ai_addr, (int)addri->ai_addrlen) == -1) { + int connect_result = connect(ctx->sock, addri->ai_addr, (int)addri->ai_addrlen); + freeaddrinfo(addri); + addri = NULL; + if (connect_result == -1) { #ifdef TLS_WINDOWS int error = WSAGetLastError(); if (error != WSAEWOULDBLOCK && error != WSAEINPROGRESS) { - freeaddrinfo(addri); closesocket(ctx->sock); TLS_FREE(ctx); return result; } #else if (errno != EWOULDBLOCK && errno != EINPROGRESS && errno != EAGAIN) { - freeaddrinfo(addri); close(ctx->sock); TLS_FREE(ctx); return result; } #endif - } else { - freeaddrinfo(addri); - addri = NULL; } ctx->state = TLS_STATE_PENDING; @@ -642,7 +919,7 @@ TLS_Connection tls_connect(const char* hostname, int port) cred.dwFlags = SCH_USE_STRONG_CRYPTO // Disable deprecated or otherwise weak algorithms (on as default). | SCH_CRED_AUTO_CRED_VALIDATION // Automatically validate server cert (on as default), as opposed to manual verify. | SCH_CRED_NO_DEFAULT_CREDS; // Client certs are not supported. - cred.grbitEnabledProtocols = SP_PROT_TLS1_2; // Specifically pick only TLS 1.2. + cred.grbitEnabledProtocols = SP_PROT_TLS1_2 | SP_PROT_TLS1_3; // TLS 1.2 minimum, 1.3 if available. if (AcquireCredentialsHandleA(NULL, (char*)UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL, &cred, NULL, NULL, &ctx->handle, NULL) != SEC_E_OK) { @@ -661,6 +938,7 @@ TLS_Connection tls_connect(const char* hostname, int port) return result; } if (s2n_connection_set_fd(connection, ctx->sock) < 0) { + s2n_connection_free(connection); close(ctx->sock); TLS_FREE(ctx); return result; @@ -682,27 +960,37 @@ TLS_Connection tls_connect(const char* hostname, int port) #endif #endif // defined(TLS_WINDOWS) || defined(TLS_S2N) + #ifdef TLS_ANDROID + ctx->port = port; + ctx->connect_done = 0; + ctx->state = TLS_STATE_PENDING; + if (pthread_create(&ctx->connect_thread, NULL, tls_connect_thread, ctx) != 0) { + TLS_FREE(ctx); + return result; + } + #endif // TLS_ANDROID + #ifdef TLS_APPLE // Used for syncing the packet queue. pthread_mutex_init(&ctx->q.lock, NULL); + // Initialize refcount for preventing use-after-free from async callbacks. + ctx->refcount = 1; + ctx->disconnecting = 0; + // Turn on TLS (default config). nw_endpoint_t endpoint = nw_endpoint_create_host(hostname, sport); nw_parameters_configure_protocol_block_t configure_tls = NW_PARAMETERS_DEFAULT_CONFIGURATION; nw_parameters_t parameters = nw_parameters_create_secure_tcp(configure_tls, NW_PARAMETERS_DEFAULT_CONFIGURATION); - // Set ipv4. nw_protocol_stack_t protocol_stack = nw_parameters_copy_default_protocol_stack(parameters); nw_protocol_options_t ip_options = nw_protocol_stack_copy_internet_protocol(protocol_stack); - nw_ip_options_set_version(ip_options, nw_ip_version_4); // Create actual connection object. ctx->connection = nw_connection_create(endpoint, parameters); - nw_retain(ctx->connection); // Create an async queue for dispatching all of our connection's callbacks/blocks. ctx->dispatch = dispatch_queue_create("com.tls.internal_queue", DISPATCH_QUEUE_SERIAL); - dispatch_retain(ctx->dispatch); // Various calls into Network.framework are asynchronous and use a queue to dispatch callbacks (blocks). nw_connection_set_queue(ctx->connection, ctx->dispatch); @@ -864,14 +1152,24 @@ TLS_State tls_process(TLS_Connection connection) while (size != 0) { int d = send(ctx->sock, buffer, size, 0); if (d <= 0) { - break; + #ifdef TLS_WINDOWS + int error = WSAGetLastError(); + if (error != WSAEWOULDBLOCK && error != WSAEINPROGRESS) { + break; + } + #else + if (errno != EAGAIN && errno != EWOULDBLOCK && errno != EINPROGRESS) { + break; + } + #endif + } else { + size -= d; + buffer += d; } - size -= d; - buffer += d; } FreeContextBuffer(outbuffers[0].pvBuffer); if (size != 0) { - // Somehow failed to send() data to server. + // Failed to send() data to server. ctx->state = TLS_STATE_UNKNOWN_ERROR; return ctx->state; } @@ -904,6 +1202,14 @@ TLS_State tls_process(TLS_Connection connection) tls_recv(ctx); #endif // TLS_WINDOWS + #ifdef TLS_ANDROID + // Check if background connect thread has finished. + if (ctx->connect_done) { + pthread_join(ctx->connect_thread, NULL); + // ctx->state was set by the thread (CONNECTED or error). + } + #endif // TLS_ANDROID + #ifdef TLS_APPLE // Nothing needed here. #endif // TLS_APPLE @@ -917,7 +1223,7 @@ TLS_State tls_process(TLS_Connection connection) s2n_error_type etype = (s2n_error_type)s2n_error_get_type(s2n_errno); if (etype == S2N_ERR_T_PROTO) { // For some unknown reason s2n doesn't expose their error constants, like at all. - // So to avoid finding the correct header and including it, we can at least us + // So to avoid finding the correct header and including it, we can at least use // string comparisons, as that's the only way s2n has exposed error codes that aren't // a nightmare to hookup, and likely won't break as they add new error types. #define TLS_S2N_ERROR_MATCHES(X) (!TLS_STRCMP(s2n_strerror_name(s2n_errno), #X)) @@ -1016,6 +1322,46 @@ TLS_State tls_process(TLS_Connection connection) } #endif // TLS_WINDOWS + #ifdef TLS_ANDROID + // Check for available data and push to packet queue. + int attached; + JNIEnv* env = tls_get_env(&attached); + jclass input_class = TLS_JNI(env)->FindClass(env, "java/io/InputStream"); + jmethodID available_method = TLS_JNI(env)->GetMethodID(env, input_class, "available", "()I"); + jint available = TLS_JNI(env)->CallIntMethod(env, ctx->input, available_method); + + if (TLS_JNI(env)->ExceptionCheck(env)) { + TLS_JNI(env)->ExceptionClear(env); + ctx->state = TLS_STATE_DISCONNECTED; + TLS_JNI(env)->DeleteLocalRef(env, input_class); + } else if (available > 0) { + jbyteArray buffer = TLS_JNI(env)->NewByteArray(env, available); + jmethodID read_method = TLS_JNI(env)->GetMethodID(env, input_class, "read", "([B)I"); + jint bytes_read = TLS_JNI(env)->CallIntMethod(env, ctx->input, read_method, buffer); + TLS_JNI(env)->DeleteLocalRef(env, input_class); + + if (TLS_JNI(env)->ExceptionCheck(env)) { + TLS_JNI(env)->ExceptionClear(env); + ctx->state = TLS_STATE_DISCONNECTED; + TLS_JNI(env)->DeleteLocalRef(env, buffer); + } else if (bytes_read > 0) { + void* data = TLS_MALLOC(bytes_read); + TLS_JNI(env)->GetByteArrayRegion(env, buffer, 0, bytes_read, (jbyte*)data); + TLS_JNI(env)->DeleteLocalRef(env, buffer); + tls_packet_queue_push(&ctx->q, data, bytes_read); + } else if (bytes_read < 0) { + // End of stream + ctx->state = TLS_STATE_DISCONNECTED; + TLS_JNI(env)->DeleteLocalRef(env, buffer); + } else { + TLS_JNI(env)->DeleteLocalRef(env, buffer); + } + } else { + TLS_JNI(env)->DeleteLocalRef(env, input_class); + } + tls_detach_if_needed(attached); + #endif // TLS_ANDROID + #ifdef TLS_APPLE // Nothing on Apple. // Reads are setup via chained callbacks upon connection starting. @@ -1092,6 +1438,9 @@ void tls_disconnect(TLS_Connection connection) int size = outbuffers[0].cbBuffer; while (size != 0) { int d = send(ctx->sock, buffer, size, 0); + if (d <= 0) { + break; + } buffer += d; size -= d; } @@ -1104,10 +1453,48 @@ void tls_disconnect(TLS_Connection connection) closesocket(ctx->sock); #endif + #ifdef TLS_ANDROID + // Cancel and wait for connect thread if still running. + if (!ctx->connect_done) { + ctx->cancel_requested = 1; + // Close connect_socket to interrupt blocking connect/handshake. + if (ctx->connect_socket) { + int attached; + JNIEnv* env = tls_get_env(&attached); + jclass socket_class = TLS_JNI(env)->FindClass(env, "java/net/Socket"); + jmethodID close_method = TLS_JNI(env)->GetMethodID(env, socket_class, "close", "()V"); + TLS_JNI(env)->CallVoidMethod(env, ctx->connect_socket, close_method); + TLS_JNI(env)->ExceptionClear(env); + TLS_JNI(env)->DeleteLocalRef(env, socket_class); + tls_detach_if_needed(attached); + } + pthread_join(ctx->connect_thread, NULL); + } + + // Clean up socket if connection succeeded. + if (ctx->socket) { + int attached; + JNIEnv* env = tls_get_env(&attached); + jclass socket_class = TLS_JNI(env)->FindClass(env, "java/net/Socket"); + jmethodID close_method = TLS_JNI(env)->GetMethodID(env, socket_class, "close", "()V"); + TLS_JNI(env)->CallVoidMethod(env, ctx->socket, close_method); + TLS_JNI(env)->ExceptionClear(env); + TLS_JNI(env)->DeleteLocalRef(env, socket_class); + + TLS_JNI(env)->DeleteGlobalRef(env, ctx->socket); + TLS_JNI(env)->DeleteGlobalRef(env, ctx->input); + TLS_JNI(env)->DeleteGlobalRef(env, ctx->output); + tls_detach_if_needed(attached); + } + #endif + #ifdef TLS_APPLE - dispatch_release(ctx->dispatch); - nw_release(ctx->connection); - pthread_mutex_destroy(&ctx->q.lock); + // Signal callbacks to stop and release owner reference. + // Actual cleanup happens in tls_ctx_release when refcount hits 0. + ctx->disconnecting = 1; + nw_connection_cancel(ctx->connection); + tls_ctx_release(ctx); + return; #endif #ifdef TLS_S2N @@ -1117,6 +1504,7 @@ void tls_disconnect(TLS_Connection connection) s2n_shutdown(ctx->connection, &blocked); s2n_connection_free(ctx->connection); + close(ctx->sock); #endif while (ctx->q.count) { @@ -1125,6 +1513,7 @@ void tls_disconnect(TLS_Connection connection) tls_packet_queue_pop(&ctx->q, &packet, &size); TLS_FREE(packet); } + TLS_FREE(ctx); } @@ -1212,14 +1601,15 @@ int tls_send(TLS_Connection connection, const void* data, int size) return -1; } #else - if (errno != EAGAIN) { + if (errno != EAGAIN && errno != EWOULDBLOCK) { // Error sending data to socket, or server disconnected. ctx->state = TLS_STATE_UNKNOWN_ERROR; return -1; } #endif + } else { + sent += d; } - sent += d; } data = (const void*)((uintptr_t)data + use); @@ -1227,6 +1617,38 @@ int tls_send(TLS_Connection connection, const void* data, int size) } #endif // TLS_WINDOWS + #ifdef TLS_ANDROID + int attached; + JNIEnv* env = tls_get_env(&attached); + jclass output_class = TLS_JNI(env)->FindClass(env, "java/io/OutputStream"); + jmethodID write_method = TLS_JNI(env)->GetMethodID(env, output_class, "write", "([B)V"); + jmethodID flush_method = TLS_JNI(env)->GetMethodID(env, output_class, "flush", "()V"); + + jbyteArray buffer = TLS_JNI(env)->NewByteArray(env, size); + TLS_JNI(env)->SetByteArrayRegion(env, buffer, 0, size, (const jbyte*)data); + TLS_JNI(env)->CallVoidMethod(env, ctx->output, write_method, buffer); + TLS_JNI(env)->DeleteLocalRef(env, buffer); + + if (TLS_JNI(env)->ExceptionCheck(env)) { + TLS_JNI(env)->ExceptionClear(env); + TLS_JNI(env)->DeleteLocalRef(env, output_class); + ctx->state = TLS_STATE_UNKNOWN_ERROR; + tls_detach_if_needed(attached); + return -1; + } + + TLS_JNI(env)->CallVoidMethod(env, ctx->output, flush_method); + TLS_JNI(env)->DeleteLocalRef(env, output_class); + + if (TLS_JNI(env)->ExceptionCheck(env)) { + TLS_JNI(env)->ExceptionClear(env); + ctx->state = TLS_STATE_UNKNOWN_ERROR; + tls_detach_if_needed(attached); + return -1; + } + tls_detach_if_needed(attached); + #endif // TLS_ANDROID + #ifdef TLS_APPLE // Again, so over-engineered. We just need to send a blob of bytes... void* copy = TLS_MALLOC(size); @@ -1239,6 +1661,7 @@ int tls_send(TLS_Connection connection, const void* data, int size) ctx->state = TLS_STATE_UNKNOWN_ERROR; } }); + dispatch_release(dispatch_data); #endif // TLS_APPLE #ifdef TLS_S2N @@ -1259,6 +1682,15 @@ int tls_send(TLS_Connection connection, const void* data, int size) return 0; } +void tls_init(void* platform_data) +{ +#ifdef TLS_ANDROID + g_tls_jvm = (JavaVM*)platform_data; +#else + (void)platform_data; +#endif +} + #endif // CUTE_TLS_IMPLEMENTATION_ONCE #endif // CUTE_TLS_IMPLEMENTATION @@ -1267,7 +1699,7 @@ int tls_send(TLS_Connection connection, const void* data, int size) This software is available under 2 licenses - you may choose the one you like. ------------------------------------------------------------------------------ ALTERNATIVE A - zlib license - Copyright (c) 2023 Randy Gaul https://randygaul.github.io/ + Copyright (c) 2026 Randy Gaul https://randygaul.github.io/ 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. diff --git a/libraries/imgui/imconfig.h b/libraries/imgui/imconfig.h index 5b158ff0a..e289b1398 100644 --- a/libraries/imgui/imconfig.h +++ b/libraries/imgui/imconfig.h @@ -31,6 +31,7 @@ //#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import //#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden #define IMGUI_API CF_API +#define CIMGUI_API CF_API //---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names. //#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS diff --git a/libraries/pico/pico_unit.h b/libraries/pico/pico_unit.h index 35e05e2c2..c4bf5b507 100644 --- a/libraries/pico/pico_unit.h +++ b/libraries/pico/pico_unit.h @@ -75,7 +75,7 @@ extern "C" { * * @param name The name of the test. Must be a valid C function name */ -#define TEST_CASE(name) static bool name() +#define TEST_CASE(name) static bool name(void) /** * @brief Asserts that a condition is true diff --git a/mkdocs.yml b/mkdocs.yml index 9a13c950a..6123c9a6a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -10,16 +10,22 @@ repo_url: https://github.com/RandyGaul/cute_framework repo_name: RandyGaul/cute_framework # Copyright -copyright: Copyright © 2019—2025 Randy Gaul +copyright: Copyright © 2019—2026 Randy Gaul # Configuration theme: name: material logo: assets/logo.png features: + - navigation.instant + - navigation.instant.prefetch + - navigation.instant.progress - navigation.path - navigation.indexes + - navigation.footer - content.code.copy + - search.suggest + - search.highlight palette: - media: "(prefers-color-scheme)" scheme: cute @@ -42,6 +48,9 @@ theme: extra_css: - stylesheets/cute.css +extra_javascript: + - javascripts/redirect.js + # Navigation not_in_nav: | /CPU/*.md @@ -50,6 +59,7 @@ not_in_nav: | /array/*.md /atomic/*.md /audio/*.md + /binding/*.md /base64/*.md /collision/*.md /coroutine/*.md @@ -62,12 +72,14 @@ not_in_nav: | /input/*.md /json/*.md /list/*.md + /map/*.md /math/*.md /multithreading/*.md /net/*.md /noise/*.md /path/*.md /png_cache/*.md + /custom_sprite/*.md /random/*.md /sprite/*.md /string/*.md @@ -85,17 +97,19 @@ nav: - Game Loop and Time: topics/game_loop_and_time.md - File I/O: topics/file_io.md - Drawing: topics/drawing.md - - Transforms: topics/camera.md - Debug/Tools UI: topics/dear_imgui.md - Allocators: topics/allocator.md + - Android Builds: topics/android.md - Atomics: topics/atomics.md - Audio: topics/audio.md - Camera: topics/camera.md - Collision: topics/collision.md - Coroutines: topics/coroutines.md + - Custom Sprites: topics/custom_sprites.md - Data Structures: topics/data_structures.md - Web Builds with Emscripten: topics/emscripten.md - Input: topics/input.md + - Input Bindings: topics/input_bindings.md - macOS + iOS Builds: topics/ios.md - Low Level Graphics: topics/low_level_graphics.md - Multithreading: topics/multithreading.md @@ -107,14 +121,18 @@ nav: - Strings: topics/strings.md - Virtual File System: topics/virtual_file_system.md - HTTPS: topics/web.md - - Samples: samples.md + - Samples: + - samples/index.md + - Waves: samples/waves.md + - Space Shooter: samples/spaceshooter.md - Made with CF: made_with_cf.md - Community / Ask for Help: community_ask_for_help.md - API Reference: api_reference.md # Plugins plugins: - - search + - search: + separator: '[\\s\\-\\_]+' - social # Extensions diff --git a/msvc2022.cmd b/msvc2022.cmd deleted file mode 100644 index 009e6a3cf..000000000 --- a/msvc2022.cmd +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -mkdir build_msvc_2022 > nul 2> nul -cmake -G "Visual Studio 17 2022" -A x64 -Bbuild_msvc_2022 . \ No newline at end of file diff --git a/msvc2026.cmd b/msvc2026.cmd new file mode 100644 index 000000000..a43cdf998 --- /dev/null +++ b/msvc2026.cmd @@ -0,0 +1,3 @@ +@echo off +mkdir build_msvc_2026 > nul 2> nul +cmake -G "Visual Studio 18 2026" -A x64 -Bbuild_msvc_2026 . diff --git a/samples/9_slice.c b/samples/9_slice.c index 0bce5b670..0c3e1db58 100644 --- a/samples/9_slice.c +++ b/samples/9_slice.c @@ -3,8 +3,7 @@ void mount_content_directory_as(const char* dir) { - const char* path = cf_fs_get_base_directory(); - path = cf_path_normalize(path); + char* path = cf_path_normalize(cf_fs_get_base_directory()); path = cf_string_append(path, "/9_slice_data"); cf_fs_mount(path, dir, false); cf_string_free(path); @@ -24,7 +23,11 @@ int main(int argc, char* argv[]) float rotation = 0.0f; bool is_tiled = false; - cf_htbl const char** animations = (const char**)cf_hashtable_keys(sprite.animations); + int animation_count = cf_sprite_animation_count(&sprite); + const char** animation_names = NULL; + for (int i = 0; i < animation_count; i++) { + apush(animation_names, cf_sprite_animation_name_at(&sprite, i)); + } int animation_index = 0; while (cf_app_is_running()) { @@ -42,8 +45,8 @@ int main(int argc, char* argv[]) } ImGui_Begin("9 Slice", NULL, ImGuiWindowFlags_None); - if (ImGui_ComboChar("Animation", &animation_index, animations, cf_hashtable_count(sprite.animations))) { - cf_sprite_play(&sprite, animations[animation_index]); + if (ImGui_ComboChar("Animation", &animation_index, animation_names, animation_count)) { + cf_sprite_play(&sprite, animation_names[animation_index]); } ImGui_Checkbox("Tiled?", &is_tiled); @@ -58,6 +61,7 @@ int main(int argc, char* argv[]) cf_app_draw_onto_screen(true); } + afree(animation_names); cf_destroy_app(); return 0; diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index ad43994d3..549860d04 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -42,7 +42,6 @@ add_sample(windowresizing window_resizing.cpp) add_sample(basicinput basic_input.c) add_sample(windowevents window_events.c) add_sample(window window.cpp) -add_sample(docsparser docs_parser.cpp) add_sample(scratch scratch.cpp) if (NOT EMSCRIPTEN) add_sample(https https.c) @@ -74,10 +73,21 @@ add_sample(sprite_slice sprite_slice.cpp) add_sample(sprite_shatter sprite_shatter.cpp) add_sample(screen_shatter screen_shatter.cpp) add_sample(font_debug font_debug.cpp) -add_sample(clay clay.c) +# Disabled on Windows -- MSVC crashes for internal reasons when compiling these. +if(NOT WIN32) + add_sample(clay clay.c) + add_sample(clay_ui_animations clay_ui_animations.c) +endif() add_sample(9_slice 9_slice.c) -add_sample(clay_ui_animations clay_ui_animations.c) add_sample(basic_camera basic_camera.c) +add_sample(imgui_texture imgui_texture.c) +add_sample(input_binding input_binding.c) +add_sample(compute compute.c) +add_sample(galaxy galaxy.c) +add_sample(hrc hrc.c) +add_sample(canvas_readback canvas_readback.c) +add_sample(glitch glitch.cpp) +add_sample(customsprite custom_sprite.c) # Pre-compile shaders for certain samples. if (CF_CUTE_SHADERC) @@ -117,3 +127,5 @@ add_custom_command(TARGET import_spritesheet PRE_BUILD COMMAND ${CMAKE_COMMAND} add_custom_command(TARGET pivot PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/pivot_data $/pivot_data) add_custom_command(TARGET scratch PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../assets/CF_Logo_Pixel.png $/app_icon.png) add_custom_command(TARGET 9_slice PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/9_slice_data $/9_slice_data) +add_custom_command(TARGET hrc PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/hrc_data $/hrc_data) +add_custom_command(TARGET customsprite PRE_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/custom_sprite_data $/custom_sprite_data) diff --git a/samples/canvas_readback.c b/samples/canvas_readback.c new file mode 100644 index 000000000..3041a56f4 --- /dev/null +++ b/samples/canvas_readback.c @@ -0,0 +1,136 @@ +#include + +#define NUM_CIRCLES 12 + +typedef struct Circle +{ + CF_V2 pos; + CF_V2 vel; + float radius; + CF_Color color; +} Circle; + +int main(int argc, char* argv[]) +{ + int w = 640; + int h = 480; + int options = CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT; + CF_Result result = cf_make_app("Canvas Readback", 0, 0, 0, w, h, options, argv[0]); + if (cf_is_error(result)) return -1; + + CF_Canvas offscreen = cf_make_canvas(cf_canvas_defaults(w, h)); + + // Seed bouncing circles with random positions, velocities, colors, radii. + CF_Rnd rnd = cf_rnd_seed(0xCAFE); + Circle circles[NUM_CIRCLES]; + CF_Color palette[] = { + cf_color_red(), + cf_color_green(), + cf_color_blue(), + cf_color_yellow(), + cf_color_orange(), + cf_color_purple(), + cf_color_cyan(), + cf_color_magenta(), + cf_color_white(), + }; + int palette_count = sizeof(palette) / sizeof(palette[0]); + float hw = w * 0.5f; + float hh = h * 0.5f; + for (int i = 0; i < NUM_CIRCLES; i++) { + circles[i].radius = cf_rnd_range_float(&rnd, 15.0f, 40.0f); + circles[i].pos = cf_v2(cf_rnd_range_float(&rnd, -hw + circles[i].radius, hw - circles[i].radius), + cf_rnd_range_float(&rnd, -hh + circles[i].radius, hh - circles[i].radius)); + circles[i].vel = cf_v2(cf_rnd_range_float(&rnd, -150.0f, 150.0f), + cf_rnd_range_float(&rnd, -150.0f, 150.0f)); + circles[i].color = palette[cf_rnd_range_int(&rnd, 0, palette_count - 1)]; + } + + bool readback_pending = false; + CF_Readback readback = { 0 }; + float flash_timer = 0.0f; + + while (cf_app_is_running()) { + cf_app_update(NULL); + float dt = CF_DELTA_TIME; + + // Animate circles (bounce off edges). + for (int i = 0; i < NUM_CIRCLES; i++) { + circles[i].pos.x += circles[i].vel.x * dt; + circles[i].pos.y += circles[i].vel.y * dt; + float r = circles[i].radius; + if (circles[i].pos.x - r < -hw) { circles[i].pos.x = -hw + r; circles[i].vel.x = -circles[i].vel.x; } + if (circles[i].pos.x + r > hw) { circles[i].pos.x = hw - r; circles[i].vel.x = -circles[i].vel.x; } + if (circles[i].pos.y - r < -hh) { circles[i].pos.y = -hh + r; circles[i].vel.y = -circles[i].vel.y; } + if (circles[i].pos.y + r > hh) { circles[i].pos.y = hh - r; circles[i].vel.y = -circles[i].vel.y; } + } + + // Draw circles to offscreen canvas. + for (int i = 0; i < NUM_CIRCLES; i++) { + cf_draw_push_color(circles[i].color); + cf_draw_circle_fill2(circles[i].pos, circles[i].radius); + cf_draw_pop_color(); + } + + // Draw "Copied to clipboard!" text briefly after capture. + cf_draw_push_color(cf_color_white()); + const char* text = NULL; + if (flash_timer > 0.0f) { + flash_timer -= dt; + text = "Copied to clipboard!"; + } else { + text = "Press space to capture a screenshot."; + } + cf_draw_text(text, cf_v2(-cf_text_width(text, -1)*0.5f, 0), -1); + cf_draw_pop_color(); + + // Perform actual render to canvas. This captures our prior drawable commands. + cf_render_to(offscreen, true); + + // Draw offscreen canvas to screen. + cf_draw_canvas(offscreen, cf_v2(0, 0), cf_v2((float)w, (float)h)); + + // If space just pressed: initiate readback. + if (cf_key_just_pressed(CF_KEY_SPACE) && !readback_pending) { + readback = cf_canvas_readback(offscreen); + readback_pending = true; + } + + // If pending readback is ready, encode to PNG and copy to clipboard. + if (readback_pending && cf_readback_ready(readback)) { + int pixel_size = cf_readback_size(readback); + void* pixels = cf_alloc(pixel_size); + cf_readback_data(readback, pixels, pixel_size); + cf_destroy_readback(readback); + readback_pending = false; + + // Encode to PNG. + CF_Image img; + img.w = w; + img.h = h; + img.pix = (CF_Pixel*)pixels; + void* png_data = NULL; + int png_size = 0; + CF_Result save_result = cf_image_save_png_to_memory(&img, &png_data, &png_size); + cf_free(pixels); + + if (!cf_is_error(save_result) && png_data) { + // Copy PNG to clipboard. + const char* types[] = { "image/png" }; + cf_clipboard_set_data(png_data, png_size, types, 1); + cf_free(png_data); + flash_timer = 2.0f; + } + } + + cf_app_draw_onto_screen(true); + } + + if (readback_pending) { + cf_destroy_readback(readback); + } + cf_destroy_canvas(offscreen); + cf_destroy_app(); + + return 0; +} diff --git a/samples/clay.c b/samples/clay.c index 621d0bf61..133426472 100644 --- a/samples/clay.c +++ b/samples/clay.c @@ -720,7 +720,7 @@ static void handle_clay_core_commands(const Clay_RenderCommand* command) case CLAY_RENDER_COMMAND_TYPE_IMAGE: { CF_Sprite sprite = *(CF_Sprite*)command->renderData.image.imageData; - CF_V2 pivot = sprite.pivots[sprite.frame_index]; + CF_V2 pivot = cf_sprite_pivot(&sprite); sprite.scale.x = command->boundingBox.width / (float)sprite.w; sprite.scale.y = command->boundingBox.height / (float)sprite.h; sprite.transform.p.x = command->boundingBox.x + pivot.x + (float)sprite.w * sprite.scale.x * 0.5f; diff --git a/samples/clay_ui_animations.c b/samples/clay_ui_animations.c index 0dea2ae17..2825bbd5a 100644 --- a/samples/clay_ui_animations.c +++ b/samples/clay_ui_animations.c @@ -657,7 +657,7 @@ static void handle_clay_core_commands(const Clay_RenderCommand* command) case CLAY_RENDER_COMMAND_TYPE_IMAGE: { CF_Sprite sprite = *(CF_Sprite*)command->renderData.image.imageData; - CF_V2 pivot = sprite.pivots[sprite.frame_index]; + CF_V2 pivot = cf_sprite_pivot(&sprite); sprite.scale.x = command->boundingBox.width / (float)sprite.w; sprite.scale.y = command->boundingBox.height / (float)sprite.h; sprite.transform.p.x = command->boundingBox.x + pivot.x + (float)sprite.w * sprite.scale.x * 0.5f; diff --git a/samples/compute.c b/samples/compute.c new file mode 100644 index 000000000..986c2d3a7 --- /dev/null +++ b/samples/compute.c @@ -0,0 +1,153 @@ +// Compute shader sample. +// +// A compute shader writes an animated procedural pattern to a texture each frame. +// A fullscreen quad then displays the texture. If the compute dispatch works, you +// see a colorful moving gradient. If it fails, the screen stays black. + +#include + +#define TEX_W 256 +#define TEX_H 256 + +#define STR(X) #X + +// Compute shader: writes RGBA pixels to a storage texture. +// Each invocation handles one pixel. The pattern is a simple color wash +// based on pixel position and a time uniform. +const char* s_compute = STR( + layout (set = 1, binding = 0, rgba8) uniform writeonly image2D u_output; + + layout (set = 2, binding = 0) uniform uniform_block { + float u_time; + }; + + layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + + void main() + { + ivec2 coord = ivec2(gl_GlobalInvocationID.xy); + ivec2 size = imageSize(u_output); + if (coord.x >= size.x || coord.y >= size.y) return; + + vec2 uv = vec2(coord) / vec2(size); + + // Animated diagonal gradient with shifting hue. + float t = u_time * 0.5; + float r = 0.5 + 0.5 * sin(uv.x * 6.28 + t); + float g = 0.5 + 0.5 * sin(uv.y * 6.28 + t * 1.3 + 2.0); + float b = 0.5 + 0.5 * sin((uv.x + uv.y) * 3.14 + t * 0.7 + 4.0); + + imageStore(u_output, coord, vec4(r, g, b, 1.0)); + } +); + +// Vertex shader: takes position + UV from mesh. +const char* s_vs = STR( + layout (location = 0) in vec2 in_pos; + layout (location = 1) in vec2 in_uv; + + layout (location = 0) out vec2 v_uv; + + void main() + { + v_uv = in_uv; + gl_Position = vec4(in_pos, 0.0, 1.0); + } +); + +// Fragment shader: sample the compute output texture. +const char* s_fs = STR( + layout (location = 0) in vec2 v_uv; + layout (location = 0) out vec4 result; + + layout (set = 2, binding = 0) uniform sampler2D u_image; + + void main() + { + result = texture(u_image, v_uv); + } +); + +typedef struct Vertex +{ + CF_V2 pos; + CF_V2 uv; +} Vertex; + +int main(int argc, char* argv[]) +{ + CF_Result result = cf_make_app("Compute Sample", 0, 0, 0, 512, 512, CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT, argv[0]); + if (cf_is_error(result)) return -1; + + // Create the compute output texture with both sampler and compute storage write usage. + CF_TextureParams tex_params = cf_texture_defaults(TEX_W, TEX_H); + tex_params.usage = CF_TEXTURE_USAGE_SAMPLER_BIT | CF_TEXTURE_USAGE_COMPUTE_STORAGE_WRITE_BIT; + tex_params.filter = CF_FILTER_LINEAR; + CF_Texture compute_tex = cf_make_texture(tex_params); + + // Create the compute shader. + CF_ComputeShader compute_shader = cf_make_compute_shader_from_source(s_compute); + + // Create a material for compute uniforms. + CF_Material compute_material = cf_make_material(); + + // Create the graphics shader for display. + CF_Shader display_shader = cf_make_shader_from_source(s_vs, s_fs); + + // Create a material for the display shader (holds the texture binding). + CF_Material display_material = cf_make_material(); + cf_material_set_texture_fs(display_material, "u_image", compute_tex); + + // Fullscreen quad as two triangles. + Vertex verts[6] = { + { cf_v2(-1, -1), cf_v2(0, 1) }, + { cf_v2( 1, -1), cf_v2(1, 1) }, + { cf_v2( 1, 1), cf_v2(1, 0) }, + { cf_v2(-1, -1), cf_v2(0, 1) }, + { cf_v2( 1, 1), cf_v2(1, 0) }, + { cf_v2(-1, 1), cf_v2(0, 0) }, + }; + + CF_VertexAttribute attrs[2] = { 0 }; + attrs[0].name = "in_pos"; + attrs[0].format = CF_VERTEX_FORMAT_FLOAT2; + attrs[0].offset = CF_OFFSET_OF(Vertex, pos); + attrs[1].name = "in_uv"; + attrs[1].format = CF_VERTEX_FORMAT_FLOAT2; + attrs[1].offset = CF_OFFSET_OF(Vertex, uv); + CF_Mesh mesh = cf_make_mesh(sizeof(verts), attrs, CF_ARRAY_SIZE(attrs), sizeof(Vertex)); + cf_mesh_update_vertex_data(mesh, verts, 6); + + float time = 0.0f; + + while (cf_app_is_running()) { + cf_app_update(NULL); + time += CF_DELTA_TIME; + + // --- Compute pass: write the procedural pattern into compute_tex. --- + cf_material_set_uniform_cs(compute_material, "u_time", &time, CF_UNIFORM_TYPE_FLOAT, 1); + + CF_ComputeDispatch dispatch = cf_compute_dispatch_defaults(TEX_W / 16, TEX_H / 16, 1); + dispatch.rw_textures = &compute_tex; + dispatch.rw_texture_count = 1; + cf_dispatch_compute(compute_shader, compute_material, dispatch); + + // --- Graphics pass: display the texture as a fullscreen quad. --- + cf_apply_canvas(cf_app_get_canvas(), true); + cf_apply_mesh(mesh); + cf_apply_shader(display_shader, display_material); + cf_draw_elements(); + + cf_app_draw_onto_screen(false); + } + + cf_destroy_compute_shader(compute_shader); + cf_destroy_shader(display_shader); + cf_destroy_material(compute_material); + cf_destroy_material(display_material); + cf_destroy_mesh(mesh); + cf_destroy_texture(compute_tex); + cf_destroy_app(); + + return 0; +} diff --git a/samples/custom_sprite.c b/samples/custom_sprite.c new file mode 100644 index 000000000..193615235 --- /dev/null +++ b/samples/custom_sprite.c @@ -0,0 +1,86 @@ +#include + +#define MAX_FIREWORKS 256 + +typedef struct Firework +{ + CF_Sprite sprite; + CF_V2 position; +} Firework; + +static Firework fireworks[MAX_FIREWORKS]; +static int firework_count; + +int main(int argc, char* argv[]) +{ + cf_make_app("Custom Sprite", 0, 0, 0, 640, 480, CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT, argv[0]); + + // Mount data directory using full path from base directory. + char* path = cf_path_normalize(cf_fs_get_base_directory()); + sappend(path, "/custom_sprite_data"); + cf_fs_mount(path, "/", true); + sfree(path); + + // Load PNG frames. + CF_Png bullet_pngs[3], explosion_pngs[5]; + cf_custom_sprite_load_png("/bullet_pop1.png", &bullet_pngs[0]); + cf_custom_sprite_load_png("/bullet_pop2.png", &bullet_pngs[1]); + cf_custom_sprite_load_png("/bullet_pop3.png", &bullet_pngs[2]); + for (int i = 0; i < 5; i++) { + char path[32]; + snprintf(path, sizeof(path), "/explosion%d.png", i + 1); + cf_custom_sprite_load_png(path, &explosion_pngs[i]); + } + + // Create animations. + float bullet_delays[] = { 0.05f, 0.05f, 0.05f }; + float explosion_delays[] = { 0.05f, 0.05f, 0.05f, 0.05f, 0.05f }; + const CF_Animation* bullet_anim = cf_make_custom_sprite_animation("bullet_pop", bullet_pngs, 3, bullet_delays, 3); + const CF_Animation* explosion_anim = cf_make_custom_sprite_animation("explosion", explosion_pngs, 5, explosion_delays, 5); + + // Animation table + template sprite. + const CF_Animation* anims[] = { bullet_anim, explosion_anim }; + const CF_AnimationTable* table = cf_make_custom_sprite_animation_table("firework", anims, 2); + CF_Sprite fireworks_sprite = cf_make_custom_sprite("firework", table); + + // Main loop. + CF_Rnd rnd = cf_rnd_seed(0); + float spawn_timer = 0; + + while (cf_app_is_running()) { + cf_app_update(NULL); + float dt = CF_DELTA_TIME; + + // Spawn fireworks periodically. + spawn_timer += dt; + if (spawn_timer >= 0.1f && firework_count < MAX_FIREWORKS) { + spawn_timer = 0; + Firework* fw = &fireworks[firework_count++]; + fw->sprite = fireworks_sprite; + fw->position = cf_v2(cf_rnd_range_float(&rnd, -200, 200), cf_rnd_range_float(&rnd, -100, 100)); + const char* anim = cf_rnd_range_int(&rnd, 0, 1) ? "explosion" : "bullet_pop"; + cf_sprite_play(&fw->sprite, anim); + fw->sprite.loop = false; + } + + // Update, draw, and cull finished fireworks. + cf_draw_scale(2, 2); + for (int i = 0; i < firework_count; ) { + Firework* fw = &fireworks[i]; + cf_sprite_update(&fw->sprite); + fw->sprite.transform.p = fw->position; + cf_draw_sprite(&fw->sprite); + + if (fw->sprite.finished) { + fireworks[i] = fireworks[--firework_count]; + } else { + i++; + } + } + + cf_app_draw_onto_screen(true); + } + + cf_destroy_app(); + return 0; +} diff --git a/samples/custom_sprite_data/bullet_pop1.png b/samples/custom_sprite_data/bullet_pop1.png new file mode 100644 index 000000000..6efc469d0 Binary files /dev/null and b/samples/custom_sprite_data/bullet_pop1.png differ diff --git a/samples/custom_sprite_data/bullet_pop2.png b/samples/custom_sprite_data/bullet_pop2.png new file mode 100644 index 000000000..0eb3325ba Binary files /dev/null and b/samples/custom_sprite_data/bullet_pop2.png differ diff --git a/samples/custom_sprite_data/bullet_pop3.png b/samples/custom_sprite_data/bullet_pop3.png new file mode 100644 index 000000000..821a9cc96 Binary files /dev/null and b/samples/custom_sprite_data/bullet_pop3.png differ diff --git a/samples/custom_sprite_data/explosion1.png b/samples/custom_sprite_data/explosion1.png new file mode 100644 index 000000000..4b8e80c2f Binary files /dev/null and b/samples/custom_sprite_data/explosion1.png differ diff --git a/samples/custom_sprite_data/explosion2.png b/samples/custom_sprite_data/explosion2.png new file mode 100644 index 000000000..a97f335e4 Binary files /dev/null and b/samples/custom_sprite_data/explosion2.png differ diff --git a/samples/custom_sprite_data/explosion3.png b/samples/custom_sprite_data/explosion3.png new file mode 100644 index 000000000..189a756db Binary files /dev/null and b/samples/custom_sprite_data/explosion3.png differ diff --git a/samples/custom_sprite_data/explosion4.png b/samples/custom_sprite_data/explosion4.png new file mode 100644 index 000000000..91801916e Binary files /dev/null and b/samples/custom_sprite_data/explosion4.png differ diff --git a/samples/custom_sprite_data/explosion5.png b/samples/custom_sprite_data/explosion5.png new file mode 100644 index 000000000..ea56f3a4c Binary files /dev/null and b/samples/custom_sprite_data/explosion5.png differ diff --git a/samples/docs_parser.cpp b/samples/docs_parser.cpp deleted file mode 100644 index e3ca726af..000000000 --- a/samples/docs_parser.cpp +++ /dev/null @@ -1,857 +0,0 @@ -#include -using namespace Cute; - -#include -#include - -// Parses all the Cute headers and generates documentation pages in .md format. - -static void panic(String msg) -{ - printf("ERROR: %s\n", msg.c_str()); - exit(-1); -} - -const char* g_relative_path; -const char* get_relative_path() -{ - if (!g_relative_path) { - g_relative_path = "../docs"; - - // Try and search up a couple directories for the docs folder. - // On different cmake generators we can get placed into various folder depths. - CF_Path path = cf_fs_get_working_directory(); - path.normalize(); - path.pop(); - path.add("/docs"); - if (!std::filesystem::is_directory(path.c_str())) { - path.pop(2); - path.add("/docs"); - g_relative_path = "../../docs"; - if (!std::filesystem::is_directory(path.c_str())) { - panic("Can't find the /docs folder."); - } - } - } - - return g_relative_path; -} - -static bool s_is_space(int cp) -{ - switch (cp) { - case ' ': - case '\n': - case '\t': - case '\v': - case '\f': - case '\r': return true; - default: return false; - } -} - -enum DocType -{ - DOC_EMPTY, - DOC_ENUM, - DOC_FUNCTION, - DOC_STRUCT, -}; - -struct Doc -{ - DocType type; - CF_Path path; - String web_category; - String title; - String file; - String brief; - int this_index = -1; - String signature; - Array param_names; - Array param_briefs; - Array enum_entries; - Array enum_entry_briefs; - Array member_briefs; - Array member_functions; - Array members; - String return_value; - String example_brief; - String example; - String remarks; - Array related; - - void emit_title(FILE* fp); - void emit_brief(FILE* fp); - void emit_signature(FILE* fp); - void emit_members(FILE* fp); - void emit_member_function_links(FILE* fp); - void emit_params(FILE* fp); - void emit_enum_entries(FILE* fp); - void emit_return(FILE* fp); - void emit_example(FILE* fp); - void emit_remarks(FILE* fp); - void emit_related(FILE* fp); -}; - -struct State -{ - // Header parsing state. - const char* in; - const char* end; - String token; - String file; - Map categories; - Map> category_index_lists; - - // Stored strings for output docs file. - Doc doc; - Array docs; - Map page_to_doc_index; - - void flush_doc() - { - CF_Path path = get_relative_path(); - path.add(doc.web_category); - String title = doc.title; - title.to_lower().replace(" ", "_").append(".md"); - path.add(title); - doc.path = path; - doc.file = file; - if (page_to_doc_index.has(sintern(title))) { - Doc& other_doc = docs[page_to_doc_index.get(sintern(title))]; - panic(String::fmt("Tried to add a duplicate page for %s (found in file %s, previously seen in file %s).", title.c_str(), doc.file.c_str(), other_doc.file.c_str())); - } - page_to_doc_index.insert(sintern(title), docs.count()); - docs.add(doc); - doc = Doc(); - } - - int get_doc_index(String title) - { - title.to_lower().replace(" ", "_").append(".md"); - int* index = page_to_doc_index.try_find(sintern(title)); - return index ? *index : -1; - } - - bool doc_has_link(String title) - { - return get_doc_index(title) != -1; - } - - String doc_get_link(String title) - { - int index = get_doc_index(title); - String link = docs[index].path.c_str(); - link.replace(get_relative_path(), ""); - return link; - } - - bool has_doc(const char* file) - { - return page_to_doc_index.has(sintern(file)); - } - - void clear() { token.clear(); in = end = NULL; } - bool done() { return in >= end; } - void append(int ch) { token.append(ch); } - void ltrim() { while (!done()) { int cp = *in; if (s_is_space(cp)) ++in; else break; } } - int next() { int cp; in = cf_decode_UTF8(in, &cp); return cp; } - int peek() { int cp; cf_decode_UTF8(in, &cp); return cp; } - void skip() { int cp; in = cf_decode_UTF8(in, &cp); } - bool expect(int ch) { int cp = next(); if (cp != ch) { return false; } return true; } - bool try_next(int ch) { int cp; const char* next = cf_decode_UTF8(in, &cp); if (cp == ch) { in = next; return true; } return false; } -}; - -State state; -State* s = &state; - -String linkify(String text, String scan, bool ticks = true) -{ - if (s->doc_has_link(scan)) { - String link = s->doc_get_link(scan); - String coded_link = String::fmt("[%s](..%s)", scan.c_str(), link.c_str()); - scan = String::fmt(ticks ? "`%s`" : "%s", scan.c_str()); - text.replace(scan, coded_link); - } - return text; -} - -void Doc::emit_title(FILE* fp) -{ - fprintf(fp, "[//]: # (This file is automatically generated by Cute Framework's docs parser.)\n"); - fprintf(fp, "[//]: # (Do not edit this file by hand!)\n"); - fprintf(fp, "[//]: # (See: https://github.com/RandyGaul/cute_framework/blob/master/samples/docs_parser.cpp)\n"); - fprintf(fp, "# %s\n\n", title.c_str()); - String link = linkify(web_category, web_category, false); - fprintf(fp, "Category: [%s](../api_reference.md#%s) \n", web_category.c_str(), link.to_lower().c_str()); - fprintf(fp, "GitHub: [%s](https://github.com/RandyGaul/cute_framework/blob/master/include/%s) \n---\n\n", file.c_str(), file.c_str()); -} - -void Doc::emit_brief(FILE* fp) -{ - fprintf(fp, "%s\n\n", brief.c_str()); -} - -void Doc::emit_signature(FILE* fp) -{ - fprintf(fp, "```cpp\n%s\n```\n\n", signature.c_str()); -} - -void Doc::emit_members(FILE* fp) -{ - if (members.count()) { - fprintf(fp, "Struct Members | Description\n--- | ---\n"); - for (int i = 0; i < members.count(); ++i) { - fprintf(fp, "`%s` | %s\n", members[i].c_str(), member_briefs[i].c_str()); - } - fprintf(fp, "\n"); - } -} - -void Doc::emit_member_function_links(FILE* fp) -{ - // This function is not finished. - if (member_functions.count()) { - fprintf(fp, "Functions | Description\n--- | ---\n"); - for (int i = 0; i < member_functions.count(); ++i) { - int index = member_functions[i]; - Doc& doc = s->docs[index]; - // TODO - Linkify title spot here. - fprintf(fp, "%s | %s\n", doc.title.c_str(), doc.brief.c_str()); - } - fprintf(fp, "\n"); - } -} - -void Doc::emit_params(FILE* fp) -{ - if (param_names.count()) { - fprintf(fp, "Parameters | Description\n--- | ---\n"); - for (int i = 0; i < param_names.count(); ++i) { - fprintf(fp, "%s | %s\n", param_names[i].c_str(), param_briefs[i].c_str()); - } - fprintf(fp, "\n"); - } -} - -void Doc::emit_enum_entries(FILE* fp) -{ - fprintf(fp, "## Values\n\n"); - fprintf(fp, "Enum | Description\n--- | ---\n"); - for (int i = 0; i < enum_entries.count(); ++i) { - fprintf(fp, "%s | %s\n", enum_entries[i].c_str(), enum_entry_briefs[i].c_str()); - } - fprintf(fp, "\n"); -} - -void Doc::emit_return(FILE* fp) -{ - if (return_value.len()) { - fprintf(fp, "## Return Value\n\n%s\n\n", return_value.c_str()); - } -} - -void Doc::emit_example(FILE* fp) -{ - if (example.len()) { - fprintf(fp, "## Code Example\n\n"); - if (example_brief.len()) { - fprintf(fp, "%s\n\n", example_brief.c_str()); - } - fprintf(fp, "```cpp%s```\n\n", example.c_str()); - } -} - -void Doc::emit_remarks(FILE* fp) -{ - if (remarks.len()) { - fprintf(fp, "## Remarks\n\n"); - fprintf(fp, "%s\n\n", remarks.c_str()); - } -} - -void Doc::emit_related(FILE* fp) -{ - if (related.count()) { - fprintf(fp, "## Related Pages\n\n"); - for (int i = 0; i < related.count(); ++i) { - fprintf(fp, "%s\n", related[i].c_str()); - } - } -} - -void parse_token() -{ - s->token.clear(); - while (!s->done()) { - int cp = s->next(); - if (s_is_space(cp)) { - if (s->token.len()) { - return; - } - } else { - s->append(cp); - } - } -} - -String parse_single_comment_line() -{ - String line; - if (s->try_next(' ')) { - if (s->try_next('*')) { - if (s->try_next('/')) { - s->in--; - s->in--; - return line; - } - } - } - while (!s->done()) { - int cp = s->peek(); - if (cp == '\n') { - break; - } else { - s->skip(); - line.add(cp); - } - } - return line.trim(); -} - -String parse_paragraph(bool example = false) -{ - String paragraph; - while (!s->done()) { - int cp = s->peek(); - if (cp == '\n') { - paragraph.append(cp); - s->skip(); - if (s->try_next(' ')) { - if (s->try_next('*')) { - if (s->try_next('/')) { - s->in--; - s->in--; - break; - } else { - s->try_next(' '); - } - } - } - } else if (cp == '@') { - break; - } else if (cp == '*') { - s->skip(); - if (s->try_next('/')) { - s->in--; - s->in--; - break; - } - } else { - if (cp != '\r') { - paragraph.append(cp); - } - s->skip(); - } - } - return !example ? paragraph.trim().replace("\n ", "\n") : paragraph; -} - -void parse_command() -{ - if (s->token == "@brief") { - s->doc.brief = parse_paragraph(); - } else if (s->token == "@example") { - s->doc.example_brief = parse_single_comment_line(); - s->doc.example = parse_paragraph(true).replace("\n ", "\n"); - } else if (s->token == "@remarks") { - s->doc.remarks = parse_paragraph(); - } else if (s->token == "@related") { - s->doc.related = parse_paragraph().split(' '); - } else if (s->token == "@param") { - parse_token(); - s->doc.param_names.add(s->token); - s->doc.param_briefs.add(parse_paragraph()); - } else if (s->token == "@return") { - s->doc.return_value = parse_paragraph(); - } else if (s->token == "@category") { - s->doc.web_category = parse_paragraph(); - const char* category = sintern(s->doc.web_category); - if (!s->categories.has(category)) { - s->categories.insert(category, category); - } - } else { - panic(String::fmt("Found unrecognized command %s.", s->token.c_str())); - } -} - -void parse_enum() -{ - s->doc.type = DOC_ENUM; - s->doc.title = parse_paragraph(); - while (!s->done()) { - parse_token(); - if (s->token.first() == '@') { - parse_command(); - } else if (s->token == "*/") { - break; - } - } - while (!s->done()) { - parse_token(); - if (s->token.first() == '@') { - if (s->token == "@entry") { - s->doc.enum_entry_briefs.add(parse_paragraph()); - } else if (s->token == "@end") { - break; - } else { - panic(String::fmt("Expected to find @entry or @end, but instead found %s.", s->token.c_str())); - } - } else if (s->token.begins_with("CF_ENUM(")) { - s->token.replace("CF_ENUM(", ""); - s->token.pop(); // Remove ',' - s->doc.enum_entries.add(s->token); - } - } - if (s->doc.enum_entries.count() != s->doc.enum_entry_briefs.count()) { - int a = s->doc.enum_entries.count(); - int b = s->doc.enum_entry_briefs.count(); - panic(String::fmt("Enum entries count %d did not match entry description count %d.", a, b)); - } - s->flush_doc(); -} - -String parse_line() -{ - String line; - s->try_next('\n'); - while (!s->done()) { - int cp = s->peek(); - if (cp == '\n') { - break; - } else { - s->skip(); - line.add(cp); - } - } - return line.trim(); -} - -void parse_comment_block() -{ - s->doc.title = parse_paragraph(); - while (!s->done()) { - parse_token(); - if (s->token.first() == '@') { - parse_command(); - } else if (s->token == "*/") { - break; - } - } -} - -void parse_function() -{ - s->doc.type = DOC_FUNCTION; - parse_comment_block(); - s->doc.signature = parse_line().replace("CF_API ", "").replace("CF_CALL ", "").replace("CF_INLINE ", ""); - int index = s->doc.signature.find(" {"); - if (index != -1) { - s->doc.signature.set_len(index); - } - s->flush_doc(); -} - -void parse_struct() -{ - s->doc.type = DOC_STRUCT; - parse_comment_block(); - int doc_index = s->docs.count(); - s->flush_doc(); - Doc& doc = s->docs.last(); - while (!s->done()) { - parse_token(); - if (s->token.first() == '@') { - if (s->token == "@member") { - String brief = parse_paragraph(); - parse_token(); // Skip "*/" - String member = parse_line().replace("CF_INLINE ", "").replace(";", ""); - doc.member_briefs.add(brief); - doc.members.add(member); - } else if (s->token == "@function") { - doc.member_functions.add(s->docs.count()); - parse_function(); - s->docs.last().this_index = doc_index; - } else if (s->token == "@end") { - break; - } else { - panic(String::fmt("Found unexpected command %s while parsing a struct, @member, @end, or @function.", s->token.c_str())); - } - } - } -} - -void parse(String header) -{ - size_t sz = 0; - char* in = fs_read_entire_file_to_memory_and_nul_terminate(header, &sz); - s->clear(); - s->in = in; - s->end = in + sz; - s->file = header; - - while (!s->done()) { - parse_token(); - if (s->token.first() == '@') { - if (s->token == "@enum") { - parse_enum(); - } else if (s->token == "@function") { - parse_function(); - } else if (s->token == "@struct") { - parse_struct(); - } else { - panic(String::fmt("Found unexpected command %s, expected @enum, @function, or @struct.", s->token.c_str())); - } - } - } -} - -// Scans text for `things in tick marks like this`. It will replace the tick mark text -// with a page link if an appropriate page link is found based on the title of the page. -String auto_generate_links(String text) -{ - String scan; - - bool found = false; - for (int i = 0; i < text.len(); ++i) { - char ch = text[i]; - if (found) { - if (ch != '`') { - scan.add(ch); - } else { - text = linkify(text, scan); - found = false; - scan.clear(); - } - } else { - if (ch == '`') { - found = true; - } - } - } - - return text; -} - -void save_api_reference_links(FILE* fp, const char* category, Array pages, const char* type, const Map>& related) -{ - if (pages.size()) { - fprintf(fp, "### %s\n", type); - for (int i = 0; i < pages.count(); ++i) { - String page = pages[i]; - String page_lower = page; - page_lower.to_lower().replace(" ", "_"); - fprintf(fp, "- [%s](%s/%s.md)\n", page.c_str(), category, page_lower.c_str()); - } - fprintf(fp, "\n\n"); - } - const Array related_links = related.find(category); - if (related_links.size()) { - fprintf(fp, "### Related Reading\n"); - for (int i = 0; i < related_links.size(); ++i) { - fprintf(fp, "%s\n", related_links[i]); - } - fprintf(fp, "\n"); - } -} - -int main(int argc, char* argv[]) -{ - // Mount the headers folder as "/". - cf_fs_init(argv[0]); - CF_Path path = cf_fs_get_base_directory(); - path = path.normalize(); - path.popn(2); - path.add("/include"); - cf_fs_mount(path.c_str(), "/", true); - printf("Mounted %s as /.\n", path.c_str()); - - // Mount docs folder as "/docs". - path = cf_fs_get_base_directory(); - path = path.normalize(); - path.popn(2); - path.add("/docs"); - cf_fs_mount(path.c_str(), "/docs", true); - printf("Mounted %s as /docs.\n", path.c_str()); - - // Allow us to freely write in the project directory. - path = cf_fs_get_base_directory(); - path = path.normalize(); - path.popn(2); - cf_fs_set_write_directory(path.c_str()); - printf("Set write directory to %s.\n", path.c_str()); - - // Parse each header into docs. - CF_Directory headers = CF_Directory::open(""); - for (const char* file = headers.next(); file; file = headers.next()) { - if (spext_equ(file, ".h")) { - parse(file); - } - } - - // Auto-generate links throughout the text. - for (int i = 0; i < s->docs.count(); ++i) { - Doc& doc = s->docs[i]; - doc.brief = auto_generate_links(doc.brief); - for (int i = 0; i < doc.param_briefs.count(); ++i) { - doc.param_briefs[i] = auto_generate_links(doc.param_briefs[i]); - } - for (int i = 0; i < doc.enum_entry_briefs.count(); ++i) { - doc.enum_entry_briefs[i] = auto_generate_links(doc.enum_entry_briefs[i]); - } - for (int i = 0; i < doc.member_briefs.count(); ++i) { - doc.member_briefs[i] = auto_generate_links(doc.member_briefs[i]); - } - doc.return_value = auto_generate_links(doc.return_value); - doc.example_brief = auto_generate_links(doc.example_brief); - doc.example = auto_generate_links(doc.example); - doc.remarks = auto_generate_links(doc.remarks); - for (int i = 0; i < doc.related.count();) { - String related = doc.related[i]; - if (related == doc.title) { - doc.related.unordered_remove(i); - continue; - } - doc.related[i] = linkify(related, related, false); - doc.related[i].append(" "); - ++i; - } - } - - // Save each doc file. - for (int i = 0; i < s->docs.count(); ++i) { - Doc& doc = s->docs[i]; - { - CF_Path path = doc.path; - path.pop(); - std::filesystem::create_directory(path.c_str()); - } - FILE* fp = fopen(doc.path.c_str(), "wb"); - CF_ASSERT(fp); - const char* category = sintern(doc.web_category.c_str()); - if (!s->category_index_lists.has(category)) { - s->category_index_lists.insert(category); - } - Array& categories = s->category_index_lists.find(category); - categories.add(sintern(doc.title)); - - if (doc.type == DOC_ENUM) { - doc.emit_title(fp); - doc.emit_brief(fp); - doc.emit_enum_entries(fp); - doc.emit_example(fp); - doc.emit_remarks(fp); - doc.emit_related(fp); - } else if (doc.type == DOC_FUNCTION) { - doc.emit_title(fp); - doc.emit_brief(fp); - doc.emit_signature(fp); - doc.emit_params(fp); - doc.emit_return(fp); - doc.emit_example(fp); - doc.emit_remarks(fp); - doc.emit_related(fp); - } else if (doc.type == DOC_STRUCT) { - doc.emit_title(fp); - doc.emit_brief(fp); - doc.emit_members(fp); - doc.emit_member_function_links(fp); - doc.emit_example(fp); - doc.emit_remarks(fp); - doc.emit_related(fp); - } - printf("Wrote %s\n", doc.path.c_str()); - fclose(fp); - } - - // Generate api_reference.md file. - { - CF_Path path = get_relative_path(); - path += "api_reference.md"; - FILE* fp = fopen(path, "wb"); - fprintf(fp, "[//]: # (This file is automatically generated by Cute Framework's docs parser.)\n"); - fprintf(fp, "[//]: # (Do not edit this file by hand!)\n"); - fprintf(fp, "[//]: # (See: https://github.com/RandyGaul/cute_framework/blob/master/samples/docs_parser.cpp)\n"); - fprintf(fp, "# API Reference\n\n"); - fprintf(fp, "This is a list of all functions in Cute Framework organized by categories. This is great for users that want to see all the available functionality laid out plainly.\n\n\n"); - - Map> related; - related.add("allocator", { - "- [Allocator](../topics/allocator)", - }); - related.add("app", { - "- [Application Window](../topics/application_window.md)", - "- [Game Loop and Time](../topics/game_loop_and_time.md)", - }); - related.add("array", { - "- [Data Structures](../topics/data_structures.md)", - }); - related.add("atomics", { - "- [Atomics](../topics/atomics.md)", - }); - related.add("base64", { - "- [Serialization](../topics/serialization.md)", - }); - related.add("collision", { - "- [Collision](../topics/collision.md)", - }); - related.add("coroutine", { - "- [Coroutines](../topics/coroutines.md)", - }); - related.add("draw", { - "- [Drawing](../topics/drawing.md)", - }); - related.add("ecs", { - "- [Entity Component System](../topics/entity_component_system.md)", - }); - related.add("file", { - "- [Virtual File System](../topics/virtual_file_system.md)", - }); - related.add("graphics", { - "- [Low Level Graphics](../topics/low_level_graphics.md)", - }); - related.add("haptic", { - "- [Input](../topics/input.md)", - }); - related.add("hash", { - "- [Data Structures](../topics/data_structures.md)", - }); - related.add("input", { - "- [Input](../topics/input.md)", - }); - related.add("list", { - "- [Data Structures](../topics/data_structures.md)", - }); - related.add("math", { - "- [Collision](../topics/collision.md)", - }); - related.add("multithreading", { - "- [Atomics](../topics/atomics.md)", - "- [Multithreading](../topics/multithreading.md)", - }); - related.add("net", { - "- [Networking](../topics/networking.md)", - }); - related.add("path", { - "- [Virtual File System](../topics/virtual_file_system.md)", - }); - related.add("pathfinding", { - "- [Path Finding](../topics/pathfinding.md)", - }); - related.add("random", { - "- [Random Numbers](../topics/random_numbers.md)", - }); - related.add("serialization", { - "- [Serialization](../topics/serialization.md)", - }); - related.add("sprite", { - "- [Drawing](../topics/drawing.md)", - }); - related.add("string", { - "- [Strings](../topics/strings.md)", - }); - related.add("time", { - "- [Game Loop and Time](../topics/game_loop_and_time.md)", - }); - related.add("text", { - "- [Drawing](../topics/drawing.md)", - "- [Input](../topics/input.md)", - }); - related.add("web", { - "- [Networking](../topics/networking.md)", - }); - - s->categories.sort_by_items([](const char* a, const char* b) { return sicmp(a, b) < 0; }); - for (int i = 0; i < s->categories.count(); ++i) { - const char* category = s->categories.items()[i]; - Array index_list = s->category_index_lists.find(category); - fprintf(fp, "## %s\n\n", category); - - // Save functions.md. - bool has_functions = false; - { - Array functions; - for (int i = 0; i < index_list.size(); ++i) { - const char* title = index_list[i]; - if (s->docs[s->get_doc_index(title)].type == DOC_FUNCTION) { - functions.add(title); - } - } - has_functions = functions.count() > 0; - std::sort(functions.data(), functions.data() + functions.count(), [](const char* a, const char* b) { return sicmp(a, b) < 0; }); - save_api_reference_links(fp, category, functions, "functions", related); - } - - // Save structs.md. - bool has_structs = false; - { - Array structs; - for (int i = 0; i < index_list.size(); ++i) { - const char* title = index_list[i]; - if (s->docs[s->get_doc_index(title)].type == DOC_STRUCT) { - structs.add(title); - } - } - has_structs = structs.count() > 0; - std::sort(structs.data(), structs.data() + structs.count(), [](const char* a, const char* b) { return sicmp(a, b) < 0; }); - save_api_reference_links(fp, category, structs, "structs", related); - } - - // Save enums.md. - bool has_enums = false; - { - Array enums; - for (int i = 0; i < index_list.size(); ++i) { - const char* title = index_list[i]; - if (s->docs[s->get_doc_index(title)].type == DOC_ENUM) { - enums.add(title); - } - } - has_enums = enums.count() > 0; - std::sort(enums.data(), enums.data() + enums.count(), [](const char* a, const char* b) { return sicmp(a, b) < 0; }); - save_api_reference_links(fp, category, enums, "enums", related); - } - } - printf("Wrote %s\n", path.c_str()); - - fclose(fp); - } - -#if 1 - // Delete any old document files that are no longer used in the category folders. - { - for (int i = 0; i < s->categories.count(); ++i) { - const char* category = s->categories.keys()[i]; - CF_Path path = "/docs"; - path.add(category); - CF_Directory dir = CF_Directory::open(path); - const char* file; - while ((file = dir.next())) { - if (siequ(file, "enums.md") || siequ(file, "functions.md") || siequ(file, "structs.md")) { - continue; - } - if (!s->has_doc(file)) { - CF_Path filepath = path; - filepath += file; - fs_remove(filepath.c_str()); - } - } - } - } -#endif - - printf("docsparser: Success!\n"); - - return 0; -} diff --git a/samples/fetch_image.cpp b/samples/fetch_image.cpp index 3b73f9f65..c3cc0693a 100644 --- a/samples/fetch_image.cpp +++ b/samples/fetch_image.cpp @@ -13,10 +13,13 @@ int main(int argc, char *argv[]) CF_Sprite sprite = cf_make_demo_sprite(); sprite.scale = cf_mul_v2_f(sprite.scale, 3.0f); - - const char **names = (const char**)cf_hashtable_keys(sprite.animations); - int count = cf_hashtable_count(sprite.animations); - + + int count = cf_sprite_animation_count(&sprite); + const char** names = nullptr; + for (int i = 0; i < count; i++) { + apush(names, cf_sprite_animation_name_at(&sprite, i)); + } + int animation_index = 0; while (cf_app_is_running()) { @@ -52,7 +55,8 @@ int main(int argc, char *argv[]) cf_app_draw_onto_screen(true); } + afree(names); cf_destroy_app(); - + return 0; } diff --git a/samples/fluid_sim.cpp b/samples/fluid_sim.cpp index b4350519e..401544249 100644 --- a/samples/fluid_sim.cpp +++ b/samples/fluid_sim.cpp @@ -16,8 +16,10 @@ const char* s_shd = STR( float dt; }; - vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) + vec4 shader(vec4 color, ShaderParams params) { + vec2 screen_uv = params.screen_uv; + // --- Read current state --- vec4 C = texture(tex, screen_uv); ivec2 ts = textureSize(tex, 0); diff --git a/samples/font_debug.cpp b/samples/font_debug.cpp index cd17ecadd..883c897de 100644 --- a/samples/font_debug.cpp +++ b/samples/font_debug.cpp @@ -16,7 +16,7 @@ int string_glyph_length(const char* str) while (true) { int codepoint = 0; - s = cf_decode_UTF8(s, &codepoint); + s = cf_string_decode_UTF8(s, &codepoint); if (codepoint == 0) { break; @@ -57,7 +57,7 @@ int main(int argc, char* argv[]) int text_glyph_length = 0; int new_line_codepoint = 0; - cf_decode_UTF8("\n", &new_line_codepoint); + cf_string_decode_UTF8("\n", &new_line_codepoint); while (cf_app_is_running()) { @@ -68,7 +68,7 @@ int main(int argc, char* argv[]) const char* font_name = font_names[font_index]; CF_Font* font = cf_font_get(font_name); - float scale = stbtt_ScaleForPixelHeight(&font->info, font_size); + float scale = cf_font_scale_for_pixel_height(font, font_size); float ascent = font->ascent * scale; float descent = font->descent * scale; float line_gap = font->line_gap * scale; @@ -104,7 +104,7 @@ int main(int argc, char* argv[]) while (true) { prev_codepoint = codepoint; - s = cf_decode_UTF8(s, &codepoint); + s = cf_string_decode_UTF8(s, &codepoint); bool is_new_line = new_line_codepoint == codepoint; if (current_glyph_index == glyph_index) diff --git a/samples/galaxy.c b/samples/galaxy.c new file mode 100644 index 000000000..3c4595bbb --- /dev/null +++ b/samples/galaxy.c @@ -0,0 +1,341 @@ +// GPU N-body galaxy collision simulation. +// +// 16,384 stars stored as position/velocity in a 128x128 RGBA32F texture. +// A compute shader runs gravitational N-body simulation with shared-memory +// tiling and leapfrog integration. Two spiral galaxies approach each other +// and merge, producing tidal tails and bridges. +// +// Controls: none (just watch the galaxies collide). + +#include +#include +#include + +#define TEX_SIZE 128 +#define PARTICLE_COUNT (TEX_SIZE * TEX_SIZE) +#define SUB_STEPS 4 +#define DT 0.0005f +#define CENTRAL_MASS 150.0f +#define GALAXY_G 0.0001f +#define SOFTENING 0.005f +#define QUAD_SIZE 0.004f +#define BULGE_FRAC 0.1f +#define PI 3.14159265f + +// Galaxy collision: two galaxies offset from center with approach velocities. +#define GAL_RADIUS 0.30f +#define GAL_A_CX -0.25f +#define GAL_A_CY 0.04f +#define GAL_A_VX 0.025f +#define GAL_A_VY -0.008f +#define GAL_B_CX 0.25f +#define GAL_B_CY -0.04f +#define GAL_B_VX -0.025f +#define GAL_B_VY 0.008f + +// --------------------------------------------------------------------------- +// Simulation compute shader. +// One thread per particle. Reads state from sampler, writes to storage image. +// Central-body gravity + particle-particle N-body via shared memory tiling. +#define TILE_SIZE 256 +const char* s_sim_cs = + "layout(set = 0, binding = 0) uniform sampler2D u_state_in;\n" + "layout(set = 1, binding = 0, rgba32f) uniform writeonly image2D u_state_out;\n" + "layout(set = 2, binding = 0) uniform sim_params {\n" + " float u_dt;\n" + " float u_softening_sq;\n" + " float u_central_mass;\n" + " float u_G;\n" + " float u_tex_size;\n" + "};\n" + "layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;\n" + "shared vec4 tile[256];\n" + "void main()\n" + "{\n" + " ivec2 my_coord = ivec2(gl_GlobalInvocationID.xy);\n" + " int tex_sz = int(u_tex_size);\n" + " vec2 my_uv = (vec2(my_coord) + 0.5) / u_tex_size;\n" + " vec4 me = texture(u_state_in, my_uv);\n" + " vec2 pos = me.xy;\n" + " vec2 vel = me.zw;\n" + " vec2 acc = vec2(0.0);\n" + " vec2 d = -pos;\n" + " float dist_sq = dot(d, d) + u_softening_sq;\n" + " float inv_dist = inversesqrt(dist_sq);\n" + " float inv_dist3 = inv_dist * inv_dist * inv_dist;\n" + " acc += d * (u_G * u_central_mass * inv_dist3);\n" + " uint gid = uint(my_coord.y * tex_sz + my_coord.x);\n" + " uint lid = gl_LocalInvocationIndex;\n" + " uint total = uint(tex_sz * tex_sz);\n" + " uint num_tiles = (total + 255u) / 256u;\n" + " for (uint t = 0u; t < num_tiles; t++) {\n" + " uint idx = t * 256u + lid;\n" + " if (idx < total) {\n" + " ivec2 tc = ivec2(int(idx) % tex_sz, int(idx) / tex_sz);\n" + " tile[lid] = texelFetch(u_state_in, tc, 0);\n" + " } else {\n" + " tile[lid] = vec4(0.0);\n" + " }\n" + " barrier();\n" + " for (uint j = 0u; j < 256u; j++) {\n" + " uint other = t * 256u + j;\n" + " if (other >= total || other == gid) continue;\n" + " vec2 dp = tile[j].xy - pos;\n" + " float dsq = dot(dp, dp) + u_softening_sq;\n" + " float id2 = inversesqrt(dsq);\n" + " float id3 = id2 * id2 * id2;\n" + " acc += dp * (u_G * id3);\n" + " }\n" + " barrier();\n" + " }\n" + " vel += acc * u_dt;\n" + " pos += vel * u_dt;\n" + " if (my_coord.x < tex_sz && my_coord.y < tex_sz)\n" + " imageStore(u_state_out, my_coord, vec4(pos, vel));\n" + "}\n"; + +// --------------------------------------------------------------------------- +// Star vertex shader. +// Per-vertex: quad corner position and local UV for falloff. +// Per-instance: UV coordinate into state texture to fetch star position. +const char* s_star_vs = + "layout(location = 0) in vec2 in_pos;\n" + "layout(location = 1) in vec2 in_local;\n" + "layout(location = 2) in vec2 in_uv;\n" + "layout(location = 0) out vec2 v_local;\n" + "layout(location = 1) out float v_speed;\n" + "layout(set = 0, binding = 0) uniform sampler2D u_state;\n" + "layout(set = 1, binding = 0) uniform star_params {\n" + " float u_quad_size;\n" + " float u_aspect;\n" + "};\n" + "void main()\n" + "{\n" + " vec4 s = texture(u_state, in_uv);\n" + " vec2 star_pos = s.xy;\n" + " vec2 star_vel = s.zw;\n" + " v_speed = length(star_vel);\n" + " v_local = in_local;\n" + " vec2 offset = in_pos * u_quad_size;\n" + " offset.x /= u_aspect;\n" + " gl_Position = vec4(star_pos + offset, 0.0, 1.0);\n" + "}\n"; + +// --------------------------------------------------------------------------- +// Star fragment shader. +// Gaussian circular falloff, velocity-based color mapping, premultiplied output. +const char* s_star_fs = + "layout(location = 0) in vec2 v_local;\n" + "layout(location = 1) in float v_speed;\n" + "layout(location = 0) out vec4 result;\n" + "void main()\n" + "{\n" + " float r = length(v_local);\n" + " if (r > 1.0) discard;\n" + " float intensity = exp(-r * r * 4.0);\n" + " float t = clamp(v_speed * 3.0, 0.0, 1.0);\n" + " vec3 warm = vec3(1.0, 0.7, 0.3);\n" + " vec3 cool = vec3(0.5, 0.7, 1.0);\n" + " vec3 color = mix(warm, cool, t);\n" + " result = vec4(color * intensity, intensity);\n" + "}\n"; + +// --------------------------------------------------------------------------- +// Quad vertex data. +typedef struct QuadVertex +{ + CF_V2 pos; + CF_V2 local; +} QuadVertex; + +// Per-instance data: UV into state texture. +typedef struct StarInstance +{ + CF_V2 uv; +} StarInstance; + +// --------------------------------------------------------------------------- +// Random float in [lo, hi]. +float randf(float lo, float hi) +{ + return lo + (hi - lo) * ((float)rand() / (float)RAND_MAX); +} + +int main(int argc, char* argv[]) { + CF_Result result = cf_make_app("Galaxy N-Body", 0, 0, 0, 900, 900, CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT, argv[0]); + if (cf_is_error(result)) return -1; + + // -- State textures (ping-pong pair). -- + CF_TextureParams tp = cf_texture_defaults(TEX_SIZE, TEX_SIZE); + tp.pixel_format = CF_PIXEL_FORMAT_R32G32B32A32_FLOAT; + tp.usage = CF_TEXTURE_USAGE_SAMPLER_BIT | CF_TEXTURE_USAGE_COMPUTE_STORAGE_WRITE_BIT; + tp.filter = CF_FILTER_NEAREST; + CF_Texture state[2]; + state[0] = cf_make_texture(tp); + state[1] = cf_make_texture(tp); + + // -- Generate galaxy collision initial conditions. -- + // Two spiral galaxies at offset positions with opposing bulk velocities. + float* init_data = (float*)malloc(PARTICLE_COUNT * 4 * sizeof(float)); + srand(42); + int half = PARTICLE_COUNT / 2; + for (int i = 0; i < PARTICLE_COUNT; i++) { + float* p = init_data + i * 4; + + // Which galaxy? + int gal = (i >= half) ? 1 : 0; + float cx = gal ? GAL_B_CX : GAL_A_CX; + float cy = gal ? GAL_B_CY : GAL_A_CY; + float bvx = gal ? GAL_B_VX : GAL_A_VX; + float bvy = gal ? GAL_B_VY : GAL_A_VY; + float dir = gal ? -1.0f : 1.0f; // Counter-rotating galaxies. + int local_i = gal ? (i - half) : i; + + float r, angle; + if (local_i < (int)(half * BULGE_FRAC)) { + // Central bulge. + r = randf(0.0f, GAL_RADIUS * 0.15f); + angle = randf(0.0f, 2.0f * PI); + } else { + // Spiral arms. + r = randf(0.01f, 1.0f); + r = -0.2f * logf(r); + if (r > GAL_RADIUS) r = randf(0.05f, GAL_RADIUS); + + int arm = local_i % 2; + float base_angle = arm * PI; + float spiral = 2.5f * logf(r + 0.1f); + angle = base_angle + spiral + randf(-0.25f, 0.25f); + } + + float px = cx + r * cosf(angle); + float py = cy + r * sinf(angle); + + // Orbital velocity around local galaxy center. + float speed = sqrtf(GALAXY_G * (float)half / (r + SOFTENING)) * 0.3f; + float vx = bvx + dir * (-speed * sinf(angle)); + float vy = bvy + dir * ( speed * cosf(angle)); + + vx += randf(-0.005f, 0.005f); + vy += randf(-0.005f, 0.005f); + + p[0] = px; + p[1] = py; + p[2] = vx; + p[3] = vy; + } + cf_texture_update(state[0], init_data, PARTICLE_COUNT * 4 * (int)sizeof(float)); + free(init_data); + + // -- Compute shader + material for simulation. -- + CF_ComputeShader sim_shader = cf_make_compute_shader_from_source(s_sim_cs); + CF_Material sim_material = cf_make_material(); + + // -- Graphics shader + material for star rendering. -- + CF_Shader star_shader = cf_make_shader_from_source(s_star_vs, s_star_fs); + CF_Material star_material = cf_make_material(); + + // Additive blend state. + CF_RenderState rs = cf_render_state_defaults(); + rs.blend.enabled = true; + rs.blend.rgb_op = CF_BLEND_OP_ADD; + rs.blend.rgb_src_blend_factor = CF_BLENDFACTOR_ONE; + rs.blend.rgb_dst_blend_factor = CF_BLENDFACTOR_ONE; + rs.blend.alpha_op = CF_BLEND_OP_ADD; + rs.blend.alpha_src_blend_factor = CF_BLENDFACTOR_ONE; + rs.blend.alpha_dst_blend_factor = CF_BLENDFACTOR_ONE; + cf_material_set_render_state(star_material, rs); + + // -- Mesh: instanced star quads. -- + // Two triangles forming a quad, with local coords for Gaussian falloff. + QuadVertex quad_verts[6] = { + { cf_v2(-1, -1), cf_v2(-1, -1) }, + { cf_v2( 1, -1), cf_v2( 1, -1) }, + { cf_v2( 1, 1), cf_v2( 1, 1) }, + { cf_v2(-1, -1), cf_v2(-1, -1) }, + { cf_v2( 1, 1), cf_v2( 1, 1) }, + { cf_v2(-1, 1), cf_v2(-1, 1) }, + }; + + CF_VertexAttribute attrs[3] = { 0 }; + attrs[0].name = "in_pos"; + attrs[0].format = CF_VERTEX_FORMAT_FLOAT2; + attrs[0].offset = CF_OFFSET_OF(QuadVertex, pos); + attrs[1].name = "in_local"; + attrs[1].format = CF_VERTEX_FORMAT_FLOAT2; + attrs[1].offset = CF_OFFSET_OF(QuadVertex, local); + attrs[2].name = "in_uv"; + attrs[2].format = CF_VERTEX_FORMAT_FLOAT2; + attrs[2].offset = CF_OFFSET_OF(StarInstance, uv); + attrs[2].per_instance = true; + + CF_Mesh mesh = cf_make_mesh(sizeof(quad_verts), attrs, 3, sizeof(QuadVertex)); + cf_mesh_update_vertex_data(mesh, quad_verts, 6); + + // Instance data: one UV per particle pointing into the state texture. + StarInstance* instances = (StarInstance*)malloc(PARTICLE_COUNT * sizeof(StarInstance)); + for (int i = 0; i < PARTICLE_COUNT; i++) { + int x = i % TEX_SIZE; + int y = i / TEX_SIZE; + instances[i].uv = cf_v2(((float)x + 0.5f) / TEX_SIZE, ((float)y + 0.5f) / TEX_SIZE); + } + cf_mesh_set_instance_buffer(mesh, PARTICLE_COUNT * (int)sizeof(StarInstance), (int)sizeof(StarInstance)); + cf_mesh_update_instance_data(mesh, instances, PARTICLE_COUNT); + free(instances); + + int cur = 0; + + while (cf_app_is_running()) { + cf_app_update(NULL); + + // -- Compute pass: N-body simulation sub-steps. -- + float dt = DT; + float softening_sq = SOFTENING * SOFTENING; + float central_mass = CENTRAL_MASS; + float grav = GALAXY_G; + float tex_size = (float)TEX_SIZE; + + for (int step = 0; step < SUB_STEPS; step++) { + cf_material_set_texture_cs(sim_material, "u_state_in", state[cur]); + cf_material_set_uniform_cs(sim_material, "u_dt", &dt, CF_UNIFORM_TYPE_FLOAT, 1); + cf_material_set_uniform_cs(sim_material, "u_softening_sq", &softening_sq, CF_UNIFORM_TYPE_FLOAT, 1); + cf_material_set_uniform_cs(sim_material, "u_central_mass", ¢ral_mass, CF_UNIFORM_TYPE_FLOAT, 1); + cf_material_set_uniform_cs(sim_material, "u_G", &grav, CF_UNIFORM_TYPE_FLOAT, 1); + cf_material_set_uniform_cs(sim_material, "u_tex_size", &tex_size, CF_UNIFORM_TYPE_FLOAT, 1); + + int groups = TEX_SIZE / 16; + CF_ComputeDispatch dispatch = cf_compute_dispatch_defaults(groups, groups, 1); + dispatch.rw_textures = &state[1 - cur]; + dispatch.rw_texture_count = 1; + cf_dispatch_compute(sim_shader, sim_material, dispatch); + + cur = 1 - cur; + } + + // -- Graphics pass: render stars as additive glowing quads. -- + cf_apply_canvas(cf_app_get_canvas(), true); + + cf_material_set_texture_vs(star_material, "u_state", state[cur]); + float quad_size = QUAD_SIZE; + float aspect = 1.0f; // Square window. + cf_material_set_uniform_vs(star_material, "u_quad_size", &quad_size, CF_UNIFORM_TYPE_FLOAT, 1); + cf_material_set_uniform_vs(star_material, "u_aspect", &aspect, CF_UNIFORM_TYPE_FLOAT, 1); + + cf_apply_mesh(mesh); + cf_apply_shader(star_shader, star_material); + cf_draw_elements(); + + cf_app_draw_onto_screen(false); + } + + cf_destroy_compute_shader(sim_shader); + cf_destroy_shader(star_shader); + cf_destroy_material(sim_material); + cf_destroy_material(star_material); + cf_destroy_mesh(mesh); + cf_destroy_texture(state[0]); + cf_destroy_texture(state[1]); + cf_destroy_app(); + + return 0; +} diff --git a/samples/glitch.cpp b/samples/glitch.cpp new file mode 100644 index 000000000..2af70e623 --- /dev/null +++ b/samples/glitch.cpp @@ -0,0 +1,61 @@ +#include +using namespace Cute; + +const char* s_glitch = R"( + vec4 shader(vec4 color, ShaderParams params) + { + float time = params.attributes.x; + float band_count = params.attributes.y; + float intensity = params.attributes.z; + + // Need valid UV bounds for the clamp-to-edge feature. + vec2 uv_range = params.uv_max - params.uv_min; + + // Normalize UV to 0-1 within the sprite's atlas region. + vec2 uv = v_uv; + vec2 local_uv = (uv - params.uv_min) / uv_range; + + // Slice into horizontal bands based on local position. + float band = floor(local_uv.y * band_count); + float hash = fract(sin(band * 43.758 + floor(time * 12.0)) * 4758.5453); + + // Only glitch ~40% of bands; offset horizontally scaled to sprite width. + if (hash > 0.6) { + uv.x += (hash - 0.6) * 2.5 * intensity * uv_range.x; + } + + // Clamp to sprite atlas bounds -- prevents bleeding from neighboring atlas entries. + uv = clamp(uv, params.uv_min, params.uv_max); + + // Re-sample the texture at the glitched+clamped UV. + return texture(u_image, smooth_uv(uv, u_texture_size)); + } +)"; + +int main(int argc, char* argv[]) +{ + CF_Result result = make_app("Glitch", 0, 0, 0, 640, 480, CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT, argv[0]); + if (is_error(result)) return -1; + CF_Shader shd = make_draw_shader_from_source(s_glitch); + + CF_Sprite girl = cf_make_demo_sprite(); + sprite_play(girl, "spin"); + girl.scale = V2(4, 4); + + float time = 0; + + while (app_is_running()) { + app_update(); + time += CF_DELTA_TIME; + + draw_push_shader(shd); + draw_push_vertex_attributes(time, 20.0f, 0.3f, 0); + sprite_update(girl); + sprite_draw(girl); + + app_draw_onto_screen(true); + } + + destroy_app(); + return 0; +} diff --git a/samples/hello_triangle_data/color_fill.glsl b/samples/hello_triangle_data/color_fill.glsl deleted file mode 100644 index 1e2d79cf3..000000000 --- a/samples/hello_triangle_data/color_fill.glsl +++ /dev/null @@ -1,28 +0,0 @@ -@module color_fill -@ctype vec2 CF_V2 -@ctype vec4 CF_Pixel - -@vs vs - layout (location = 0) in vec2 in_pos; - layout (location = 1) in vec4 in_col; - - layout (location = 0) out vec4 col; - - void main() { - vec4 posH = vec4(in_pos, 0, 1); - col = in_col; - gl_Position = posH; - } -@end - -@fs fs - layout (location = 0) in vec4 col; - - out vec4 result; - - void main() { - result = col; - } -@end - -@program shader vs fs diff --git a/samples/hrc.c b/samples/hrc.c new file mode 100644 index 000000000..0ae212ae8 --- /dev/null +++ b/samples/hrc.c @@ -0,0 +1,816 @@ +// Holographic Radiance Cascades +// +// Demonstrates HRC 2D global illumination based on the Amitabha-style SSBO pipeline with f16 packing and 4-rotation frustum. +// +// Reference: Freeman, Sannikov, Margel (2025) "Holographic Radiance Cascades" +// https://arxiv.org/pdf/2505.02041 +// https://github.com/entropylost/amitabha + +#include + +//-------------------------------------------------------------------------------------------------- +// Configuration. + +#define POW2_LOG2(n) ( \ + (n) >= 4096 ? 12 : \ + (n) >= 2048 ? 11 : \ + (n) >= 1024 ? 10 : \ + (n) >= 512 ? 9 : \ + (n) >= 256 ? 8 : \ + (n) >= 128 ? 7 : \ + (n) >= 64 ? 6 : \ + (n) >= 32 ? 5 : \ + (n) >= 16 ? 4 : \ + (n) >= 8 ? 3 : \ + (n) >= 4 ? 2 : \ + (n) >= 2 ? 1 : 0 \ +) + +#define HRC_WORLD_SIZE 1024 +#define HRC_MAX_N POW2_LOG2(HRC_WORLD_SIZE) +#define HRC_WG 16 +#define HRC_ABS_THRESHOLD 0.1f +#define HRC_NUM_DEBUG 8 // debug modes 0..7 + +//-------------------------------------------------------------------------------------------------- +// Shader loading. + +CF_ComputeShader load_compute_shader(const char* path) +{ + char* src = cf_fs_read_entire_file_to_memory_and_nul_terminate(path, NULL); + CF_ComputeShader cs = cf_make_compute_shader_from_source(src); + cf_free(src); + return cs; +} +//-------------------------------------------------------------------------------------------------- +// HRC state. + +typedef struct Hrc +{ + CF_Canvas emissivity; + CF_Canvas absorption; + CF_StorageBuffer vrays_rad[HRC_MAX_N + 1]; + CF_StorageBuffer vrays_trn[HRC_MAX_N + 1]; + CF_StorageBuffer r_rad[2]; + CF_StorageBuffer r_zero; + CF_StorageBuffer frustum[4]; + CF_Canvas fluence; + CF_Material mat_trace; + CF_Material mat_extend; + CF_Material mat_merge; + CF_Material mat_composite; + CF_Material mat_copy; + CF_ComputeShader cs_seed; + CF_ComputeShader cs_trace; + CF_ComputeShader cs_extend; + CF_ComputeShader cs_merge; + CF_ComputeShader cs_copy; + CF_ComputeShader cs_composite; + int vrays_w[HRC_MAX_N + 1]; + int debug_mode; + int grid; + int n; + int trace_levels; // how many cascade levels use direct trace (0..n+1) + int cminus1; // c-1 gathering enabled + int upscale_mode; // 0=nearest, 1=minmax bilinear, 2=joint bilateral + int blend_boundary; // angle-based weight blending near ±45° edges + float blend_width; // blend falloff width in direction slots + int prefilter_mode; // 0=off, 1=box, 2=mipmap + CF_Canvas minmax; + CF_Material mat_minmax; + CF_ComputeShader cs_minmax; + CF_Canvas emissivity_filtered; + CF_Canvas absorption_filtered; + CF_Material mat_prefilter; + CF_ComputeShader cs_prefilter; + CF_ComputeShader cs_prefilter_gauss; +} Hrc; + +Hrc hrc; + +//-------------------------------------------------------------------------------------------------- +// Helpers. + +CF_StorageBuffer hrc_make_buf(int w, int h) +{ + CF_StorageBufferParams p = cf_storage_buffer_defaults(w * h * 8); + p.compute_readable = true; + p.compute_writable = true; + return cf_make_storage_buffer(p); +} + +CF_Canvas hrc_make_canvas(int w, int h, CF_PixelFormat fmt, CF_Filter filter, bool allocate_mipmaps) +{ + CF_CanvasParams p = cf_canvas_defaults(w, h); + p.target.pixel_format = fmt; + p.target.filter = filter; + p.target.usage = CF_TEXTURE_USAGE_SAMPLER_BIT | CF_TEXTURE_USAGE_COLOR_TARGET_BIT | CF_TEXTURE_USAGE_COMPUTE_STORAGE_READ_BIT | CF_TEXTURE_USAGE_COMPUTE_STORAGE_WRITE_BIT; + p.target.wrap_u = CF_WRAP_MODE_CLAMP_TO_EDGE; + p.target.wrap_v = CF_WRAP_MODE_CLAMP_TO_EDGE; + if (allocate_mipmaps) { + p.target.allocate_mipmaps = true; + p.target.mip_count = 0; + } + return cf_make_canvas(p); +} + +int hrc_div_ceil(int a, int b) +{ + return (a + b - 1) / b; +} + +//-------------------------------------------------------------------------------------------------- +// Init / shutdown. + +void hrc_set_grid(int grid) +{ + hrc.grid = grid; + hrc.n = POW2_LOG2(grid); + for (int i = 0; i <= hrc.n; i++) { + int interval = 1 << i; + int rays = interval + 1; + int probes = grid >> i; + hrc.vrays_w[i] = probes * rays; + } +} + +void hrc_init() +{ + CF_MEMSET(&hrc, 0, sizeof(hrc)); + + // Start at half resolution. + hrc_set_grid(HRC_WORLD_SIZE / 2); + hrc.trace_levels = 3; // trace T_0..T_2, extend T_3..T_N + hrc.cminus1 = 1; + hrc.upscale_mode = 1; + hrc.blend_boundary = 1; + hrc.prefilter_mode = 2; + hrc.blend_width = 4.0f; + + // Precompute max T cascade dimensions for allocation. + int max_vrays_w[HRC_MAX_N + 1]; + for (int i = 0; i <= HRC_MAX_N; i++) { + int interval = 1 << i; + int rays = interval + 1; + int probes = HRC_WORLD_SIZE >> i; + max_vrays_w[i] = probes * rays; + } + + // Scene input canvases (nearest-neighbor: discrete pixel grid, no sRGB-space blending). + // Mipmaps allocated for hardware mipmap prefilter mode. + hrc.emissivity = hrc_make_canvas(HRC_WORLD_SIZE, HRC_WORLD_SIZE, CF_PIXEL_FORMAT_R16G16B16A16_FLOAT, CF_FILTER_NEAREST, true); + hrc.absorption = hrc_make_canvas(HRC_WORLD_SIZE, HRC_WORLD_SIZE, CF_PIXEL_FORMAT_R16G16B16A16_FLOAT, CF_FILTER_NEAREST, true); + + // Per-cascade T SSBOs (uvec2 per texel = 8 bytes, f16-packed). Allocated at max size. + for (int i = 0; i <= HRC_MAX_N; i++) { + hrc.vrays_rad[i] = hrc_make_buf(max_vrays_w[i], HRC_WORLD_SIZE); + hrc.vrays_trn[i] = hrc_make_buf(max_vrays_w[i], HRC_WORLD_SIZE); + } + + // R ping-pong SSBOs + zero buffer for R_N = 0. + for (int i = 0; i < 2; i++) + hrc.r_rad[i] = hrc_make_buf(HRC_WORLD_SIZE, HRC_WORLD_SIZE); + hrc.r_zero = hrc_make_buf(HRC_WORLD_SIZE, HRC_WORLD_SIZE); + { + int sz = HRC_WORLD_SIZE * HRC_WORLD_SIZE * 8; + void* zeros = cf_calloc(sz, 1); + cf_update_storage_buffer(hrc.r_zero, zeros, sz); + cf_free(zeros); + } + + // Per-frustum output SSBOs (4 rotations). + for (int i = 0; i < 4; i++) + hrc.frustum[i] = hrc_make_buf(HRC_WORLD_SIZE, HRC_WORLD_SIZE); + + // Final output canvas (linear filtering for smooth display). + hrc.fluence = hrc_make_canvas(HRC_WORLD_SIZE, HRC_WORLD_SIZE, CF_PIXEL_FORMAT_R8G8B8A8_UNORM, CF_FILTER_LINEAR, false); + + // Min/max absorption canvas (grid-res, allocated at world size to handle runtime grid changes). + hrc.minmax = hrc_make_canvas(HRC_WORLD_SIZE, HRC_WORLD_SIZE, CF_PIXEL_FORMAT_R16G16_FLOAT, CF_FILTER_NEAREST, false); + + // Prefiltered scene inputs (grid-res, allocated at world size). + hrc.emissivity_filtered = hrc_make_canvas(HRC_WORLD_SIZE, HRC_WORLD_SIZE, CF_PIXEL_FORMAT_R16G16B16A16_FLOAT, CF_FILTER_NEAREST, false); + hrc.absorption_filtered = hrc_make_canvas(HRC_WORLD_SIZE, HRC_WORLD_SIZE, CF_PIXEL_FORMAT_R16G16B16A16_FLOAT, CF_FILTER_NEAREST, false); + + // Materials. + hrc.mat_trace = cf_make_material(); + hrc.mat_extend = cf_make_material(); + hrc.mat_merge = cf_make_material(); + hrc.mat_composite = cf_make_material(); + hrc.mat_copy = cf_make_material(); + hrc.mat_minmax = cf_make_material(); + hrc.mat_prefilter = cf_make_material(); + + // Compute shaders (loaded from hrc_data/ next to the executable). + hrc.cs_seed = load_compute_shader("/hrc_data/hrc_seed.c_shd"); + hrc.cs_trace = load_compute_shader("/hrc_data/hrc_trace.c_shd"); + hrc.cs_extend = load_compute_shader("/hrc_data/hrc_extend.c_shd"); + hrc.cs_merge = load_compute_shader("/hrc_data/hrc_merge.c_shd"); + hrc.cs_copy = load_compute_shader("/hrc_data/hrc_copy.c_shd"); + hrc.cs_composite = load_compute_shader("/hrc_data/hrc_composite.c_shd"); + hrc.cs_minmax = load_compute_shader("/hrc_data/hrc_minmax.c_shd"); + hrc.cs_prefilter = load_compute_shader("/hrc_data/hrc_prefilter.c_shd"); + hrc.cs_prefilter_gauss = load_compute_shader("/hrc_data/hrc_prefilter_gauss.c_shd"); +} + +void hrc_shutdown() +{ + cf_destroy_canvas(hrc.emissivity); + cf_destroy_canvas(hrc.absorption); + for (int i = 0; i <= HRC_MAX_N; i++) { + cf_destroy_storage_buffer(hrc.vrays_rad[i]); + cf_destroy_storage_buffer(hrc.vrays_trn[i]); + } + for (int i = 0; i < 2; i++) + cf_destroy_storage_buffer(hrc.r_rad[i]); + cf_destroy_storage_buffer(hrc.r_zero); + for (int i = 0; i < 4; i++) + cf_destroy_storage_buffer(hrc.frustum[i]); + cf_destroy_canvas(hrc.fluence); + cf_destroy_canvas(hrc.minmax); + cf_destroy_canvas(hrc.emissivity_filtered); + cf_destroy_canvas(hrc.absorption_filtered); + cf_destroy_material(hrc.mat_trace); + cf_destroy_material(hrc.mat_extend); + cf_destroy_material(hrc.mat_merge); + cf_destroy_material(hrc.mat_composite); + cf_destroy_material(hrc.mat_copy); + cf_destroy_material(hrc.mat_minmax); + cf_destroy_material(hrc.mat_prefilter); + cf_destroy_compute_shader(hrc.cs_seed); + cf_destroy_compute_shader(hrc.cs_trace); + cf_destroy_compute_shader(hrc.cs_extend); + cf_destroy_compute_shader(hrc.cs_merge); + cf_destroy_compute_shader(hrc.cs_copy); + cf_destroy_compute_shader(hrc.cs_composite); + cf_destroy_compute_shader(hrc.cs_minmax); + cf_destroy_compute_shader(hrc.cs_prefilter); + cf_destroy_compute_shader(hrc.cs_prefilter_gauss); +} + +//-------------------------------------------------------------------------------------------------- +// Cascade compute pipeline. + +void hrc_compute() +{ + CF_Texture emiss_tex = cf_canvas_get_target(hrc.emissivity); + CF_Texture absrp_tex = cf_canvas_get_target(hrc.absorption); + CF_Texture fluence_tex = cf_canvas_get_target(hrc.fluence); + CF_Texture minmax_tex = cf_canvas_get_target(hrc.minmax); + + int dim = hrc.grid; + int N = hrc.n; + + // Textures used by the trace shader (swapped to prefiltered versions when enabled). + CF_Texture trace_emiss = emiss_tex; + CF_Texture trace_absrp = absrp_tex; + + // Mipmap prefilter: generate mipmaps from base level for textureLod sampling. + float mip_level = 0.0f; + float absrp_mip_level = 0.0f; + if ((hrc.prefilter_mode == 2 || hrc.prefilter_mode == 4) && dim < HRC_WORLD_SIZE) { + cf_generate_mipmaps(emiss_tex); + mip_level = log2f((float)HRC_WORLD_SIZE / (float)dim); + if (hrc.prefilter_mode == 2) { + cf_generate_mipmaps(absrp_tex); + absrp_mip_level = mip_level; + } + } + + // Box/Gauss prefilter: downsample emissivity and absorption to grid resolution. + // Mip+max (mode 4): run box prefilter for absorption max only (emissivity uses mipmaps). + if ((hrc.prefilter_mode == 1 || hrc.prefilter_mode == 3 || hrc.prefilter_mode == 4) && dim < HRC_WORLD_SIZE) { + CF_Texture emiss_filt_tex = cf_canvas_get_target(hrc.emissivity_filtered); + CF_Texture absrp_filt_tex = cf_canvas_get_target(hrc.absorption_filtered); + + int world = HRC_WORLD_SIZE; + cf_material_set_texture_cs(hrc.mat_prefilter, "u_emissivity", emiss_tex); + cf_material_set_texture_cs(hrc.mat_prefilter, "u_absorption", absrp_tex); + cf_material_set_uniform_cs(hrc.mat_prefilter, "u_world_size", &world, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_prefilter, "u_grid_size", &dim, CF_UNIFORM_TYPE_INT, 1); + + CF_ComputeDispatch d = cf_compute_dispatch_defaults( + hrc_div_ceil(dim, HRC_WG), + hrc_div_ceil(dim, HRC_WG), + 1 + ); + CF_Texture rw_tex[2] = { emiss_filt_tex, absrp_filt_tex }; + d.rw_textures = rw_tex; + d.rw_texture_count = 2; + CF_ComputeShader cs = hrc.prefilter_mode == 3 ? hrc.cs_prefilter_gauss : hrc.cs_prefilter; + cf_dispatch_compute(cs, hrc.mat_prefilter, d); + + if (hrc.prefilter_mode == 4) { + // Mip+max: only swap absorption (emissivity uses mipmaps via textureLod). + trace_absrp = absrp_filt_tex; + } else { + trace_emiss = emiss_filt_tex; + trace_absrp = absrp_filt_tex; + } + } + + // Set mip levels for trace/seed shaders. + cf_material_set_uniform_cs(hrc.mat_trace, "u_mip_level", &mip_level, CF_UNIFORM_TYPE_FLOAT, 1); + cf_material_set_uniform_cs(hrc.mat_trace, "u_absrp_mip_level", &absrp_mip_level, CF_UNIFORM_TYPE_FLOAT, 1); + + for (int j = 0; j < 4; j++) { + // Seed T_0 when no levels are traced (sample at probe, no raymarching). + if (hrc.trace_levels == 0) { + int params[4] = { 0, j, dim, hrc.vrays_w[0] }; + cf_material_set_texture_cs(hrc.mat_trace, "u_emissivity", trace_emiss); + cf_material_set_texture_cs(hrc.mat_trace, "u_absorption", trace_absrp); + cf_material_set_uniform_cs(hrc.mat_trace, "u_cascade", params + 0, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_trace, "u_rotate", params + 1, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_trace, "u_world_size", params + 2, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_trace, "u_curr_w", params + 3, CF_UNIFORM_TYPE_INT, 1); + + CF_ComputeDispatch d = cf_compute_dispatch_defaults( + hrc_div_ceil(hrc.vrays_w[0], HRC_WG), + hrc_div_ceil(dim, HRC_WG), + 1 + ); + CF_StorageBuffer rw[2] = { hrc.vrays_rad[0], hrc.vrays_trn[0] }; + d.rw_buffers = rw; + d.rw_buffer_count = 2; + cf_dispatch_compute(hrc.cs_seed, hrc.mat_trace, d); + } + + // Direct trace T_0..T_{trace_levels-1}. + for (int i = 0; i < hrc.trace_levels; i++) { + int params[5] = { i, j, dim, hrc.vrays_w[i], hrc.blend_boundary }; + cf_material_set_texture_cs(hrc.mat_trace, "u_emissivity", trace_emiss); + cf_material_set_texture_cs(hrc.mat_trace, "u_absorption", trace_absrp); + cf_material_set_uniform_cs(hrc.mat_trace, "u_cascade", params + 0, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_trace, "u_rotate", params + 1, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_trace, "u_world_size", params + 2, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_trace, "u_curr_w", params + 3, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_trace, "u_blend_boundary", params + 4, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_trace, "u_blend_width", &hrc.blend_width, CF_UNIFORM_TYPE_FLOAT, 1); + + CF_ComputeDispatch d = cf_compute_dispatch_defaults( + hrc_div_ceil(hrc.vrays_w[i], HRC_WG), + hrc_div_ceil(dim, HRC_WG), + 1 + ); + CF_StorageBuffer rw[2] = { hrc.vrays_rad[i], hrc.vrays_trn[i] }; + d.rw_buffers = rw; + d.rw_buffer_count = 2; + cf_dispatch_compute(hrc.cs_trace, hrc.mat_trace, d); + } + + // Extend remaining levels. + int ext_start = hrc.trace_levels > 0 ? hrc.trace_levels : 1; + for (int i = ext_start; i <= N; i++) { + int params[4] = { i, dim, hrc.vrays_w[i - 1], hrc.vrays_w[i] }; + cf_material_set_uniform_cs(hrc.mat_extend, "u_cascade", params + 0, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_extend, "u_world_size", params + 1, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_extend, "u_prev_w", params + 2, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_extend, "u_curr_w", params + 3, CF_UNIFORM_TYPE_INT, 1); + + CF_ComputeDispatch d = cf_compute_dispatch_defaults( + hrc_div_ceil(hrc.vrays_w[i], HRC_WG), + hrc_div_ceil(dim, HRC_WG), + 1 + ); + CF_StorageBuffer ro[2] = { hrc.vrays_rad[i - 1], hrc.vrays_trn[i - 1] }; + d.ro_buffers = ro; + d.ro_buffer_count = 2; + CF_StorageBuffer rw[2] = { hrc.vrays_rad[i], hrc.vrays_trn[i] }; + d.rw_buffers = rw; + d.rw_buffer_count = 2; + cf_dispatch_compute(hrc.cs_extend, hrc.mat_extend, d); + } + + // Merge R_{N-1} down to R_0. + int r_ping = 0; + for (int i = N - 1; i >= 0; i--) { + int params[6] = { i, dim, hrc.vrays_w[i], hrc.vrays_w[i + 1], dim, dim }; + cf_material_set_uniform_cs(hrc.mat_merge, "u_cascade", params + 0, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_merge, "u_world_size", params + 1, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_merge, "u_t_curr_w", params + 2, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_merge, "u_t_next_w", params + 3, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_merge, "u_r_prev_w", params + 4, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_merge, "u_r_curr_w", params + 5, CF_UNIFORM_TYPE_INT, 1); + + CF_ComputeDispatch d = cf_compute_dispatch_defaults( + hrc_div_ceil(dim, HRC_WG), + hrc_div_ceil(dim, HRC_WG), + 1 + ); + CF_StorageBuffer r_prev = (i == N - 1) ? hrc.r_zero : hrc.r_rad[1 - r_ping]; + CF_StorageBuffer ro[5] = { + hrc.vrays_rad[i], hrc.vrays_trn[i], + hrc.vrays_rad[i + 1], hrc.vrays_trn[i + 1], + r_prev + }; + d.ro_buffers = ro; + d.ro_buffer_count = 5; + CF_StorageBuffer rw[1] = { hrc.r_rad[r_ping] }; + d.rw_buffers = rw; + d.rw_buffer_count = 1; + cf_dispatch_compute(hrc.cs_merge, hrc.mat_merge, d); + r_ping = 1 - r_ping; + } + + // Copy R_0 -> frustum[j]. + { + int count = dim * dim; + cf_material_set_uniform_cs(hrc.mat_copy, "u_count", &count, CF_UNIFORM_TYPE_INT, 1); + + CF_ComputeDispatch d = cf_compute_dispatch_defaults( + hrc_div_ceil(count, 256), + 1, + 1 + ); + CF_StorageBuffer ro[1] = { hrc.r_rad[1 - r_ping] }; + d.ro_buffers = ro; + d.ro_buffer_count = 1; + CF_StorageBuffer rw[1] = { hrc.frustum[j] }; + d.rw_buffers = rw; + d.rw_buffer_count = 1; + cf_dispatch_compute(hrc.cs_copy, hrc.mat_copy, d); + } + } + + // Minmax absorption pass: compute per-grid-cell min/max absorption for bilinear R_0 upscaling. + // Dispatched at world resolution so the composite shader can read at world coordinates directly. + if (hrc.upscale_mode == 1) { + int world = HRC_WORLD_SIZE; + cf_material_set_texture_cs(hrc.mat_minmax, "u_absorption", absrp_tex); + cf_material_set_uniform_cs(hrc.mat_minmax, "u_world_size", &world, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_minmax, "u_grid_size", &dim, CF_UNIFORM_TYPE_INT, 1); + + CF_ComputeDispatch d = cf_compute_dispatch_defaults( + hrc_div_ceil(world, HRC_WG), + hrc_div_ceil(world, HRC_WG), + 1 + ); + CF_Texture rw_tex[1] = { minmax_tex }; + d.rw_textures = rw_tex; + d.rw_texture_count = 1; + cf_dispatch_compute(hrc.cs_minmax, hrc.mat_minmax, d); + } + + // Composite: c-1 gathering, sum 4 quadrants, cross blur, output. + { + int world = HRC_WORLD_SIZE; + float abs_thresh = HRC_ABS_THRESHOLD; + int debug = hrc.debug_mode <= 5 ? hrc.debug_mode : 0; + cf_material_set_texture_cs(hrc.mat_composite, "u_emissivity", emiss_tex); + cf_material_set_texture_cs(hrc.mat_composite, "u_absorption", absrp_tex); + cf_material_set_texture_cs(hrc.mat_composite, "u_minmax", minmax_tex); + cf_material_set_uniform_cs(hrc.mat_composite, "u_world_size", &world, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_composite, "u_grid_size", &dim, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_composite, "u_abs_threshold", &abs_thresh, CF_UNIFORM_TYPE_FLOAT, 1); + cf_material_set_uniform_cs(hrc.mat_composite, "u_debug_mode", &debug, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_composite, "u_cminus1", &hrc.cminus1, CF_UNIFORM_TYPE_INT, 1); + cf_material_set_uniform_cs(hrc.mat_composite, "u_upscale_mode", &hrc.upscale_mode, CF_UNIFORM_TYPE_INT, 1); + + CF_ComputeDispatch d = cf_compute_dispatch_defaults( + hrc_div_ceil(world, HRC_WG), + hrc_div_ceil(world, HRC_WG), + 1 + ); + CF_StorageBuffer ro[4] = { + hrc.frustum[0], hrc.frustum[1], + hrc.frustum[2], hrc.frustum[3] + }; + d.ro_buffers = ro; + d.ro_buffer_count = 4; + CF_Texture rw_tex[1] = { fluence_tex }; + d.rw_textures = rw_tex; + d.rw_texture_count = 1; + cf_dispatch_compute(hrc.cs_composite, hrc.mat_composite, d); + } +} + +//-------------------------------------------------------------------------------------------------- +// Demo scene state. + +typedef struct OrbLight +{ + float radius; + float speed; + float angle; + CF_Color color; +} OrbLight; + +float time_acc = 0.0f; +int test_scene = 0; + +OrbLight orbs[4]; + +void scene_init() +{ + orbs[0] = (OrbLight){ 140.0f, 0.7f, 0.0f, cf_make_color_rgb_f(1.0f, 0.2f, 0.1f) }; + orbs[1] = (OrbLight){ 160.0f, -0.5f, CF_PI * 0.5f, cf_make_color_rgb_f(0.1f, 1.0f, 0.2f) }; + orbs[2] = (OrbLight){ 120.0f, 0.9f, CF_PI, cf_make_color_rgb_f(0.2f, 0.3f, 1.0f) }; + orbs[3] = (OrbLight){ 180.0f, -0.3f, CF_PI * 1.5f, cf_make_color_rgb_f(1.0f, 0.9f, 0.1f) }; +} + +//-------------------------------------------------------------------------------------------------- +// Input handling. + +void handle_input() +{ + if (cf_key_just_pressed(CF_KEY_D)) { + hrc.debug_mode = (hrc.debug_mode + 1) % HRC_NUM_DEBUG; + } + if (cf_key_just_pressed(CF_KEY_T)) { + test_scene = !test_scene; + } + if (cf_key_just_pressed(CF_KEY_H)) { + if (hrc.grid == 128) hrc_set_grid(256); + else if (hrc.grid == 256) hrc_set_grid(512); + else if (hrc.grid == 512) hrc_set_grid(1024); + else hrc_set_grid(128); + if (hrc.trace_levels > hrc.n + 1) + hrc.trace_levels = hrc.n + 1; + } + if (cf_key_just_pressed(CF_KEY_R)) { + hrc.trace_levels = (hrc.trace_levels + 1) % (hrc.n + 2); + } + if (cf_key_just_pressed(CF_KEY_C)) { + hrc.cminus1 = !hrc.cminus1; + } + if (cf_key_just_pressed(CF_KEY_M)) { + hrc.upscale_mode = (hrc.upscale_mode + 1) % 3; + } + if (cf_key_just_pressed(CF_KEY_P)) { + hrc.prefilter_mode = (hrc.prefilter_mode + 1) % 5; + } + if (cf_key_just_pressed(CF_KEY_B)) { + hrc.blend_boundary = !hrc.blend_boundary; + } + if (cf_key_just_pressed(CF_KEY_LEFTBRACKET)) { + hrc.blend_width = cf_max(0.5f, hrc.blend_width - 0.5f); + } + if (cf_key_just_pressed(CF_KEY_RIGHTBRACKET)) { + hrc.blend_width += 0.5f; + } +} + +//-------------------------------------------------------------------------------------------------- +// Drawing helpers. + +void begin_canvas_draw() +{ + cf_draw_push(); + float ws = (float)HRC_WORLD_SIZE; + float half = ws * 0.5f; + cf_draw_TSR_absolute(cf_v2(0, 0), cf_v2(1, 1), 0); + cf_draw_projection(cf_ortho_2d(0, 0, ws, ws)); + cf_draw_translate(-half, -half); +} + +void end_canvas_draw() +{ + cf_draw_pop(); +} + +void draw_circle_at(float x, float y, float r) +{ + cf_draw_circle_fill2(cf_v2(x, y), r); +} + +// Push a render state with premultiplied alpha blending and the correct pixel format for f16 canvases. +void push_f16_render_state() +{ + CF_RenderState rs = cf_render_state_defaults(); + rs.blend.pixel_format = CF_PIXEL_FORMAT_R16G16B16A16_FLOAT; + rs.blend.rgb_src_blend_factor = CF_BLENDFACTOR_ONE; + rs.blend.rgb_dst_blend_factor = CF_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + rs.blend.rgb_op = CF_BLEND_OP_ADD; + rs.blend.alpha_src_blend_factor = CF_BLENDFACTOR_ONE; + rs.blend.alpha_dst_blend_factor = CF_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + rs.blend.alpha_op = CF_BLEND_OP_ADD; + cf_draw_push_render_state(rs); +} + +// Per-frame light positions (computed once, drawn into both canvases). +typedef struct Light +{ + float x, y, r; + CF_Color color; +} Light; + +#define MAX_LIGHTS 16 +Light frame_lights[MAX_LIGHTS]; +int frame_light_count; + +// Compute all light positions for this frame and store them. +void update_lights() +{ + float half = (float)HRC_WORLD_SIZE * 0.5f; + float ws = (float)HRC_WORLD_SIZE; + float dt = CF_DELTA_TIME; + time_acc += dt; + frame_light_count = 0; + + // Orbiting lights. + for (int i = 0; i < 4; i++) { + orbs[i].angle += orbs[i].speed * dt; + float cx = half + cosf(orbs[i].angle) * orbs[i].radius; + float cy = half + sinf(orbs[i].angle) * orbs[i].radius; + frame_lights[frame_light_count++] = (Light){ cx, cy, 15.0f, orbs[i].color }; + } + + // Corner accent lights. + { + float margin = 60.0f; + CF_Color corners[4] = { + cf_make_color_rgb_f(0.0f, 0.4f, 0.4f), + cf_make_color_rgb_f(0.5f, 0.3f, 0.0f), + cf_make_color_rgb_f(0.4f, 0.1f, 0.3f), + cf_make_color_rgb_f(0.2f, 0.5f, 0.0f), + }; + float lx[4] = { margin, ws - margin, margin, ws - margin }; + float ly[4] = { margin, margin, ws - margin, ws - margin }; + for (int i = 0; i < 4; i++) { + frame_lights[frame_light_count++] = (Light){ lx[i], ly[i], 8.0f, corners[i] }; + } + } +} + +// Draw all lights as colored circles (for emissivity canvas). +void draw_lights_emissive() +{ + for (int i = 0; i < frame_light_count; i++) { + cf_draw_push_color(frame_lights[i].color); + draw_circle_at(frame_lights[i].x, frame_lights[i].y, frame_lights[i].r); + cf_draw_pop_color(); + } +} + +// Draw all lights as white circles (for absorption canvas). +// Light sources are opaque emitters -- they must absorb to emit. +void draw_lights_absorbing() +{ + cf_draw_push_color(cf_make_color_rgb_f(1.0f, 1.0f, 1.0f)); + for (int i = 0; i < frame_light_count; i++) { + draw_circle_at(frame_lights[i].x, frame_lights[i].y, frame_lights[i].r); + } + cf_draw_pop_color(); +} + +//-------------------------------------------------------------------------------------------------- +// Draw emissivity (lights). + +void draw_emissivity() +{ + begin_canvas_draw(); + push_f16_render_state(); + + if (test_scene) { + float half = (float)HRC_WORLD_SIZE * 0.5f; + float s = (float)HRC_WORLD_SIZE / (float)hrc.grid; + cf_draw_push_color(cf_make_color_rgb_f(1.0f, 1.0f, 1.0f)); + cf_draw_quad_fill(cf_make_aabb(cf_v2(half - s, half - s), cf_v2(half + s, half + s)), 0); + cf_draw_pop_color(); + } else { + draw_lights_emissive(); + } + + cf_draw_pop_render_state(); + cf_render_to(hrc.emissivity, true); + end_canvas_draw(); +} + +//-------------------------------------------------------------------------------------------------- +// Draw absorption (shadow casters + light sources). + +void draw_absorption() +{ + begin_canvas_draw(); + push_f16_render_state(); + + if (test_scene) { + float half = (float)HRC_WORLD_SIZE * 0.5f; + float s = (float)HRC_WORLD_SIZE / (float)hrc.grid; + cf_draw_push_color(cf_make_color_rgb_f(1.0f, 1.0f, 1.0f)); + cf_draw_quad_fill(cf_make_aabb(cf_v2(half - s, half - s), cf_v2(half + s, half + s)), 0); + cf_draw_pop_color(); + } else { + cf_draw_push_color(cf_make_color_rgb_f(1.0f, 1.0f, 1.0f)); + + float ws = (float)HRC_WORLD_SIZE; + float half = ws * 0.5f; + + // 5 circular pillars in quincunx pattern. + draw_circle_at(half, half, 25.0f); + draw_circle_at(half - 110.0f, half - 110.0f, 20.0f); + draw_circle_at(half + 110.0f, half - 110.0f, 20.0f); + draw_circle_at(half - 110.0f, half + 110.0f, 20.0f); + draw_circle_at(half + 110.0f, half + 110.0f, 20.0f); + + // 2 angled wall segments. + cf_draw_quad_fill2( + cf_v2(100.0f, 200.0f), cf_v2(110.0f, 200.0f), + cf_v2(200.0f, 310.0f), cf_v2(190.0f, 310.0f), 0 + ); + cf_draw_quad_fill2( + cf_v2(400.0f, 200.0f), cf_v2(390.0f, 200.0f), + cf_v2(300.0f, 310.0f), cf_v2(310.0f, 310.0f), 0 + ); + + // 3 small scattered square blocks. + cf_draw_quad_fill(cf_make_aabb(cf_v2(340.0f, 400.0f), cf_v2(365.0f, 425.0f)), 0); + cf_draw_quad_fill(cf_make_aabb(cf_v2(150.0f, 380.0f), cf_v2(175.0f, 405.0f)), 0); + cf_draw_quad_fill(cf_make_aabb(cf_v2(370.0f, 120.0f), cf_v2(395.0f, 145.0f)), 0); + + cf_draw_pop_color(); + + // Light sources must absorb to emit (radiative transfer: rad = emiss * (1 - T)). + draw_lights_absorbing(); + } + + cf_draw_pop_render_state(); + cf_render_to(hrc.absorption, true); + end_canvas_draw(); +} + +//-------------------------------------------------------------------------------------------------- +// Display fluence onto screen. + +void display_fluence() +{ + CF_Canvas display = hrc.fluence; + if (hrc.debug_mode == 6) display = hrc.emissivity; + else if (hrc.debug_mode == 7) display = hrc.absorption; + + float ws = (float)HRC_WORLD_SIZE; + cf_draw_canvas(display, cf_v2(0, 0), cf_v2(ws, ws)); +} + +//-------------------------------------------------------------------------------------------------- +// HUD overlay. + +void draw_hud() +{ + const char* mode_names[] = { + "0: Normal (blur)", + "1: Rot 0 (+x)", + "2: Rot 1 (+y)", + "3: Rot 2 (-x)", + "4: Rot 3 (-y)", + "5: All rotations (no blur)", + "6: Emissivity", + "7: Absorption", + }; + + char buf[512]; + snprintf(buf, sizeof(buf), + "[B] Blend boundary: %s\n" + "[[] []] Blend width: %.1f\n" + "[C] c-1 gathering: %s\n" + "[D] Debug: %s\n" + "[H] Grid: %d (%s)\n" + "[M] Upscale: %s\n" + "[P] Prefilter: %s\n" + "[R] Trace: %d / %d\n" + "[T] Test scene: %s", + hrc.blend_boundary ? "on" : "off", + hrc.blend_width, + hrc.cminus1 ? "on" : "off", + mode_names[hrc.debug_mode], + hrc.grid, hrc.grid == HRC_WORLD_SIZE ? "1x" : hrc.grid == HRC_WORLD_SIZE/2 ? "2x" : hrc.grid == HRC_WORLD_SIZE/4 ? "4x" : "8x", + hrc.upscale_mode == 0 ? "nearest" : hrc.upscale_mode == 1 ? "minmax" : "bilateral", + hrc.prefilter_mode == 0 ? "off" : hrc.prefilter_mode == 1 ? "box" : hrc.prefilter_mode == 2 ? "mipmap" : hrc.prefilter_mode == 3 ? "gauss" : "mip+max", + hrc.trace_levels, hrc.n + 1, + test_scene ? "on" : "off" + ); + + float half = (float)HRC_WORLD_SIZE * 0.5f; + cf_draw_push_color(cf_color_white()); + cf_push_font_size(16.0f); + cf_draw_text(buf, cf_v2(-half + 10.0f, half - 20.0f), -1); + cf_pop_font_size(); + cf_draw_pop_color(); +} + +//-------------------------------------------------------------------------------------------------- +// Entry point. + +int main(int argc, char* argv[]) +{ + cf_make_app("HRC - Holographic Radiance Cascades", 0, 0, 0, HRC_WORLD_SIZE, HRC_WORLD_SIZE, CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT, argv[0]); + + hrc_init(); + scene_init(); + while (cf_app_is_running()) { + cf_app_update(NULL); + cf_draw_push_shape_aa(0); + handle_input(); + update_lights(); + draw_emissivity(); + draw_absorption(); + hrc_compute(); + display_fluence(); + draw_hud(); + cf_app_draw_onto_screen(true); + } + + hrc_shutdown(); + cf_destroy_app(); + return 0; +} diff --git a/samples/hrc_data/hrc_composite.c_shd b/samples/hrc_data/hrc_composite.c_shd new file mode 100644 index 000000000..c48ea77bb --- /dev/null +++ b/samples/hrc_data/hrc_composite.c_shd @@ -0,0 +1,373 @@ +// Sum 4 quadrants with c-1 gathering, cross blur, output (Sec 4.2, Eq 21). +// +// Each quadrant's R_0 was computed in +x-aligned space by the merge-down phase +// and stored in F_0..F_3. This shader reverses the rotation, raymarches a c-1 +// interval per quadrant toward the nearest c0 probe to bridge the gap between the +// pixel and the R_0 probe at +1 offset, merges c-1 with R_0, applies the cross +// blur to remove checkerboard artifacts, and writes the final fluence. +// +// The cascade grid (u_grid_size) may be smaller than the output (u_world_size). +// Frustum buffers are grid-sized; the output fluence and scene textures are world-sized. +// c-1 sampling at world resolution provides per-pixel detail even when upscaling. +// +// Pipeline: [trace x 3] -> [extend x (N-2)] -> [merge x N] -> composite +// ^^^^^^^^^ +// Inputs: +// u_emissivity — scene emissivity (for c-1 trace) +// u_absorption — scene absorption (for c-1 trace + cross blur opacity test) +// u_f0..u_f3 — R_0 per quadrant (grid_size x grid_size, uvec2 f16) +// +// Outputs: +// u_fluence — final fluence image (rgba8, world_size x world_size) + +layout(set = 0, binding = 0) uniform sampler2D u_emissivity; +layout(set = 0, binding = 1) uniform sampler2D u_absorption; +layout(set = 0, binding = 2) uniform sampler2D u_minmax; +layout(std430, set = 0, binding = 3) readonly buffer F0 { uvec2 data[]; } u_f0; +layout(std430, set = 0, binding = 4) readonly buffer F1 { uvec2 data[]; } u_f1; +layout(std430, set = 0, binding = 5) readonly buffer F2 { uvec2 data[]; } u_f2; +layout(std430, set = 0, binding = 6) readonly buffer F3 { uvec2 data[]; } u_f3; +layout(set = 1, binding = 0, rgba8) uniform writeonly image2D u_fluence; + +layout(set = 2, binding = 0) uniform Params +{ + int u_world_size; // output resolution (e.g. 512) + int u_grid_size; // cascade grid resolution (e.g. 256) + float u_abs_threshold; // opacity similarity cutoff for cross blur + int u_debug_mode; + int u_cminus1; // 0 = skip c-1 gathering, 1 = raymarch c-1 + int u_upscale_mode; // 0 = nearest, 1 = minmax bilinear, 2 = joint bilateral +}; + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +struct Interval +{ + vec3 rad; + vec3 trn; +}; + +vec4 f16_load(uvec2 packed) +{ + return vec4(unpackHalf2x16(packed.x), unpackHalf2x16(packed.y)); +} + +vec3 srgb_from_linear(vec3 c) +{ + return pow(c, vec3(1.0 / 2.2)); +} + +vec3 linear_from_srgb(vec3 c) +{ + return pow(c, vec3(2.2)); +} + +// Inverse of seed's rotate_uv, in integer pixel space. +// Operates in grid-space coordinates (grid_size x grid_size). +// +// Seed forward (rotated -> world): +// rot 0: (rx, ry) -> (rx, ry) +// rot 1: (rx, ry) -> (s-ry, rx) +// rot 2: (rx, ry) -> (s-rx, s-ry) +// rot 3: (rx, ry) -> (ry, s-rx) +// +// Inverse (world -> rotated): +ivec2 grid_to_rotated(ivec2 p, int rot, int s) +{ + if (rot == 1) return ivec2(p.y, s - p.x); + if (rot == 2) return ivec2(s - p.x, s - p.y); + if (rot == 3) return ivec2(s - p.y, p.x); + return p; +} + +// Load fluence from one quadrant's F buffer at a rotated grid coordinate. +// No offset applied here -- caller is responsible for the +1 shift. +vec3 F_load(int rot, ivec2 rotated_pos) +{ + if (rotated_pos.x < 0 || rotated_pos.x >= u_grid_size || rotated_pos.y < 0 || rotated_pos.y >= u_grid_size) return vec3(0.0); + int idx = rotated_pos.y * u_grid_size + rotated_pos.x; + vec4 val; + if (rot == 0) val = f16_load(u_f0.data[idx]); + else if (rot == 1) val = f16_load(u_f1.data[idx]); + else if (rot == 2) val = f16_load(u_f2.data[idx]); + else val = f16_load(u_f3.data[idx]); + return val.rgb; +} + +// World-space march direction for each quadrant's rotated +x axis. +// rot 0: +x in rotated -> +x in world +// rot 1: +x in rotated -> +y in world +// rot 2: +x in rotated -> -x in world +// rot 3: +x in rotated -> -y in world +ivec2 quadrant_dir(int rot) +{ + if (rot == 1) return ivec2(0, 1); + if (rot == 2) return ivec2(-1, 0); + if (rot == 3) return ivec2(0, -1); + return ivec2(1, 0); +} + +// Raymarch c-1 interval from the current pixel toward the nearest c0 probe. +// Covers the gap between the pixel and the R_0 probe at +1 grid offset in +// the quadrant's +x direction. The result is NOT stored in cascade memory. +// +// With upscaling (world_size > grid_size), the probe is S = world_size/grid_size +// world pixels away from the grid cell origin. The march steps through each +// intervening world pixel, accumulating emission and transmittance via Beer's law. +// At 1x (no upscaling) this reduces to a single-pixel tap matching c0 probe spacing. +Interval trace_cminus1(ivec2 world_pos, int rot) +{ + int S = u_world_size / u_grid_size; + ivec2 dir = quadrant_dir(rot); + + // Effective coordinate along the march direction (increases toward the probe). + int pos_along; + if (rot == 0) pos_along = world_pos.x; + else if (rot == 1) pos_along = world_pos.y; + else if (rot == 2) pos_along = (u_world_size - 1) - world_pos.x; + else pos_along = (u_world_size - 1) - world_pos.y; + + // Exact number of world pixels from here to the c0 probe at +1 grid offset. + int grid_along = pos_along * u_grid_size / u_world_size; + int num_steps = (grid_along + 1) * S - pos_along; + + float world = float(u_world_size); + float step_abs = 1.0 / float(S); + + vec3 acc_rad = vec3(0.0); + vec3 acc_trn = vec3(1.0); + + for (int i = 0; i < num_steps; i++) { + ivec2 p = world_pos + dir * i; + vec2 uv = (vec2(p) + 0.5) / world; + + vec3 emiss = linear_from_srgb(texture(u_emissivity, uv).rgb); + vec3 absrp = linear_from_srgb(texture(u_absorption, uv).rgb); + + vec3 trans = exp(-absrp * step_abs); + acc_rad += acc_trn * emiss * (vec3(1.0) - trans); + acc_trn *= trans; + } + + return Interval(acc_rad, acc_trn); +} + +// Read min/max absorption from the world-resolution minmax texture. +// Takes a grid position, converts to a world coordinate for the fetch. +// All world pixels in the same grid cell store the same value. +vec2 minmax_at(ivec2 grid_pos) +{ + grid_pos = clamp(grid_pos, ivec2(0), ivec2(u_grid_size - 1)); + int S = u_world_size / u_grid_size; + return texelFetch(u_minmax, grid_pos * S, 0).rg; +} + +// Read absorption magnitude at a world pixel for the opacity similarity check. +float absorption_at(ivec2 pos) +{ + float world = float(u_world_size); + vec2 uv = (vec2(pos) + 0.5) / world; + vec3 a = texture(u_absorption, uv).rgb; + return dot(a, vec3(0.333)); +} + +// Sum fluence from all 4 quadrants at a world-space pixel, with c-1 gathering. +// Each quadrant covers one 90 degree wedge of the full 2pi. +// +// Maps world_pos to the grid, rotates into each quadrant's space to read R_0, +// then merges with a c-1 interval sampled at full world resolution. +// +// Upscale modes (u_upscale_mode): +// 0 = nearest-neighbor +// 1 = minmax bilinear: bilinear in smooth areas, nearest at absorption edges +// 2 = joint bilateral: bilinear weighted by absorption similarity at each probe +vec3 sum_quadrants(ivec2 world_pos) +{ + int s = u_grid_size - 1; + ivec2 grid_pos = world_pos * u_grid_size / u_world_size; + + // Interpolation state (shared by minmax and bilateral paths). + vec2 fgrid; + ivec2 g0; + vec2 frac_pos; + float blend = 0.0; // minmax: 0=nearest, 1=bilinear + float bw00 = 1.0, bw10 = 1.0, bw01 = 1.0, bw11 = 1.0; // bilateral weights + + if (u_upscale_mode != 0 && u_world_size > u_grid_size) { + fgrid = vec2(world_pos) * float(u_grid_size) / float(u_world_size) - 0.5; + g0 = ivec2(floor(fgrid)); + frac_pos = fract(fgrid); + + if (u_upscale_mode == 1) { + // Minmax: check absorption range across neighboring grid cells. + float max_range = 0.0; + for (int dy = 0; dy <= 1; dy++) { + for (int dx = 0; dx <= 1; dx++) { + vec2 mm = minmax_at(g0 + ivec2(dx, dy)); + max_range = max(max_range, mm.g - mm.r); + } + } + blend = 1.0 - smoothstep(0.0, u_abs_threshold, max_range); + } else if (u_upscale_mode == 2) { + // Joint bilateral: weight probes by absorption similarity. + blend = 1.0; + float center_abs = absorption_at(world_pos); + int S = u_world_size / u_grid_size; + float sigma = max(u_abs_threshold, 0.01); + float inv_2s2 = 1.0 / (2.0 * sigma * sigma); + + // Absorption at each probe's world position (center of grid cell). + float d00 = absorption_at(clamp(g0, ivec2(0), ivec2(s)) * S + S / 2) - center_abs; + float d10 = absorption_at(clamp(g0 + ivec2(1,0), ivec2(0), ivec2(s)) * S + S / 2) - center_abs; + float d01 = absorption_at(clamp(g0 + ivec2(0,1), ivec2(0), ivec2(s)) * S + S / 2) - center_abs; + float d11 = absorption_at(clamp(g0 + ivec2(1,1), ivec2(0), ivec2(s)) * S + S / 2) - center_abs; + + bw00 = exp(-d00 * d00 * inv_2s2); + bw10 = exp(-d10 * d10 * inv_2s2); + bw01 = exp(-d01 * d01 * inv_2s2); + bw11 = exp(-d11 * d11 * inv_2s2); + } + } + + vec3 total = vec3(0.0); + for (int rot = 0; rot < 4; rot++) { + vec3 r0; + + if (blend > 0.0) { + // Load R_0 from 4 neighboring grid probes. + vec3 r00 = F_load(rot, grid_to_rotated(clamp(g0, ivec2(0), ivec2(s)), rot, s) + ivec2(1, 0)); + vec3 r10 = F_load(rot, grid_to_rotated(clamp(g0 + ivec2(1,0), ivec2(0), ivec2(s)), rot, s) + ivec2(1, 0)); + vec3 r01 = F_load(rot, grid_to_rotated(clamp(g0 + ivec2(0,1), ivec2(0), ivec2(s)), rot, s) + ivec2(1, 0)); + vec3 r11 = F_load(rot, grid_to_rotated(clamp(g0 + ivec2(1,1), ivec2(0), ivec2(s)), rot, s) + ivec2(1, 0)); + + if (u_upscale_mode == 2) { + // Joint bilateral: spatial bilinear * absorption similarity. + float w00 = (1.0 - frac_pos.x) * (1.0 - frac_pos.y) * bw00; + float w10 = frac_pos.x * (1.0 - frac_pos.y) * bw10; + float w01 = (1.0 - frac_pos.x) * frac_pos.y * bw01; + float w11 = frac_pos.x * frac_pos.y * bw11; + float wt = w00 + w10 + w01 + w11; + if (wt > 0.001) { + r0 = (r00 * w00 + r10 * w10 + r01 * w01 + r11 * w11) / wt; + } else { + // All probes dissimilar — fall back to nearest. + ivec2 rp = grid_to_rotated(grid_pos, rot, s); + r0 = F_load(rot, rp + ivec2(1, 0)); + } + } else { + // Minmax bilinear. + vec3 r0_bilinear = mix(mix(r00, r10, frac_pos.x), mix(r01, r11, frac_pos.x), frac_pos.y); + if (blend < 1.0) { + ivec2 rp = grid_to_rotated(grid_pos, rot, s); + vec3 r0_nearest = F_load(rot, rp + ivec2(1, 0)); + r0 = mix(r0_nearest, r0_bilinear, blend); + } else { + r0 = r0_bilinear; + } + } + } else { + // Pure nearest-neighbor. + ivec2 rp = grid_to_rotated(grid_pos, rot, s); + r0 = F_load(rot, rp + ivec2(1, 0)); + } + + // c-1 gathering: trace to nearest probe, merge with (possibly interpolated) R_0. + if (u_cminus1 != 0) { + Interval cminus1 = trace_cminus1(world_pos, rot); + total += cminus1.rad + cminus1.trn * r0; + } else { + total += r0; + } + } + return total; +} + +// Shared memory tile for precomputed sum_quadrants results. +// 16x16 workgroup + 1-pixel blur halo on each side = 18x18 tile. +// Precomputing avoids calling sum_quadrants 9x per pixel inside +// the blur, which causes shader compilation timeouts on some drivers. +#define BLUR_R 1 +#define TILE_W (16 + 2 * BLUR_R) +shared vec3 s_tile[TILE_W * TILE_W]; + +// Accumulate a blur tap from the shared memory tile, skipping +// out-of-bounds pixels and absorption discontinuities. +void tile_tap(ivec2 offset, float w, float center_abs, ivec2 world_center, ivec2 tc, inout vec3 sum, inout float weight) +{ + ivec2 np = world_center + offset; + if (np.x < 0 || np.x >= u_world_size || np.y < 0 || np.y >= u_world_size) return; + if (abs(absorption_at(np) - center_abs) > u_abs_threshold) return; + ivec2 t = tc + offset; + sum += s_tile[t.y * TILE_W + t.x] * w; + weight += w; +} + +// 3x3 cross blur from the HRC paper. +// +// K = [1 2 1; 2 4 2; 1 2 1] / 16 +// +// Skips neighbors that differ significantly in absorption from +// the center pixel, preventing light leaking through walls. +// When taps are skipped, the total weight is reduced so the +// remaining taps are renormalized. +vec3 blur_tile(ivec2 pos, ivec2 tc) +{ + float center_abs = absorption_at(pos); + vec3 sum = s_tile[tc.y * TILE_W + tc.x] * 4.0; + float weight = 4.0; + + // Cardinal neighbors (weight 2) + tile_tap(ivec2(-1, 0), 2.0, center_abs, pos, tc, sum, weight); + tile_tap(ivec2( 1, 0), 2.0, center_abs, pos, tc, sum, weight); + tile_tap(ivec2( 0, -1), 2.0, center_abs, pos, tc, sum, weight); + tile_tap(ivec2( 0, 1), 2.0, center_abs, pos, tc, sum, weight); + + // Diagonal neighbors (weight 1) + tile_tap(ivec2(-1, -1), 1.0, center_abs, pos, tc, sum, weight); + tile_tap(ivec2( 1, -1), 1.0, center_abs, pos, tc, sum, weight); + tile_tap(ivec2(-1, 1), 1.0, center_abs, pos, tc, sum, weight); + tile_tap(ivec2( 1, 1), 1.0, center_abs, pos, tc, sum, weight); + + return sum / weight; +} + +void main() +{ + ivec2 gid = ivec2(gl_GlobalInvocationID.xy); + ivec2 lid = ivec2(gl_LocalInvocationID.xy); + + // Fill shared memory tile with precomputed sum_quadrants. + ivec2 tile_origin = ivec2(gl_WorkGroupID.xy) * 16 - BLUR_R; + for (uint i = gl_LocalInvocationIndex; i < uint(TILE_W * TILE_W); i += 256u) { + ivec2 tp = tile_origin + ivec2(int(i) % TILE_W, int(i) / TILE_W); + s_tile[i] = (tp.x >= 0 && tp.x < u_world_size && tp.y >= 0 && tp.y < u_world_size) + ? sum_quadrants(tp) : vec3(0.0); + } + barrier(); + + if (gid.x >= u_world_size || gid.y >= u_world_size) return; + + vec3 fluence; + if (u_debug_mode >= 1 && u_debug_mode <= 4) { + // Show a single quadrant, no blur. + int rot = u_debug_mode - 1; + int s = u_grid_size - 1; + ivec2 grid_pos = gid * u_grid_size / u_world_size; + ivec2 rp = grid_to_rotated(grid_pos, rot, s); + vec3 r0 = F_load(rot, rp + ivec2(1, 0)); + if (u_cminus1 != 0) { + Interval cminus1 = trace_cminus1(gid, rot); + fluence = cminus1.rad + cminus1.trn * r0; + } else { + fluence = r0; + } + } else if (u_debug_mode == 5) { + // Sum all quadrants with c-1 gathering, no blur. + ivec2 tc = lid + BLUR_R; + fluence = s_tile[tc.y * TILE_W + tc.x]; + } else { + fluence = blur_tile(gid, lid + BLUR_R); + } + + imageStore(u_fluence, gid, vec4(srgb_from_linear(fluence), 1.0)); +} diff --git a/samples/hrc_data/hrc_copy.c_shd b/samples/hrc_data/hrc_copy.c_shd new file mode 100644 index 000000000..3f3933071 --- /dev/null +++ b/samples/hrc_data/hrc_copy.c_shd @@ -0,0 +1,19 @@ +// Copy one SSBO to another. +// Used to copy R_0 (merge-down result) into F[rot] (per-quadrant output). + +layout(std430, set = 0, binding = 0) readonly buffer Src { uvec2 data[]; } u_src; +layout(std430, set = 1, binding = 0) writeonly buffer Dst { uvec2 data[]; } u_dst; + +layout(set = 2, binding = 0) uniform Params +{ + int u_count; +}; + +layout(local_size_x = 256, local_size_y = 1, local_size_z = 1) in; + +void main() +{ + int gid = int(gl_GlobalInvocationID.x); + if (gid >= u_count) return; + u_dst.data[gid] = u_src.data[gid]; +} diff --git a/samples/hrc_data/hrc_extend.c_shd b/samples/hrc_data/hrc_extend.c_shd new file mode 100644 index 000000000..9a169991a --- /dev/null +++ b/samples/hrc_data/hrc_extend.c_shd @@ -0,0 +1,153 @@ +// Builds the ray interval hierarchy T_0 .. T_N (Sec 4.1). +// +// T_n stores precomputed ray intervals at cascade level n. Each entry holds radiance + +// transmittance for a ray segment of length 2^n pixels, starting from a probe and +// pointing in one of (2^n + 1) directions spanning the +x quadrant. +// +// T_0 is seeded by hrc_seed.comp (1px rays from scene data). Each subsequent level +// doubles ray length by chaining two T_{n-1} intervals end-to-end (Eq 18-20). +// +// The merge-down phase later uses T as a lookup table -- when it needs "what happens +// along a ray from A to B", it reads the answer from T instead of raymarching. +// +// Note: T_n here refers to the acceleration structure, not transmittance (T_r), which +// appears as the .trn field of an Interval. +// +// Pipeline: seed -> [extend x N] -> merge -> composite +// ^^^^^^^^^^ +// Inputs: +// u_prev_rad, u_prev_trn — T_{n-1} ray intervals (from seed or previous extend) +// u_cascade — cascade level n being built +// u_prev_w, u_curr_w — buffer widths for T_{n-1} and T_n +// +// Outputs: +// u_curr_rad, u_curr_trn — T_n ray intervals +// +// Buffer layout (same as seed): +// width = (world_size / 2^n) * (2^n + 1) +// height = world_size +// index = y * width + probe_x * directions + k + +layout(std430, set = 0, binding = 0) readonly buffer PrevRad { uvec2 data[]; } u_prev_rad; +layout(std430, set = 0, binding = 1) readonly buffer PrevTrn { uvec2 data[]; } u_prev_trn; +layout(std430, set = 1, binding = 0) writeonly buffer CurrRad { uvec2 data[]; } u_curr_rad; +layout(std430, set = 1, binding = 1) writeonly buffer CurrTrn { uvec2 data[]; } u_curr_trn; + +layout(set = 2, binding = 0) uniform Params +{ + int u_cascade; // n: the level we're building + int u_world_size; + int u_prev_w; // width of T_{n-1} buffer + int u_curr_w; // width of T_n buffer +}; + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +struct Interval +{ + vec3 rad; + vec3 trn; +}; + +Interval merge(Interval a, Interval b) +{ + return Interval( + a.rad + a.trn * b.rad, + a.trn * b.trn + ); +} + +vec4 f16_load(uvec2 packed) +{ + return vec4(unpackHalf2x16(packed.x), unpackHalf2x16(packed.y)); +} + +uvec2 f16_store(vec4 val) +{ + return uvec2(packHalf2x16(val.xy), packHalf2x16(val.zw)); +} + +// Load an interval from T_{n-1}. +// probe_x: the probe's x index (not pixel position) +// k: direction index (integer) +// y: probe y position +Interval T_load_prev(int probe_x, int k, int y) +{ + int prev_n = u_cascade - 1; + int directions = (1 << prev_n) + 1; + int num_probes = u_world_size >> prev_n; + + // Bounds check: return empty interval for out-of-range lookups. + if (probe_x < 0 || probe_x >= num_probes || k < 0 || k >= directions || y < 0 || y >= u_world_size) { + return Interval(vec3(0.0), vec3(1.0)); + } + + int idx = y * u_prev_w + probe_x * directions + k; + vec4 rad = f16_load(u_prev_rad.data[idx]); + vec4 trn = f16_load(u_prev_trn.data[idx]); + return Interval(rad.rgb, trn.rgb); +} + +// Compute the y-offset of a ray endpoint in T_{n-1} direction k. +// From v_{n-1}(k) = (2^{n-1}, 2k - 2^{n-1}), the y component is 2k - 2^{n-1}. +int far_y_offset(int k) +{ + int prev_pow2 = 1 << (u_cascade - 1); + return 2 * k - prev_pow2; +} + +// Eq 18-20: extend ray from T_{n-1} to T_n. +// +// Builds a T_n ray by chaining two T_{n-1} rays. When the target direction k maps +// to an integer direction in T_{n-1} (even k), both candidates are identical and the +// average is a no-op. When k is odd, the two candidates bend in opposite directions +// and averaging approximates the true straight ray. We always run both paths to avoid +// warp divergence from even/odd branching. +// +// Candidate A: near=k_lo, far=k_hi Candidate B: near=k_hi, far=k_lo +// +// *---k_lo--->* *---k_hi--->* +// \ / +// k_hi \ / k_lo +// * * +// +Interval extend(int probe_x, int k, int y) +{ + int k_lo = k / 2; + int k_hi = (k + 1) / 2; + int prev_probe_x = probe_x * 2; + int mid_probe_x = prev_probe_x + 1; + + // Candidate A: go k_lo then k_hi + Interval near_a = T_load_prev(prev_probe_x, k_lo, y); + int far_y_a = y + far_y_offset(k_lo); + Interval far_a = T_load_prev(mid_probe_x, k_hi, far_y_a); + Interval A = merge(near_a, far_a); + + // Candidate B: go k_hi then k_lo + Interval near_b = T_load_prev(prev_probe_x, k_hi, y); + int far_y_b = y + far_y_offset(k_hi); + Interval far_b = T_load_prev(mid_probe_x, k_lo, far_y_b); + Interval B = merge(near_b, far_b); + + return Interval( + (A.rad + B.rad) * 0.5, + (A.trn + B.trn) * 0.5 + ); +} + +void main() +{ + ivec2 gid = ivec2(gl_GlobalInvocationID.xy); + if (gid.x >= u_curr_w || gid.y >= u_world_size) return; + + int directions = (1 << u_cascade) + 1; + int probe_x = gid.x / directions; + int k = gid.x - probe_x * directions; + + Interval iv = extend(probe_x, k, gid.y); + + int out_idx = gid.y * u_curr_w + gid.x; + u_curr_rad.data[out_idx] = f16_store(vec4(iv.rad, 1.0)); + u_curr_trn.data[out_idx] = f16_store(vec4(iv.trn, 1.0)); +} diff --git a/samples/hrc_data/hrc_merge.c_shd b/samples/hrc_data/hrc_merge.c_shd new file mode 100644 index 000000000..b550f85d4 --- /dev/null +++ b/samples/hrc_data/hrc_merge.c_shd @@ -0,0 +1,256 @@ +// Builds angular fluence R_n from R_{n+1} (Sec 4, Eq 14-17). +// +// R_n(p, i) is the angular fluence at probe p for cone i at cascade n. Each cone +// splits into upper and lower sub-cones that map to R_{n+1}. We trace a ray from +// our probe to the R_{n+1} probe (looked up from T) and merge with the already- +// resolved fluence there. +// +// Odd probe x (Eq 14): no R_{n+1} at our position, trace one T_n step outward +// to reach R_{n+1} probes and merge. +// +// Even probe x (Eq 15-17): R_{n+1} exists here, but direct lookup has different +// bias than odd probes. We average the direct lookup (F0) with a longer trace +// through T_{n+1} (F1) to match the bias. +// +// R_N is implicitly zero (no light beyond scene bounds). Merge-down proceeds +// from R_{N-1} to R_0. R_0 is the final per-pixel fluence for one quadrant. +// +// Host requirement: at the first merge iteration (n = N-1), u_r_prev_rad must +// contain all zeros to represent R_N = 0. This shader trusts the host to zero- +// initialize the R ping buffer before the merge-down loop. +// +// Pipeline: seed -> [extend x N] -> [merge x N] -> composite +// ^^^^^^^^^ +// Inputs: +// u_t_curr_rad, u_t_curr_trn — T_n ray intervals +// u_t_next_rad, u_t_next_trn — T_{n+1} ray intervals (for even probe x) +// u_r_prev_rad — R_{n+1} fluence (ping-pong, zero for first iteration) +// u_cascade — cascade level n being built +// +// Outputs: +// u_r_curr_rad — R_n fluence +// +// Buffer layout: +// T: width = (world_size / 2^n) * (2^n + 1), height = world_size +// R: width = (world_size / 2^n) * 2^n, height = world_size +// R has one fewer slot per probe than T (cones between rays, not rays themselves) + +layout(std430, set = 0, binding = 0) readonly buffer TCurrRad { uvec2 data[]; } u_t_curr_rad; +layout(std430, set = 0, binding = 1) readonly buffer TCurrTrn { uvec2 data[]; } u_t_curr_trn; +layout(std430, set = 0, binding = 2) readonly buffer TNextRad { uvec2 data[]; } u_t_next_rad; +layout(std430, set = 0, binding = 3) readonly buffer TNextTrn { uvec2 data[]; } u_t_next_trn; +layout(std430, set = 0, binding = 4) readonly buffer RPrevRad { uvec2 data[]; } u_r_prev_rad; +layout(std430, set = 1, binding = 0) writeonly buffer RCurrRad { uvec2 data[]; } u_r_curr_rad; + +layout(set = 2, binding = 0) uniform Params +{ + int u_cascade; // n: the level we're building + int u_world_size; + int u_t_curr_w; // width of T_n buffer + int u_t_next_w; // width of T_{n+1} buffer + int u_r_prev_w; // width of R_{n+1} buffer + int u_r_curr_w; // width of R_n buffer +}; + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +int my_mod(int a, int b) { return ((a % b) + b) % b; } + +struct Interval +{ + vec3 rad; + vec3 trn; +}; + +vec4 f16_load(uvec2 packed) +{ + return vec4(unpackHalf2x16(packed.x), unpackHalf2x16(packed.y)); +} + +uvec2 f16_store(vec4 val) +{ + return uvec2(packHalf2x16(val.xy), packHalf2x16(val.zw)); +} + +// Direction vector for the k-th ray at cascade n (Sec 4). +vec2 v_n(int n, float k) +{ + float pow2n = float(1 << n); + return vec2(pow2n, 2.0 * k - pow2n); +} + +// Eq 13: angular size of cone i at cascade n. +float angular_span(int n, float i) +{ + vec2 left = v_n(n, i - 0.5); + vec2 right = v_n(n, i + 0.5); + return atan(right.y, right.x) - atan(left.y, left.x); +} + +// Eq 8: merge a traced interval with a final radiance value. +vec3 merge_r(Interval near, vec3 far_rad) +{ + return near.rad + near.trn * far_rad; +} + +// Load a ray interval from T_n. +Interval T_load(int probe_x, int k, int y) +{ + int n = u_cascade; + int directions = (1 << n) + 1; + int num_probes = u_world_size >> n; + if (probe_x < 0 || probe_x >= num_probes || k < 0 || k >= directions || y < 0 || y >= u_world_size) { + return Interval(vec3(0.0), vec3(1.0)); + } + int idx = y * u_t_curr_w + probe_x * directions + k; + vec4 rad = f16_load(u_t_curr_rad.data[idx]); + vec4 trn = f16_load(u_t_curr_trn.data[idx]); + return Interval(rad.rgb, trn.rgb); +} + +// Load a ray interval from T_{n+1}. +Interval T_load_next(int probe_x, int k, int y) +{ + int n = u_cascade + 1; + int directions = (1 << n) + 1; + int num_probes = u_world_size >> n; + if (probe_x < 0 || probe_x >= num_probes || k < 0 || k >= directions || y < 0 || y >= u_world_size) { + return Interval(vec3(0.0), vec3(1.0)); + } + int idx = y * u_t_next_w + probe_x * directions + k; + vec4 rad = f16_load(u_t_next_rad.data[idx]); + vec4 trn = f16_load(u_t_next_trn.data[idx]); + return Interval(rad.rgb, trn.rgb); +} + +// Load angular fluence from R_{n+1}. +vec3 R_load(int probe_x, int i, int y) +{ + int n = u_cascade + 1; + int directions = 1 << n; + int num_probes = u_world_size >> n; + if (probe_x < 0 || probe_x >= num_probes || i < 0 || i >= directions || y < 0 || y >= u_world_size) { + return vec3(0.0); + } + int idx = y * u_r_prev_w + probe_x * directions + i; + vec4 rad = f16_load(u_r_prev_rad.data[idx]); + return rad.rgb; +} + +// Sub-cone indices and weights for cone i split into cascade n+1. +// Cone i has edges at rays k=i (lower) and k=i+1 (upper). Each edge +// maps to a cone in R_{n+1}: j_minus (lower half) and j_plus (upper half). +// Weights convert radiance to angular fluence (Eq 13). +struct SubCone +{ + int j_plus; // upper sub-cone index in R_{n+1} + int j_minus; // lower sub-cone index in R_{n+1} + float w_plus; // angular span of upper sub-cone + float w_minus; // angular span of lower sub-cone +}; + +SubCone subcone(int i) +{ + SubCone sc; + sc.j_plus = 2 * i + 1; + sc.j_minus = 2 * i; + sc.w_plus = angular_span(u_cascade + 1, float(sc.j_plus) + 0.5) * 0.5; + sc.w_minus = angular_span(u_cascade + 1, float(sc.j_minus) + 0.5) * 0.5; + return sc; +} + +// Eq 14 core: weight a traced interval by angular span and merge with +// resolved fluence from R_{n+1}. This is the fundamental merge-down +// operation — a ray interval attenuates and adds to the far fluence. +vec3 weighted_merge(Interval traced, float weight, vec3 far_fluence) +{ + traced.rad *= weight; + return merge_r(traced, far_fluence); +} + +// Eq 14: merge-down for odd probe x. +// +// q+ ---* R_{n+1} probe, direction j+ +// / | +// T_n / F+ | +// / | +// p --*----------+ +// \ | +// T_n \ F- | +// \ | +// q- ---* R_{n+1} probe, direction j- +// +vec3 merge_odd(int probe_x, int i, int y) +{ + SubCone sc = subcone(i); + int next_probe_x = (probe_x + 1) / 2; + int pow2n = 1 << u_cascade; + + vec3 F_plus = weighted_merge( + T_load(probe_x, i + 1, y), + sc.w_plus, + R_load(next_probe_x, sc.j_plus, y + 2 * (i + 1) - pow2n) + ); + + vec3 F_minus = weighted_merge( + T_load(probe_x, i, y), + sc.w_minus, + R_load(next_probe_x, sc.j_minus, y + 2 * i - pow2n) + ); + + return F_plus + F_minus; +} + +// Eq 15-17: merge-down for even probe x. +// +// R_{n+1} here R_{n+1} there +// | | +// F0: read ------->* | +// | | +// F1: trace ------>*------- T_{n+1} ----->* merge with R_{n+1} +// | | +// result: (F0 + F1) / 2 +// +vec3 merge_even(int probe_x, int i, int y) +{ + SubCone sc = subcone(i); + int next_probe_x = probe_x / 2; + + // F1: trace 2x through T_{n+1} (Eq 17) + // 2*v_n(k) = v_{n+1}(2k), so T_n edges k=i+1,i become T_{n+1} directions 2*(i+1),2*i + int far_probe_x = next_probe_x + 1; + int pow2n1 = 1 << (u_cascade + 1); + + vec3 F1_plus = weighted_merge( + T_load_next(next_probe_x, 2 * (i + 1), y), + sc.w_plus, + R_load(far_probe_x, sc.j_plus, y + 2 * 2 * (i + 1) - pow2n1) + ); + + vec3 F1_minus = weighted_merge( + T_load_next(next_probe_x, 2 * i, y), + sc.w_minus, + R_load(far_probe_x, sc.j_minus, y + 2 * 2 * i - pow2n1) + ); + + // F0: direct lookup (Eq 16) + vec3 F0_plus = R_load(next_probe_x, sc.j_plus, y); + vec3 F0_minus = R_load(next_probe_x, sc.j_minus, y); + + return (F0_plus + F1_plus + F0_minus + F1_minus) * 0.5; +} + +void main() +{ + ivec2 gid = ivec2(gl_GlobalInvocationID.xy); + if (gid.x >= u_r_curr_w || gid.y >= u_world_size) return; + + int directions = 1 << u_cascade; + int probe_x = gid.x / directions; + int i = gid.x - probe_x * directions; + + vec3 fluence = (probe_x % 2 == 0) ? merge_even(probe_x, i, gid.y) : merge_odd(probe_x, i, gid.y); + + int out_idx = gid.y * u_r_curr_w + gid.x; + u_r_curr_rad.data[out_idx] = f16_store(vec4(fluence, 1.0)); +} diff --git a/samples/hrc_data/hrc_minmax.c_shd b/samples/hrc_data/hrc_minmax.c_shd new file mode 100644 index 000000000..e7582396e --- /dev/null +++ b/samples/hrc_data/hrc_minmax.c_shd @@ -0,0 +1,48 @@ +// Mipmap-style box-average absorption luminance per grid cell, at world resolution. +// +// Dispatched at world_size x world_size. Each world pixel determines which +// grid cell it belongs to, averages the absorption luminance over all SxS +// world pixels in that cell, and writes the result to its own position. +// All world pixels in the same grid cell write the same average value. +// +// The composite shader compares averages between neighboring cells to detect +// absorption edges. This is smoother than min/max range, which spikes at +// any single wall pixel in a cell. +// +// Pipeline: [trace] -> [extend] -> [merge] -> mipmap -> [composite] +// ^^^^^^ + +layout(set = 0, binding = 0) uniform sampler2D u_absorption; +layout(set = 1, binding = 0, rg16f) uniform writeonly image2D u_minmax; + +layout(set = 2, binding = 0) uniform Params +{ + int u_world_size; + int u_grid_size; +}; + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +void main() +{ + ivec2 gid = ivec2(gl_GlobalInvocationID.xy); + if (gid.x >= u_world_size || gid.y >= u_world_size) return; + + int S = u_world_size / u_grid_size; + ivec2 grid_cell = gid / S; + ivec2 base = grid_cell * S; + float world = float(u_world_size); + + float total = 0.0; + for (int dy = 0; dy < S; dy++) { + for (int dx = 0; dx < S; dx++) { + ivec2 p = base + ivec2(dx, dy); + vec2 uv = (vec2(p) + 0.5) / world; + vec3 a = texture(u_absorption, uv).rgb; + total += dot(a, vec3(0.333)); + } + } + + float avg = total / float(S * S); + imageStore(u_minmax, gid, vec4(avg, 0.0, 0.0, 0.0)); +} diff --git a/samples/hrc_data/hrc_prefilter.c_shd b/samples/hrc_data/hrc_prefilter.c_shd new file mode 100644 index 000000000..79ba57ee6 --- /dev/null +++ b/samples/hrc_data/hrc_prefilter.c_shd @@ -0,0 +1,60 @@ +// Prefilter scene inputs from world resolution to grid resolution. +// +// Emissivity: box-average (correct energy conservation) +// Absorption: per-channel max (conservative occlusion — prevents light +// leaking through thin geometry that falls between probes) +// +// Dispatched at grid resolution. Each thread reads the SxS block of world +// pixels that map to one grid cell (S = world_size / grid_size), then writes +// the result back to all SxS world pixels in the output images so the trace +// shader's world-space UVs map correctly. +// +// Pipeline: prefilter -> [trace] -> [extend] -> [merge] -> composite +// ^^^^^^^^^ + +layout(set = 0, binding = 0) uniform sampler2D u_emissivity; +layout(set = 0, binding = 1) uniform sampler2D u_absorption; +layout(set = 1, binding = 0, rgba16f) uniform writeonly image2D u_out_emissivity; +layout(set = 1, binding = 1, rgba16f) uniform writeonly image2D u_out_absorption; + +layout(set = 2, binding = 0) uniform Params +{ + int u_world_size; + int u_grid_size; +}; + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +void main() +{ + ivec2 gid = ivec2(gl_GlobalInvocationID.xy); + if (gid.x >= u_grid_size || gid.y >= u_grid_size) return; + + int S = u_world_size / u_grid_size; + ivec2 base = gid * S; + float world = float(u_world_size); + float inv_count = 1.0 / float(S * S); + + vec3 emiss_sum = vec3(0.0); + vec3 absrp_max = vec3(0.0); + + for (int dy = 0; dy < S; dy++) { + for (int dx = 0; dx < S; dx++) { + ivec2 p = base + ivec2(dx, dy); + vec2 uv = (vec2(p) + 0.5) / world; + emiss_sum += texture(u_emissivity, uv).rgb; + absrp_max = max(absrp_max, texture(u_absorption, uv).rgb); + } + } + + vec4 emiss_avg = vec4(emiss_sum * inv_count, 1.0); + vec4 absrp_out = vec4(absrp_max, 1.0); + + // Write to all SxS world pixels so trace shader UVs map correctly. + for (int dy = 0; dy < S; dy++) { + for (int dx = 0; dx < S; dx++) { + imageStore(u_out_emissivity, base + ivec2(dx, dy), emiss_avg); + imageStore(u_out_absorption, base + ivec2(dx, dy), absrp_out); + } + } +} diff --git a/samples/hrc_data/hrc_prefilter_gauss.c_shd b/samples/hrc_data/hrc_prefilter_gauss.c_shd new file mode 100644 index 000000000..f2ecc16e1 --- /dev/null +++ b/samples/hrc_data/hrc_prefilter_gauss.c_shd @@ -0,0 +1,71 @@ +// Gaussian prefilter scene inputs from world resolution to grid resolution. +// +// Emissivity: Gaussian-weighted average (smoother than box, reduces blocky +// artifacts at grid cell boundaries) +// Absorption: per-channel max (conservative occlusion — same as box prefilter) +// +// Dispatched at grid resolution. Each thread reads the SxS block of world +// pixels that map to one grid cell (S = world_size / grid_size), applies +// Gaussian weights to the emissivity sum, then writes the result back to +// all SxS world pixels in the output images. +// +// Pipeline: prefilter -> [trace] -> [extend] -> [merge] -> composite +// ^^^^^^^^^ + +layout(set = 0, binding = 0) uniform sampler2D u_emissivity; +layout(set = 0, binding = 1) uniform sampler2D u_absorption; +layout(set = 1, binding = 0, rgba16f) uniform writeonly image2D u_out_emissivity; +layout(set = 1, binding = 1, rgba16f) uniform writeonly image2D u_out_absorption; + +layout(set = 2, binding = 0) uniform Params +{ + int u_world_size; + int u_grid_size; +}; + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +void main() +{ + ivec2 gid = ivec2(gl_GlobalInvocationID.xy); + if (gid.x >= u_grid_size || gid.y >= u_grid_size) return; + + int S = u_world_size / u_grid_size; + ivec2 base = gid * S; + float world = float(u_world_size); + + // Gaussian sigma: S/3 gives good coverage within the cell. + float sigma = float(S) / 3.0; + float inv_2sigma2 = 1.0 / (2.0 * sigma * sigma); + float center = (float(S) - 1.0) * 0.5; // center of the SxS block + + vec3 emiss_sum = vec3(0.0); + float weight_sum = 0.0; + vec3 absrp_max = vec3(0.0); + + for (int dy = 0; dy < S; dy++) { + for (int dx = 0; dx < S; dx++) { + ivec2 p = base + ivec2(dx, dy); + vec2 uv = (vec2(p) + 0.5) / world; + + float fx = float(dx) - center; + float fy = float(dy) - center; + float w = exp(-(fx * fx + fy * fy) * inv_2sigma2); + + emiss_sum += texture(u_emissivity, uv).rgb * w; + weight_sum += w; + absrp_max = max(absrp_max, texture(u_absorption, uv).rgb); + } + } + + vec4 emiss_avg = vec4(emiss_sum / weight_sum, 1.0); + vec4 absrp_out = vec4(absrp_max, 1.0); + + // Write to all SxS world pixels so trace shader UVs map correctly. + for (int dy = 0; dy < S; dy++) { + for (int dx = 0; dx < S; dx++) { + imageStore(u_out_emissivity, base + ivec2(dx, dy), emiss_avg); + imageStore(u_out_absorption, base + ivec2(dx, dy), absrp_out); + } + } +} diff --git a/samples/hrc_data/hrc_seed.c_shd b/samples/hrc_data/hrc_seed.c_shd new file mode 100644 index 000000000..837dabb37 --- /dev/null +++ b/samples/hrc_data/hrc_seed.c_shd @@ -0,0 +1,94 @@ +// Seed T_0: single-pixel interval at each probe position (no raymarching). +// +// Samples the scene at the probe origin and creates a direction-weighted interval. +// Used when trace_levels = 0 to see the visual difference vs direct raymarching. +// The extend shader's zigzag averaging of these seeds creates manhattan distance +// artifacts at low cascades -- this is expected and is why direct tracing exists. +// +// Pipeline: [seed] -> [extend x N] -> [merge x N] -> composite +// ^^^^^^ +// +// Uses the same bindings as hrc_trace so the material setup is shared. +// +// Output buffer layout (same as trace/extend): +// width = (world_size / 2^n) * (2^n + 1) +// height = world_size +// index = y * width + probe_x * directions + k + +layout(set = 0, binding = 0) uniform sampler2D u_emissivity; +layout(set = 0, binding = 1) uniform sampler2D u_absorption; + +layout(std430, set = 1, binding = 0) writeonly buffer OutRad { uvec2 data[]; } u_out_rad; +layout(std430, set = 1, binding = 1) writeonly buffer OutTrn { uvec2 data[]; } u_out_trn; + +layout(set = 2, binding = 0) uniform Params +{ + int u_cascade; + int u_rotate; + int u_world_size; + int u_curr_w; + float u_mip_level; // emissivity mipmap LOD (0.0 = base level) + float u_absrp_mip_level; // absorption mipmap LOD (0.0 = base level) +}; + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +// Direction vector for the k-th ray at cascade n (Sec 4). +// v_n(k) = (2^n, 2k - 2^n) +vec2 v_n(int n, float k) +{ + float pow2n = float(1 << n); + return vec2(pow2n, 2.0 * k - pow2n); +} + +uvec2 f16_store(vec4 val) +{ + return uvec2(packHalf2x16(val.xy), packHalf2x16(val.zw)); +} + +vec3 linear_from_srgb(vec3 c) +{ + return pow(c, vec3(2.2)); +} + +// Pre-rotate UV so the rest of HRC operates along the +x axis. +vec2 rotate_uv(vec2 uv, int rot, float inv) +{ + if (rot == 1) return vec2(inv - uv.y, uv.x); + else if (rot == 2) return inv - uv; + else if (rot == 3) return vec2(uv.y, inv - uv.x); + return uv; +} + +void main() +{ + int n = u_cascade; + int pow2n = 1 << n; + int directions = pow2n + 1; + + ivec2 gid = ivec2(gl_GlobalInvocationID.xy); + if (gid.x >= u_curr_w || gid.y >= u_world_size) return; + + int probe_x = gid.x / directions; + int k = gid.x - probe_x * directions; + + // Sample at probe origin (no stepping). + vec2 origin = vec2(float(probe_x) * float(pow2n), float(gid.y)); + float world = float(u_world_size); + vec2 uv = rotate_uv((origin + 0.5) / world, u_rotate, 1.0); + + vec3 emiss = linear_from_srgb(textureLod(u_emissivity, uv, u_mip_level).rgb); + vec3 absrp = linear_from_srgb(textureLod(u_absorption, uv, u_absrp_mip_level).rgb); + + // Direction-dependent step length for correct optical depth. + vec2 delta = v_n(n, float(k)); + float step_length = length(delta / float(pow2n)); + + vec3 optical_depth = absrp * step_length; + vec3 transmittance = exp(-optical_depth); + vec3 radiance = emiss * (vec3(1.0) - transmittance); + + int out_idx = gid.y * u_curr_w + gid.x; + u_out_rad.data[out_idx] = f16_store(vec4(radiance, 1.0)); + u_out_trn.data[out_idx] = f16_store(vec4(transmittance, 1.0)); +} diff --git a/samples/hrc_data/hrc_trace.c_shd b/samples/hrc_data/hrc_trace.c_shd new file mode 100644 index 000000000..01554a815 --- /dev/null +++ b/samples/hrc_data/hrc_trace.c_shd @@ -0,0 +1,168 @@ +// Direct ray tracing for cascades 0-2 (replaces seed + extend at low levels). +// +// At low cascades, the extend shader's zigzag averaging of two diagonal candidates +// creates manhattan distance artifacts because c0 only has two diagonal directions +// (1,-1) and (1,1). This shader instead traces each ray directly using a column-by- +// column DDA: step one x-cell per iteration and split at y-cell boundaries so every +// cell the ray passes through is visited with its correct physical path length. +// +// Pipeline: [trace x 3] -> [extend x (N-2)] -> merge -> composite +// ^^^^^^^^^^^^ +// +// Dispatched once per cascade level (0, 1, 2) per rotation. Uses the same buffer +// layout as seed/extend so the merge phase is unaffected. +// +// Output buffer layout (same as seed/extend): +// width = (world_size / 2^n) * (2^n + 1) +// height = world_size +// index = y * width + probe_x * directions + k + +layout(set = 0, binding = 0) uniform sampler2D u_emissivity; +layout(set = 0, binding = 1) uniform sampler2D u_absorption; + +layout(std430, set = 1, binding = 0) writeonly buffer OutRad { uvec2 data[]; } u_out_rad; +layout(std430, set = 1, binding = 1) writeonly buffer OutTrn { uvec2 data[]; } u_out_trn; + +layout(set = 2, binding = 0) uniform Params +{ + int u_cascade; + int u_rotate; + int u_world_size; + int u_curr_w; + int u_blend_boundary; // [B] angle-based weight blending near ±45° edges + float u_blend_width; // [/]] blend falloff width in direction slots + float u_mip_level; // emissivity mipmap LOD (0.0 = base level) + float u_absrp_mip_level; // absorption mipmap LOD (0.0 = base level) +}; + +layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; + +struct Interval +{ + vec3 rad; + vec3 trn; +}; + +Interval merge(Interval a, Interval b) +{ + return Interval( + a.rad + a.trn * b.rad, + a.trn * b.trn + ); +} + +// Direction vector for the k-th ray at cascade n (Sec 4). +// v_n(k) = (2^n, 2k - 2^n) +vec2 v_n(int n, float k) +{ + float pow2n = float(1 << n); + return vec2(pow2n, 2.0 * k - pow2n); +} + +uvec2 f16_store(vec4 val) +{ + return uvec2(packHalf2x16(val.xy), packHalf2x16(val.zw)); +} + +vec3 linear_from_srgb(vec3 c) +{ + return pow(c, vec3(2.2)); +} + +// Pre-rotate UV so the rest of HRC operates along the +x axis. +vec2 rotate_uv(vec2 uv, int rot, float inv) +{ + if (rot == 1) return vec2(inv - uv.y, uv.x); + else if (rot == 2) return inv - uv; + else if (rot == 3) return vec2(uv.y, inv - uv.x); + return uv; +} + +// Accumulate one cell's Beer-Lambert contribution along a ray. +// cell is the integer pixel coordinate; ds is the physical distance through the cell. +void trace_cell(vec2 cell, float ds, inout vec3 rad, inout vec3 trn) +{ + float world = float(u_world_size); + if (cell.x < 0.0 || cell.x >= world || cell.y < 0.0 || cell.y >= world) return; + vec2 uv = rotate_uv((cell + 0.5) / world, u_rotate, 1.0); + vec3 emiss = linear_from_srgb(textureLod(u_emissivity, uv, u_mip_level).rgb); + vec3 absrp = linear_from_srgb(textureLod(u_absorption, uv, u_absrp_mip_level).rgb); + vec3 T = exp(-absrp * ds); + rad += trn * emiss * (vec3(1.0) - T); + trn *= T; +} + +void main() +{ + int n = u_cascade; + int pow2n = 1 << n; + int directions = pow2n + 1; + + ivec2 gid = ivec2(gl_GlobalInvocationID.xy); + if (gid.x >= u_curr_w || gid.y >= u_world_size) return; + + int probe_x = gid.x / directions; + int k = gid.x - probe_x * directions; + + // Ray direction and tracing parameters. + vec2 delta = v_n(n, float(k)); + float max_comp = float(pow2n); // max(|delta.x|, |delta.y|) = 2^n always + vec2 slope = delta / max_comp; // normalize so max component = 1 + float step_length = length(slope); // physical distance per x-column step + int num_steps = pow2n; // exactly 2^n x-columns + bool is_boundary = (k == 0 || k == pow2n); + + // Probe origin in rotated world space. + vec2 origin = vec2(float(probe_x) * float(pow2n), float(gid.y)); + float world = float(u_world_size); + + // Column-by-column DDA: one x-column per iteration. When the ray crosses a + // y-cell boundary within a column, split the step so both cells get their + // correct fractional path length through Beer-Lambert. + vec3 acc_rad = vec3(0.0); + vec3 acc_trn = vec3(1.0); + + float y_offset = 0.5; // start at cell center so diagonal rays split symmetrically + int cy = 0; // current y-cell offset from int(origin.y) + + for (int s = 0; s < num_steps; s++) { + float px = origin.x + float(s); + if (px >= world) break; + + float next_y = 0.5 + slope.y * float(s + 1); + int next_cy = int(floor(next_y)); + + vec2 col = vec2(px, origin.y); + + if (cy == next_cy || is_boundary) { + // No y-boundary splitting: either no crossing, or boundary ray + // (k=0, k=pow2n) where we trace only the entry cell to match + // extend's one-cell-per-column footprint at the ±45° edges. + trace_cell(col + vec2(0, float(cy)), step_length, acc_rad, acc_trn); + } else { + // Y-boundary crossed -- split into two sub-steps. + float y_bound = slope.y > 0.0 ? float(cy + 1) : float(cy); + float frac = (y_bound - y_offset) / slope.y; + trace_cell(col + vec2(0, float(cy)), frac * step_length, acc_rad, acc_trn); + trace_cell(col + vec2(0, float(next_cy)), (1.0 - frac) * step_length, acc_rad, acc_trn); + } + + y_offset = next_y; + cy = next_cy; + } + + // Blend boundary: near-boundary rays (small k or k near pow2n) get reduced + // weight so that the overlapping contributions from adjacent rotations sum + // to ~1.0 instead of >1.0. Boundary rays (k=0, k=pow2n) get weight 0.5 + // (shared equally between rotations), fading to 1.0 two directions in. + if (u_blend_boundary != 0) { + float edge_dist = float(min(k, pow2n - k)); + float weight = mix(0.5, 1.0, smoothstep(0.0, u_blend_width, edge_dist)); + acc_rad *= weight; + acc_trn = mix(vec3(1.0), acc_trn, weight); + } + + int out_idx = gid.y * u_curr_w + gid.x; + u_out_rad.data[out_idx] = f16_store(vec4(acc_rad, 1.0)); + u_out_trn.data[out_idx] = f16_store(vec4(acc_trn, 1.0)); +} diff --git a/samples/ime.c b/samples/ime.c index dae3b4410..2a3106eb3 100644 --- a/samples/ime.c +++ b/samples/ime.c @@ -33,7 +33,7 @@ void text_append_input(TextBoxData* data, CF_InputTextBuffer buffer) { } void text_pop(TextBoxData* data) { - if (data->utf32 && alen(data->utf32) > 0) { + if (data->utf32 && asize(data->utf32) > 0) { int codepoint = apop(data->utf32); spopn(data->utf8, utf8_bytes_required(codepoint)); } diff --git a/samples/imgui_texture.c b/samples/imgui_texture.c new file mode 100644 index 000000000..6e2cbbabd --- /dev/null +++ b/samples/imgui_texture.c @@ -0,0 +1,83 @@ +#include +#include + +static inline ImTextureRef imgui_texture_ref_from_id(ImTextureID tex_id) { + ImTextureRef tex_ref = { ._TexData = NULL, ._TexID = tex_id }; + return tex_ref; +} + +ImTextureRef imgui_texture_ref_from_cf_texture(CF_Texture tex) { + ImTextureID tex_id = (ImTextureID)(uintptr_t)cf_texture_handle(tex); + return imgui_texture_ref_from_id(tex_id); +} + +CF_Texture cf_texture_from_sprite( + CF_Sprite* sprite, + CF_Canvas canvas, + int canvas_width, + int canvas_height +) { + cf_draw_push(); + float scale_x = cf_floor((float)canvas_width / (float)sprite->w); + float scale_y = cf_floor((float)canvas_height / (float)sprite->h); + float scale = (scale_x < scale_y) ? scale_x : scale_y; + cf_draw_scale(scale, scale); + cf_sprite_update(sprite); + cf_draw_sprite(sprite); + cf_clear_color(0.0f, 0.0f, 0.0f, 0.0f); + cf_render_to(canvas, true); + CF_Texture tex = cf_canvas_get_target(canvas); + cf_draw_pop(); + return tex; +} + +int main(int argc, char* argv[]) { + CF_Result result = cf_make_app( + "ImGui Texture", + 0, + 0, + 0, + 640, + 360, + CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT | CF_APP_OPTIONS_RESIZABLE_BIT, + argv[0] + ); + if (cf_is_error(result)) { + return -1; + } + + cf_app_init_imgui(); + int canvas_width = 512; + int canvas_height = 512; + CF_Sprite demo_sprite = cf_make_demo_sprite(); + cf_sprite_play(&demo_sprite, "spin"); + CF_Canvas canvas = cf_make_canvas( + cf_canvas_defaults(canvas_width, canvas_height) + ); + + while (cf_app_is_running()) { + cf_app_update(NULL); + + CF_Texture demo_tex = cf_texture_from_sprite( + &demo_sprite, + canvas, + canvas_width, + canvas_height + ); + + ImGui_SetNextWindowSize((ImVec2){ 128, 128 }, ImGuiCond_FirstUseEver); + if (ImGui_Begin("Textures", NULL, ImGuiWindowFlags_None)) { + ImTextureRef tex_ref = imgui_texture_ref_from_cf_texture(demo_tex); + ImVec2 avail = ImGui_GetContentRegionAvail(); + ImGui_Image(tex_ref, avail); + } + ImGui_End(); + + cf_app_draw_onto_screen(true); + } + + cf_destroy_canvas(canvas); + cf_destroy_app(); + + return 0; +} diff --git a/samples/input_binding.c b/samples/input_binding.c new file mode 100644 index 000000000..c4bc7012b --- /dev/null +++ b/samples/input_binding.c @@ -0,0 +1,117 @@ +// Input Bindings Example -- Platformer Character +// +// Shows how to wire up keyboard + joypad inputs using Cute Framework's binding system. + +#include + +// Tuning. +#define PRESS_BUFFER 0.1f // Seconds to buffer press/release for forgiving input. +#define DEADZONE 0.25f // Analog stick deadzone. +#define DASH_SPEED 350.0f +#define DASH_TIME 0.15f + +int main(int argc, char* argv[]) +{ + CF_Result result = cf_make_app("Binding Example", 0, 0, 0, 640, 480, CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT, argv[0]); + if (cf_is_error(result)) return -1; + + // Movement stick: WASD + arrow keys + d-pad + left analog stick. + CF_StickBinding move = cf_make_stick_binding(0); + cf_stick_binding_add_wasd(move); + cf_stick_binding_add_arrow_keys(move); + cf_stick_binding_add_dpad(move); + cf_stick_binding_add_left_stick(move, DEADZONE); + + // Jump button: spacebar + Z key + joypad A. + CF_ButtonBinding jump = cf_make_button_binding(0, PRESS_BUFFER); + cf_button_binding_add_key(jump, CF_KEY_SPACE); + cf_button_binding_add_key(jump, CF_KEY_Z); + cf_button_binding_add_joypad_button(jump, CF_JOYPAD_BUTTON_A); + + // Dash button: left shift + X key + joypad B. + CF_ButtonBinding dash = cf_make_button_binding(0, PRESS_BUFFER); + cf_button_binding_add_key(dash, CF_KEY_LSHIFT); + cf_button_binding_add_key(dash, CF_KEY_X); + cf_button_binding_add_joypad_button(dash, CF_JOYPAD_BUTTON_B); + + float x = 0, y = 0; + float vx = 0, vy = 0; + bool on_ground = true; + float ground_y = -160.0f; + float dash_timer = 0; + float dash_dir = 1.0f; + + while (cf_app_is_running()) { + cf_app_update(NULL); + float dt = CF_DELTA_TIME; + + // -- Dash -- + if (cf_binding_pressed(dash) && dash_timer <= 0) { + cf_binding_consume_press(dash); + dash_dir = cf_binding_sign(move).x; + if (dash_dir == 0) dash_dir = 1.0f; + dash_timer = DASH_TIME; + } + + dash_timer -= dt; + if (dash_timer < 0) dash_timer = 0; + + if (dash_timer > 0) { + // Dash overrides normal movement. + vx = DASH_SPEED * dash_dir; + } else { + // -- Normal movement -- + CF_V2 input = cf_binding_value(move); + vx = input.x * 120.0f; + } + + // -- Jump (buffered) -- + if (on_ground && cf_binding_pressed(jump)) { + cf_binding_consume_press(jump); + vy = 250.0f; + on_ground = false; + } + + // Variable jump height: cut upward velocity when button released. + if (!on_ground && vy > 0 && !cf_button_binding_down(jump)) { + vy *= 0.5f; + } + + // Gravity + integration. + if (dash_timer <= 0) { + vy -= 800.0f * dt; + } + x += vx * dt; + y += vy * dt; + if (y <= ground_y) { y = ground_y; vy = 0; on_ground = true; } + + // Draw the player. + cf_draw_push_color(cf_color_white()); + cf_draw_box_fill(cf_make_aabb(cf_v2(x - 8, y), cf_v2(x + 8, y + 16)), 0); + cf_draw_pop_color(); + + // Draw the ground line. + cf_draw_push_color(cf_color_grey()); + cf_draw_line(cf_v2(-320, ground_y), cf_v2(320, ground_y), 0); + cf_draw_pop_color(); + + // Draw controls. + cf_draw_push_color(cf_color_grey()); + float tx = -300.0f; + float ty = 220.0f; + float line = 18.0f; + cf_draw_text("Controls:", cf_v2(tx, ty), -1); + cf_draw_text("Move: WASD / Arrow Keys / D-Pad / Left Stick", cf_v2(tx, ty - line), -1); + cf_draw_text("Jump: Space / Z / A Button", cf_v2(tx, ty - line*2), -1); + cf_draw_text("Dash: LShift / X / B Button", cf_v2(tx, ty - line*3), -1); + cf_draw_pop_color(); + + cf_app_draw_onto_screen(true); + } + + cf_destroy_binding(move); + cf_destroy_binding(jump); + cf_destroy_binding(dash); + cf_destroy_app(); + return 0; +} diff --git a/samples/metaballs.cpp b/samples/metaballs.cpp index 9ceba2104..413d73f34 100644 --- a/samples/metaballs.cpp +++ b/samples/metaballs.cpp @@ -8,9 +8,9 @@ float scale = 1; const char* s_shd = R"( layout(set = 2, binding = 1) uniform sampler2D tex; - vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) + vec4 shader(vec4 color, ShaderParams params) { - float d = texture(tex, screen_uv).x; + float d = texture(tex, params.screen_uv).x; d = d > 0.5 ? 1.0 : 0.0; return vec4(vec3(d), 1); } @@ -53,9 +53,9 @@ void update() float r = rnd_range(rnd, 10.0f,60.0f); // Render a perfect soft-circle using anti-alias scaling and zero-radius. - draw_push_antialias_scale(r); + draw_push_shape_aa(r); draw_circle_fill(make_circle(V2(x, y), 0)); - draw_pop_antialias_scale(); + draw_pop_shape_aa(); } static int toggle = false; diff --git a/samples/metaballs_shd.h b/samples/metaballs_shd.h index 7f06dbc31..0e67d21c9 100644 --- a/samples/metaballs_shd.h +++ b/samples/metaballs_shd.h @@ -274,13 +274,13 @@ vec2 pts[8]; void main() { - bool is_sprite = v_type >= (0.0 / 255.0) && v_type < (0.5 / 255.0); - bool is_text = v_type > (0.5 / 255.0) && v_type < (1.5 / 255.0); - bool is_box = v_type > (1.5 / 255.0) && v_type < (2.5 / 255.0); - bool is_seg = v_type > (2.5 / 255.0) && v_type < (3.5 / 255.0); - bool is_tri = v_type > (3.5 / 255.0) && v_type < (4.5 / 255.0); - bool is_tri_sdf = v_type > (4.5 / 255.0) && v_type < (5.5 / 255.0); - bool is_poly = v_type > (5.5 / 255.0) && v_type < (6.5 / 255.0); + bool is_sprite = v_type >= 0.0 && v_type < 0.5; + bool is_text = v_type > 0.5 && v_type < 1.5; + bool is_box = v_type > 1.5 && v_type < 2.5; + bool is_seg = v_type > 2.5 && v_type < 3.5; + bool is_tri = v_type > 3.5 && v_type < 4.5; + bool is_tri_sdf = v_type > 4.5 && v_type < 5.5; + bool is_poly = v_type > 5.5 && v_type < 6.5; vec4 c = vec4(0); diff --git a/samples/platformer.cpp b/samples/platformer.cpp index 3666af799..b0a06879a 100644 --- a/samples/platformer.cpp +++ b/samples/platformer.cpp @@ -199,7 +199,7 @@ static inline int aabb_to_shape(CF_Aabb aabb, BlockShape shape) } else { - return cf_aabb_to_poly(aabb, &shape.poly, nullptr); + return cf_aabb_to_poly(aabb, &shape.poly); } } @@ -211,7 +211,7 @@ static inline CF_Manifold aabb_to_shape_manifold(CF_Aabb aabb, BlockShape shape) } else { - return cf_aabb_to_poly_manifold(aabb, &shape.poly, nullptr); + return cf_aabb_to_poly_manifold(aabb, &shape.poly); } } @@ -643,10 +643,10 @@ int step_move(dyna Block *blocks, CF_Transform *transform, CF_V2 *velocity, CF_A if (aabb_to_shape(cull_aabb, block_shape)) { - CF_ToiResult result = cf_toi(&aabb, CF_SHAPE_TYPE_AABB, nullptr, dp, - &block_shape._data, block_shape.type, nullptr, cf_v2(0.0f, 0.0f), + CF_ToiResult result = cf_toi(&aabb, CF_SHAPE_TYPE_AABB, dp, + &block_shape._data, block_shape.type, cf_v2(0.0f, 0.0f), false); - bool collided = cf_collided(&aabb, nullptr, CF_SHAPE_TYPE_AABB, &block_shape._data, nullptr, block_shape.type); + bool collided = cf_collided(&aabb, CF_SHAPE_TYPE_AABB, &block_shape._data, block_shape.type); if (result.hit && !collided) { diff --git a/samples/polygon.cpp b/samples/polygon.cpp index 57f003a16..579a69596 100644 --- a/samples/polygon.cpp +++ b/samples/polygon.cpp @@ -36,8 +36,7 @@ int main(int argc, char* argv[]) ImGui::End(); draw_push_color(color); - draw_push_antialias_scale(aa_scale); - draw_push_antialias(aa); + draw_push_shape_aa(aa ? aa_scale : 0); draw_polygon_fill(polygon, CF_ARRAY_SIZE(polygon), chubbiness); if (!ImGui::GetIO().WantCaptureMouse) { diff --git a/samples/recolor.cpp b/samples/recolor.cpp index 4dae7cf91..235891474 100644 --- a/samples/recolor.cpp +++ b/samples/recolor.cpp @@ -6,11 +6,11 @@ using namespace Cute; const char* s_recolor = R"( #include "blend.shd" - vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) + vec4 shader(vec4 color, ShaderParams params) { vec3 a = rgb_to_hsv(color.rgb); - vec3 b = rgb_to_hsv(params.rgb); - vec3 c = hsv_to_rgb(mix(a, b, params.a)); + vec3 b = rgb_to_hsv(params.attributes.rgb); + vec3 c = hsv_to_rgb(mix(a, b, params.attributes.a)); return vec4(c, color.a); } )"; diff --git a/samples/scratch.cpp b/samples/scratch.cpp index 87a8d708e..0653946c1 100644 --- a/samples/scratch.cpp +++ b/samples/scratch.cpp @@ -14,7 +14,7 @@ int main(int argc, char* argv[]) while (app_is_running()) { app_update(); - draw_push_antialias(false); + draw_push_shape_aa(0); draw_scale(sx, sy); draw_rotate((float)(CF_PI * CF_SECONDS / 16.0f)); diff --git a/samples/screen_shatter.cpp b/samples/screen_shatter.cpp index 778ec15c7..69ab4cc5e 100644 --- a/samples/screen_shatter.cpp +++ b/samples/screen_shatter.cpp @@ -4,7 +4,7 @@ #define STR(X) #X const char* shader_str = STR( -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { return texture(u_image, smooth_uv(v_uv, u_texture_size)); } @@ -53,20 +53,17 @@ void init(int w, int h) CF_VertexAttribute* attrs = NULL; cf_array_fit(attrs, 32); - add_vertex_attribute(attrs, "in_pos", CF_VERTEX_FORMAT_FLOAT2, CF_OFFSET_OF(CF_Vertex, p)); - add_vertex_attribute(attrs, "in_posH", CF_VERTEX_FORMAT_FLOAT2, CF_OFFSET_OF(CF_Vertex, posH)); - add_vertex_attribute(attrs, "in_n", CF_VERTEX_FORMAT_INT, CF_OFFSET_OF(CF_Vertex, n)); - add_vertex_attribute(attrs, "in_ab", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[0])); - add_vertex_attribute(attrs, "in_cd", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[2])); - add_vertex_attribute(attrs, "in_ef", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[4])); - add_vertex_attribute(attrs, "in_gh", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[6])); - add_vertex_attribute(attrs, "in_uv", CF_VERTEX_FORMAT_FLOAT2, CF_OFFSET_OF(CF_Vertex, uv)); - add_vertex_attribute(attrs, "in_col", CF_VERTEX_FORMAT_UBYTE4_NORM, CF_OFFSET_OF(CF_Vertex, color)); - add_vertex_attribute(attrs, "in_radius", CF_VERTEX_FORMAT_FLOAT, CF_OFFSET_OF(CF_Vertex, radius)); - add_vertex_attribute(attrs, "in_stroke", CF_VERTEX_FORMAT_FLOAT, CF_OFFSET_OF(CF_Vertex, stroke)); - add_vertex_attribute(attrs, "in_aa", CF_VERTEX_FORMAT_FLOAT, CF_OFFSET_OF(CF_Vertex, aa)); - add_vertex_attribute(attrs, "in_params", CF_VERTEX_FORMAT_UBYTE4_NORM, CF_OFFSET_OF(CF_Vertex, type)); - add_vertex_attribute(attrs, "in_user_params", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, attributes)); + add_vertex_attribute(attrs, "in_pos_uv", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, p)); + add_vertex_attribute(attrs, "in_n", CF_VERTEX_FORMAT_INT, CF_OFFSET_OF(CF_Vertex, n)); + add_vertex_attribute(attrs, "in_ab", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[0])); + add_vertex_attribute(attrs, "in_cd", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[2])); + add_vertex_attribute(attrs, "in_ef", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[4])); + add_vertex_attribute(attrs, "in_gh", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[6])); + add_vertex_attribute(attrs, "in_col", CF_VERTEX_FORMAT_UBYTE4_NORM, CF_OFFSET_OF(CF_Vertex, color)); + add_vertex_attribute(attrs, "in_shape", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, radius)); + add_vertex_attribute(attrs, "in_blend_posH", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, alpha)); + add_vertex_attribute(attrs, "in_user", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, attributes)); + add_vertex_attribute(attrs, "in_uv_bounds", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, uv_bounds)); draw.mesh = cf_make_mesh(CF_MB * 5, attrs, cf_array_count(attrs), sizeof(CF_Vertex)); draw.verts = NULL; @@ -238,9 +235,9 @@ void draw_screen_chunk(ScreenChunk* chunks) verts[0].aa = false; verts[1].aa = false; verts[2].aa = false; - verts[0].alpha = 255; - verts[1].alpha = 255; - verts[2].alpha = 255; + verts[0].alpha = 1.0f; + verts[1].alpha = 1.0f; + verts[2].alpha = 1.0f; for (int index = 0; index < cf_array_count(chunks); ++index) { @@ -281,7 +278,7 @@ int main(int argc, char *argv[]) CF_Sprite demo_sprite = cf_make_demo_sprite(); cf_sprite_play(&demo_sprite, "spin"); - int frame_count = cf_array_count(demo_sprite.animation->frames); + int frame_count = cf_sprite_frame_count(&demo_sprite); demo_sprite.scale = cf_v2(12, 12); CF_Canvas app_canvas = cf_app_get_canvas(); diff --git a/samples/shallow_water.cpp b/samples/shallow_water.cpp index 97cec3b9d..a48f104c6 100644 --- a/samples/shallow_water.cpp +++ b/samples/shallow_water.cpp @@ -46,9 +46,9 @@ const char* s_shd = R"( return vec4(n * 0.5 + 0.5, 1.0, 1.0); } - vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) + vec4 shader(vec4 color, ShaderParams params) { - vec2 uv = screen_uv; + vec2 uv = params.screen_uv; vec2 dim = vec2(1.0 / 160.0, 1.0 / 120.0); vec2 n = normal_from_heightmap(noise_tex, uv); vec2 w = normal_from_heightmap(wavelets_tex, uv + n * dim * 10.0); @@ -155,7 +155,7 @@ int main(int argc, char* argv[]) for (int i = 0; i < wavelets.count(); ++i) { Wavelet& w = wavelets[i]; - draw_push_antialias_scale(w.blur); + draw_push_shape_aa(w.blur); draw_push_color(make_color(1.0f, 1.0f, 1.0f, w.opacity)); draw_circle(w.p, w.r, 0); w.r += 10.0f * CF_DELTA_TIME; @@ -177,7 +177,7 @@ int main(int argc, char* argv[]) draw_set_texture("scene_tex", canvas_get_target(scene_canvas)); draw_set_uniform("show_noise", show_noise ? 1.0f : 0.0f); draw_set_uniform("show_normals", show_normals ? 1.0f : 0.0f); - draw_push_antialias(false); + draw_push_shape_aa(0); draw_box(V2(0,0), (float)W, (float)H); app_draw_onto_screen(false); } diff --git a/samples/spaceshooter.cpp b/samples/spaceshooter.cpp index 75bc61a3e..f36d64caf 100644 --- a/samples/spaceshooter.cpp +++ b/samples/spaceshooter.cpp @@ -446,7 +446,7 @@ void BulletBarn::update() } else { for (int j = 0; j < ASTEROIDS_MAX; ++j) { if (!g->asteroids.alive[j]) continue; - if (circle_to_poly(make_circle(p[i], BULLETS_RADIUS), g->asteroids.poly + j, NULL)) { + if (circle_to_poly(make_circle(p[i], BULLETS_RADIUS), g->asteroids.poly + j)) { hit(i); } } @@ -579,7 +579,7 @@ void AsteroidBarn::hit(v2 hit, float radius) CF_Circle c = make_circle(hit, radius); for (int i = 0; i < ASTEROIDS_MAX; ++i) { if (!alive[i]) continue; - if (circle_to_poly(c, poly + i, NULL)) { + if (circle_to_poly(c, poly + i)) { alive[i] = false; explode(i); } @@ -605,7 +605,7 @@ void player_movement_routine() for (int i = 0; i < ASTEROIDS_MAX; ++i) { if (!g->asteroids.alive[i]) continue; CF_Circle c = make_circle(g->player.sprite.transform.p, 12.5f); - if (circle_to_poly(c, g->asteroids.poly + i, NULL)) { + if (circle_to_poly(c, g->asteroids.poly + i)) { nav_goto("hurt"); } } @@ -827,7 +827,7 @@ void player_weapons_routine() // Collide with asteroids. for (int i = 0; i < ASTEROIDS_MAX; ++i) { if (!g->asteroids.alive[i]) continue; - if (circle_to_poly(make_circle(g->player.sprite.transform.p, 25), g->asteroids.poly + i, NULL)) { + if (circle_to_poly(make_circle(g->player.sprite.transform.p, 25), g->asteroids.poly + i)) { g->asteroids.alive[i] = false; g->asteroids.explode(i); } @@ -1073,7 +1073,7 @@ void ChargeShot::update_and_draw() // Collide with asteroids. for (int i = 0; i < ASTEROIDS_MAX; ++i) { if (!g->asteroids.alive[i]) continue; - if (circle_to_poly(make_circle(shot.transform.p, CHARGE_SHOT_RADIUS), g->asteroids.poly + i, NULL)) { + if (circle_to_poly(make_circle(shot.transform.p, CHARGE_SHOT_RADIUS), g->asteroids.poly + i)) { g->asteroids.alive[i] = false; g->asteroids.explode(i); hit(); diff --git a/samples/spaceshooter_data/flash.shd b/samples/spaceshooter_data/flash.shd index 60f02f550..c67638a26 100644 --- a/samples/spaceshooter_data/flash.shd +++ b/samples/spaceshooter_data/flash.shd @@ -1,4 +1,4 @@ -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { - return vec4(mix(color.rgb, params.rgb, params.a), color.a); + return vec4(mix(color.rgb, params.attributes.rgb, params.attributes.a), color.a); } diff --git a/samples/spaceshooter_data/flash_shd.h b/samples/spaceshooter_data/flash_shd.h index 72c43a08a..eab3eeafb 100644 --- a/samples/spaceshooter_data/flash_shd.h +++ b/samples/spaceshooter_data/flash_shd.h @@ -5,22 +5,17 @@ #line 1 0 -layout(location = 0) in vec2 v_pos; +layout(location = 0) in vec4 v_pos_uv; layout(location = 1) in flat int v_n; layout(location = 2) in vec4 v_ab; layout(location = 3) in vec4 v_cd; layout(location = 4) in vec4 v_ef; layout(location = 5) in vec4 v_gh; -layout(location = 6) in vec2 v_uv; -layout(location = 7) in vec4 v_col; -layout(location = 8) in float v_radius; -layout(location = 9) in float v_stroke; -layout(location = 10) in float v_aa; -layout(location = 11) in float v_type; -layout(location = 12) in float v_alpha; -layout(location = 13) in float v_fill; -layout(location = 14) in vec2 v_posH; -layout(location = 15) in vec4 v_user; +layout(location = 6) in vec4 v_col; +layout(location = 7) in vec4 v_shape; +layout(location = 8) in vec4 v_blend_posH; +layout(location = 9) in vec4 v_user; +layout(location = 10) in vec4 v_uv_bounds; layout(location = 0) out vec4 result; @@ -29,8 +24,26 @@ layout(set = 2, binding = 0) uniform sampler2D u_image; layout(set = 3, binding = 0) uniform uniform_block { vec2 u_texture_size; int u_alpha_discard; + int u_use_smooth_uv; }; + +vec2 pts[8]; + + + +vec2 v_pos; +vec2 v_uv; +vec2 v_posH; +float v_radius; +float v_stroke; +float v_aa; +float v_type; +float v_alpha; +float v_fill; +vec2 pos; +vec2 screen_uv; + #line 1 "blend.shd" @@ -95,7 +108,7 @@ vec4 softlight(vec4 base, vec4 blend) { return vec4(softlight(base.rgb, blend.rgb), base.a); } -#line 29 0 +#line 42 0 #line 1 "gamma.shd" vec4 gamma(vec4 c) @@ -107,7 +120,7 @@ vec4 de_gamma(vec4 c) { return vec4(pow(abs(c.rgb), vec3(2.2)), c.a); } -#line 30 0 +#line 43 0 #line 1 "smooth_uv.shd" vec2 smooth_uv(vec2 uv, vec2 texture_size) @@ -117,7 +130,7 @@ vec2 smooth_uv(vec2 uv, vec2 texture_size) pixel = seam + clamp( (pixel - seam) / fwidth(pixel), - 0.5, 0.5); return pixel / texture_size; } -#line 31 0 +#line 44 0 #line 1 "distance.shd" float safe_div(float a, float b) @@ -257,32 +270,52 @@ float distance_polygon(vec2 p, vec2[8] v, int N) return s * sqrt(d); } -#line 32 0 +#line 45 0 + +struct ShaderParams +{ + vec2 view_pos; + vec2 uv; + vec2 uv_min; + vec2 uv_max; + vec2 screen_uv; + vec4 attributes; +}; + #line 1 "shader_stub.shd" - vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) + vec4 shader(vec4 color, ShaderParams params) { - return vec4(mix(color.rgb, params.rgb, params.a), color.a); + return vec4(mix(color.rgb, params.attributes.rgb, params.attributes.a), color.a); } -#line 33 0 - - -vec2 pts[8]; +#line 57 0 void main() { - bool is_sprite = v_type >= (0.0 / 255.0) && v_type < (0.5 / 255.0); - bool is_text = v_type > (0.5 / 255.0) && v_type < (1.5 / 255.0); - bool is_box = v_type > (1.5 / 255.0) && v_type < (2.5 / 255.0); - bool is_seg = v_type > (2.5 / 255.0) && v_type < (3.5 / 255.0); - bool is_tri = v_type > (3.5 / 255.0) && v_type < (4.5 / 255.0); - bool is_tri_sdf = v_type > (4.5 / 255.0) && v_type < (5.5 / 255.0); - bool is_poly = v_type > (5.5 / 255.0) && v_type < (6.5 / 255.0); + + v_pos = v_pos_uv.xy; + v_uv = v_pos_uv.zw; + v_radius = v_shape.x; + v_stroke = v_shape.y; + v_aa = v_shape.z; + v_type = v_shape.w; + v_alpha = v_blend_posH.x; + v_fill = v_blend_posH.y; + v_posH = v_blend_posH.zw; + + bool is_sprite = v_type >= 0.0 && v_type < 0.5; + bool is_text = v_type > 0.5 && v_type < 1.5; + bool is_box = v_type > 1.5 && v_type < 2.5; + bool is_seg = v_type > 2.5 && v_type < 3.5; + bool is_tri = v_type > 3.5 && v_type < 4.5; + bool is_tri_sdf = v_type > 4.5 && v_type < 5.5; + bool is_poly = v_type > 5.5 && v_type < 6.5; vec4 c = vec4(0); - c = ! (is_sprite && is_text) ? de_gamma(texture(u_image, smooth_uv(v_uv, u_texture_size))) : c; - c = is_sprite ? gamma(c) : c; - c = is_text ? v_col * c.a : c; + vec2 uv = u_use_smooth_uv == 0 ? smooth_uv(v_uv, u_texture_size) : v_uv; + vec4 tex_c = de_gamma(texture(u_image, uv)); + c = is_sprite ? gamma(tex_c) : c; + c = is_text ? v_col * tex_c.a : c; c = is_tri ? v_col : c; @@ -308,1372 +341,1791 @@ void main() c = (! is_sprite && ! is_text && ! is_tri) ? sdf(c, v_col, d - v_radius) : c; c *= v_alpha; - vec2 screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1); - c = shader(c, v_pos, screen_uv, v_user); + pos = v_pos; + screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1); + ShaderParams sp; + sp.view_pos = pos; + sp.uv = v_uv; + sp.uv_min = v_uv_bounds.xy; + sp.uv_max = v_uv_bounds.zw; + sp.screen_uv = screen_uv; + sp.attributes = v_user; + c = shader(c, sp); if (u_alpha_discard != 0 && c.a == 0) discard; result = c; } */ -static const uint8_t s_flash_shd_bytecode_draw_content[21016] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x77, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x16, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, - 0x0E, 0x01, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, 0xA4, 0x02, 0x00, 0x00, 0xC4, 0x02, 0x00, 0x00, - 0xD5, 0x02, 0x00, 0x00, 0xD6, 0x02, 0x00, 0x00, 0xD7, 0x02, 0x00, 0x00, 0x22, 0x03, 0x00, 0x00, - 0x2B, 0x03, 0x00, 0x00, 0x34, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x54, 0x03, 0x00, 0x00, - 0x59, 0x03, 0x00, 0x00, 0x5F, 0x03, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, - 0x04, 0x00, 0x09, 0x00, 0x47, 0x4C, 0x5F, 0x41, 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x69, - 0x6E, 0x67, 0x5F, 0x6C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5F, 0x69, 0x6E, 0x63, 0x6C, - 0x75, 0x64, 0x65, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, - 0x45, 0x5F, 0x63, 0x70, 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, - 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x67, 0x61, 0x6D, 0x6D, 0x61, 0x28, 0x76, 0x66, 0x34, 0x3B, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x64, 0x65, 0x5F, 0x67, 0x61, 0x6D, 0x6D, 0x61, 0x28, 0x76, 0x66, 0x34, - 0x3B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x07, 0x00, 0x15, 0x00, 0x00, 0x00, 0x73, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x5F, 0x75, - 0x76, 0x28, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x75, 0x76, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x73, 0x61, 0x66, 0x65, 0x5F, 0x64, 0x69, 0x76, - 0x28, 0x66, 0x31, 0x3B, 0x66, 0x31, 0x3B, 0x00, 0x05, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x73, 0x61, 0x66, 0x65, 0x5F, 0x6C, 0x65, 0x6E, - 0x28, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x00, 0x73, 0x6B, 0x65, 0x77, - 0x28, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x28, 0x00, 0x00, 0x00, 0x64, 0x65, 0x74, 0x32, - 0x28, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x73, 0x64, 0x66, 0x5F, - 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x28, 0x66, 0x31, 0x3B, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x2B, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x73, 0x64, 0x66, 0x28, 0x76, 0x66, 0x34, 0x3B, 0x76, 0x66, 0x34, 0x3B, 0x66, 0x31, 0x3B, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x36, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x74, - 0x61, 0x6E, 0x63, 0x65, 0x5F, 0x61, 0x61, 0x62, 0x62, 0x28, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, - 0x32, 0x3B, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x34, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x35, 0x00, 0x00, 0x00, 0x68, 0x65, 0x00, 0x00, 0x05, 0x00, 0x0A, 0x00, - 0x3D, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x5F, 0x62, 0x6F, 0x78, - 0x28, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, - 0x3B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x39, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x3B, 0x00, 0x00, 0x00, 0x68, 0x65, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x3C, 0x00, 0x00, 0x00, - 0x75, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x43, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x74, - 0x61, 0x6E, 0x63, 0x65, 0x5F, 0x73, 0x65, 0x67, 0x6D, 0x65, 0x6E, 0x74, 0x28, 0x76, 0x66, 0x32, - 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x41, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x42, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x0B, 0x00, 0x49, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, - 0x5F, 0x74, 0x72, 0x69, 0x61, 0x6E, 0x67, 0x6C, 0x65, 0x28, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, - 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0A, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x5F, 0x70, 0x6F, 0x6C, - 0x79, 0x67, 0x6F, 0x6E, 0x28, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x5B, 0x38, 0x5D, 0x3B, - 0x69, 0x31, 0x3B, 0x00, 0x05, 0x00, 0x03, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x53, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x5C, 0x00, 0x00, 0x00, - 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x28, 0x76, 0x66, 0x34, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x76, - 0x66, 0x32, 0x3B, 0x76, 0x66, 0x34, 0x3B, 0x00, 0x05, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x70, 0x6F, 0x73, 0x00, 0x05, 0x00, 0x05, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x73, 0x63, 0x72, 0x65, - 0x65, 0x6E, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x73, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x7C, 0x00, 0x00, 0x00, - 0x70, 0x69, 0x78, 0x65, 0x6C, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x73, 0x65, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xA5, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0xCE, 0x00, 0x00, 0x00, 0x76, 0x5F, 0x73, 0x74, - 0x72, 0x6F, 0x6B, 0x65, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xD3, 0x00, 0x00, 0x00, - 0x77, 0x69, 0x72, 0x65, 0x5F, 0x64, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xD4, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0xD7, 0x00, 0x00, 0x00, - 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x5F, 0x61, 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xDA, 0x00, 0x00, 0x00, 0x76, 0x5F, 0x61, 0x61, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0xE0, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x5F, 0x6E, 0x6F, 0x5F, 0x61, 0x61, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x66, 0x69, 0x6C, 0x6C, - 0x5F, 0x61, 0x61, 0x00, 0x05, 0x00, 0x05, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x66, 0x69, 0x6C, 0x6C, - 0x5F, 0x6E, 0x6F, 0x5F, 0x61, 0x61, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xFA, 0x00, 0x00, 0x00, - 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x02, 0x01, 0x00, 0x00, - 0x66, 0x69, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0B, 0x01, 0x00, 0x00, - 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0E, 0x01, 0x00, 0x00, - 0x76, 0x5F, 0x66, 0x69, 0x6C, 0x6C, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x15, 0x01, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x29, 0x01, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x2B, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x3E, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x43, 0x01, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x47, 0x01, 0x00, 0x00, 0x70, 0x61, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4B, 0x01, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x52, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x53, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x55, 0x01, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x5D, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x61, 0x01, 0x00, 0x00, 0x65, 0x30, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x65, 0x01, 0x00, 0x00, 0x65, 0x31, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x69, 0x01, 0x00, 0x00, - 0x65, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x6D, 0x01, 0x00, 0x00, 0x76, 0x30, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x71, 0x01, 0x00, 0x00, 0x76, 0x31, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x75, 0x01, 0x00, 0x00, 0x76, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x79, 0x01, 0x00, 0x00, - 0x70, 0x71, 0x30, 0x00, 0x05, 0x00, 0x04, 0x00, 0x82, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x83, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x88, 0x01, 0x00, 0x00, 0x70, 0x71, 0x31, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x91, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x92, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x97, 0x01, 0x00, 0x00, 0x70, 0x71, 0x32, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xA0, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xA1, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0xA6, 0x01, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xA7, 0x01, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xA9, 0x01, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xAC, 0x01, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xB1, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xB3, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xBC, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xBE, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xC8, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xCA, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xDA, 0x01, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0xE5, 0x01, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0xE6, 0x01, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xE7, 0x01, 0x00, 0x00, - 0x6A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xF3, 0x01, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0xFB, 0x01, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x01, 0x02, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x02, 0x00, 0x00, - 0x63, 0x6F, 0x6E, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x55, 0x02, 0x00, 0x00, - 0x69, 0x73, 0x5F, 0x73, 0x70, 0x72, 0x69, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x56, 0x02, 0x00, 0x00, 0x76, 0x5F, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x5F, 0x02, 0x00, 0x00, 0x69, 0x73, 0x5F, 0x74, 0x65, 0x78, 0x74, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x68, 0x02, 0x00, 0x00, 0x69, 0x73, 0x5F, 0x62, 0x6F, 0x78, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x71, 0x02, 0x00, 0x00, 0x69, 0x73, 0x5F, 0x73, 0x65, 0x67, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x7A, 0x02, 0x00, 0x00, 0x69, 0x73, 0x5F, 0x74, 0x72, 0x69, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x83, 0x02, 0x00, 0x00, 0x69, 0x73, 0x5F, 0x74, 0x72, 0x69, 0x5F, 0x73, 0x64, 0x66, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x8C, 0x02, 0x00, 0x00, 0x69, 0x73, 0x5F, 0x70, 0x6F, 0x6C, 0x79, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x95, 0x02, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xA1, 0x02, 0x00, 0x00, 0x75, 0x5F, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xA4, 0x02, 0x00, 0x00, 0x76, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0xA5, 0x02, 0x00, 0x00, 0x75, 0x6E, 0x69, 0x66, 0x6F, 0x72, 0x6D, 0x5F, 0x62, 0x6C, 0x6F, 0x63, - 0x6B, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0xA5, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x75, 0x5F, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, - 0x06, 0x00, 0x07, 0x00, 0xA5, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x5F, 0x61, 0x6C, - 0x70, 0x68, 0x61, 0x5F, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x00, 0x05, 0x00, 0x03, 0x00, - 0xA7, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xA8, 0x02, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xAA, 0x02, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xB0, 0x02, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xB9, 0x02, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xC4, 0x02, 0x00, 0x00, - 0x76, 0x5F, 0x63, 0x6F, 0x6C, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xD1, 0x02, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xD5, 0x02, 0x00, 0x00, 0x76, 0x5F, 0x70, 0x6F, - 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xD6, 0x02, 0x00, 0x00, 0x76, 0x5F, 0x61, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xD7, 0x02, 0x00, 0x00, 0x76, 0x5F, 0x63, 0x64, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xD8, 0x02, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xDA, 0x02, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xDD, 0x02, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xE0, 0x02, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xE8, 0x02, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xEA, 0x02, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xED, 0x02, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xF2, 0x02, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xF4, 0x02, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xF7, 0x02, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x02, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x05, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x08, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x11, 0x03, 0x00, 0x00, 0x70, 0x74, 0x73, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x22, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x65, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x2B, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x67, 0x68, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x34, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x6E, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x35, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x37, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x39, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x48, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x4B, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x4D, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x4F, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x54, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x61, 0x6C, 0x70, 0x68, 0x61, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x58, 0x03, 0x00, 0x00, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6E, 0x5F, 0x75, - 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x59, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x70, 0x6F, - 0x73, 0x48, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5F, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x75, 0x73, - 0x65, 0x72, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x60, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x62, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x64, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x66, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xCE, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0B, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x56, 0x02, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xA1, 0x02, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xA1, 0x02, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xA4, 0x02, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xA5, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0xA5, 0x02, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0xA5, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xA7, 0x02, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xA7, 0x02, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xC4, 0x02, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xD5, 0x02, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xD6, 0x02, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xD7, 0x02, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x22, 0x03, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x2B, 0x03, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x34, 0x03, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x34, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x48, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x54, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x59, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x5F, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x21, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, 0x05, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x21, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x21, 0x00, 0x04, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x04, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x05, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x06, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x21, 0x00, 0x07, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x06, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x4B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x4B, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, - 0x4D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x4E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x50, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x21, 0x00, 0x06, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x00, 0x00, 0x21, 0x00, 0x07, 0x00, 0x57, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x2F, 0xBA, 0xE8, 0x3E, - 0x2C, 0x00, 0x06, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x71, 0x00, 0x00, 0x00, 0xCD, 0xCC, 0x0C, 0x40, 0x2C, 0x00, 0x06, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x02, 0x00, 0x99, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00, - 0xB4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00, - 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, - 0xCE, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, - 0xDA, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0xE5, 0x00, 0x00, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBF, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x20, 0x00, 0x04, 0x00, 0x0A, 0x01, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0A, 0x01, 0x00, 0x00, - 0x0B, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, - 0x0E, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x04, 0x00, 0x27, 0x01, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x28, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0xDC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0xE9, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x13, 0x02, 0x00, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x14, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x13, 0x02, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x54, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, - 0x56, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x5C, 0x02, 0x00, 0x00, 0x81, 0x80, 0x00, 0x3B, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x65, 0x02, 0x00, 0x00, 0xC1, 0xC0, 0xC0, 0x3B, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x6E, 0x02, 0x00, 0x00, 0xA1, 0xA0, 0x20, 0x3C, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x77, 0x02, 0x00, 0x00, 0xE1, 0xE0, 0x60, 0x3C, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x80, 0x02, 0x00, 0x00, 0x91, 0x90, 0x90, 0x3C, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x89, 0x02, 0x00, 0x00, 0xB1, 0xB0, 0xB0, 0x3C, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x92, 0x02, 0x00, 0x00, 0xD1, 0xD0, 0xD0, 0x3C, 0x2C, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x96, 0x02, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x9E, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x9F, 0x02, 0x00, 0x00, - 0x9E, 0x02, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xA0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9F, 0x02, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xA0, 0x02, 0x00, 0x00, 0xA1, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xA3, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xA3, 0x02, 0x00, 0x00, 0xA4, 0x02, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, 0xA5, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xA6, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0xA5, 0x02, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xA6, 0x02, 0x00, 0x00, 0xA7, 0x02, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xAB, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xC3, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xC3, 0x02, 0x00, 0x00, 0xC4, 0x02, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xA3, 0x02, 0x00, 0x00, 0xD5, 0x02, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xC3, 0x02, 0x00, 0x00, 0xD6, 0x02, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xC3, 0x02, 0x00, 0x00, 0xD7, 0x02, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x10, 0x03, 0x00, 0x00, 0x11, 0x03, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x14, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x19, 0x03, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x1D, 0x03, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xC3, 0x02, 0x00, 0x00, 0x22, 0x03, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x26, 0x03, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x2A, 0x03, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xC3, 0x02, 0x00, 0x00, 0x2B, 0x03, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x2F, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x33, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x33, 0x03, 0x00, 0x00, 0x34, 0x03, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x54, 0x03, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xA3, 0x02, 0x00, 0x00, 0x59, 0x03, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5B, 0x03, 0x00, 0x00, - 0xF3, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xC3, 0x02, 0x00, 0x00, - 0x5F, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x69, 0x03, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x54, 0x02, 0x00, 0x00, 0x55, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x54, 0x02, 0x00, 0x00, 0x5F, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x54, 0x02, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x54, 0x02, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x54, 0x02, 0x00, 0x00, 0x7A, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x54, 0x02, 0x00, 0x00, 0x83, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x54, 0x02, 0x00, 0x00, 0x8C, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x9B, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xA8, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xAA, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xB0, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xB6, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xB9, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xC0, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xD1, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xD8, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xDA, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xDD, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xE0, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xE8, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xEA, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xED, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xF2, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xF4, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xF7, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x35, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x37, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x44, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4B, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x4D, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x4F, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x60, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x64, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x66, 0x03, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x57, 0x02, 0x00, 0x00, - 0x56, 0x02, 0x00, 0x00, 0xBE, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, - 0x57, 0x02, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x5A, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x58, 0x02, 0x00, 0x00, 0x59, 0x02, 0x00, 0x00, - 0x5A, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x59, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x5B, 0x02, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x5D, 0x02, 0x00, 0x00, 0x5B, 0x02, 0x00, 0x00, 0x5C, 0x02, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x5A, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x5A, 0x02, 0x00, 0x00, - 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x5D, 0x02, 0x00, 0x00, 0x59, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x55, 0x02, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x60, 0x02, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x61, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x5C, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0x63, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x61, 0x02, 0x00, 0x00, - 0x62, 0x02, 0x00, 0x00, 0x63, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x62, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x64, 0x02, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, - 0xB8, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x66, 0x02, 0x00, 0x00, 0x64, 0x02, 0x00, 0x00, - 0x65, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x63, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x63, 0x02, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, 0x67, 0x02, 0x00, 0x00, - 0x61, 0x02, 0x00, 0x00, 0x5A, 0x02, 0x00, 0x00, 0x66, 0x02, 0x00, 0x00, 0x62, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x5F, 0x02, 0x00, 0x00, 0x67, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x69, 0x02, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x6A, 0x02, 0x00, 0x00, 0x69, 0x02, 0x00, 0x00, 0x65, 0x02, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x6C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x6A, 0x02, 0x00, 0x00, 0x6B, 0x02, 0x00, 0x00, 0x6C, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x6B, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6D, 0x02, 0x00, 0x00, - 0x56, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x6F, 0x02, 0x00, 0x00, - 0x6D, 0x02, 0x00, 0x00, 0x6E, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x6C, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x6C, 0x02, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x70, 0x02, 0x00, 0x00, 0x6A, 0x02, 0x00, 0x00, 0x63, 0x02, 0x00, 0x00, 0x6F, 0x02, 0x00, 0x00, - 0x6B, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x68, 0x02, 0x00, 0x00, 0x70, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x72, 0x02, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, - 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x73, 0x02, 0x00, 0x00, 0x72, 0x02, 0x00, 0x00, - 0x6E, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x75, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0x73, 0x02, 0x00, 0x00, 0x74, 0x02, 0x00, 0x00, 0x75, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x74, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x76, 0x02, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x78, 0x02, 0x00, 0x00, 0x76, 0x02, 0x00, 0x00, 0x77, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x75, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x75, 0x02, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x79, 0x02, 0x00, 0x00, 0x73, 0x02, 0x00, 0x00, 0x6C, 0x02, 0x00, 0x00, - 0x78, 0x02, 0x00, 0x00, 0x74, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x71, 0x02, 0x00, 0x00, - 0x79, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7B, 0x02, 0x00, 0x00, - 0x56, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x7C, 0x02, 0x00, 0x00, - 0x7B, 0x02, 0x00, 0x00, 0x77, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x7E, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x7C, 0x02, 0x00, 0x00, 0x7D, 0x02, 0x00, 0x00, - 0x7E, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7D, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7F, 0x02, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x81, 0x02, 0x00, 0x00, 0x7F, 0x02, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x7E, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7E, 0x02, 0x00, 0x00, - 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, 0x82, 0x02, 0x00, 0x00, 0x7C, 0x02, 0x00, 0x00, - 0x75, 0x02, 0x00, 0x00, 0x81, 0x02, 0x00, 0x00, 0x7D, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x7A, 0x02, 0x00, 0x00, 0x82, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x84, 0x02, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x85, 0x02, 0x00, 0x00, 0x84, 0x02, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0x87, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x85, 0x02, 0x00, 0x00, - 0x86, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x86, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x88, 0x02, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, - 0xB8, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x8A, 0x02, 0x00, 0x00, 0x88, 0x02, 0x00, 0x00, - 0x89, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x87, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x87, 0x02, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, 0x8B, 0x02, 0x00, 0x00, - 0x85, 0x02, 0x00, 0x00, 0x7E, 0x02, 0x00, 0x00, 0x8A, 0x02, 0x00, 0x00, 0x86, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x83, 0x02, 0x00, 0x00, 0x8B, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x8D, 0x02, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x8E, 0x02, 0x00, 0x00, 0x8D, 0x02, 0x00, 0x00, 0x89, 0x02, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x90, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x8E, 0x02, 0x00, 0x00, 0x8F, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x8F, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x91, 0x02, 0x00, 0x00, - 0x56, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x93, 0x02, 0x00, 0x00, - 0x91, 0x02, 0x00, 0x00, 0x92, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x90, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x90, 0x02, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x94, 0x02, 0x00, 0x00, 0x8E, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0x93, 0x02, 0x00, 0x00, - 0x8F, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x8C, 0x02, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x95, 0x02, 0x00, 0x00, 0x96, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x97, 0x02, 0x00, 0x00, 0x55, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x98, 0x02, 0x00, 0x00, 0x5F, 0x02, 0x00, 0x00, 0xA7, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x99, 0x02, 0x00, 0x00, 0x97, 0x02, 0x00, 0x00, 0x98, 0x02, 0x00, 0x00, - 0xA8, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x9A, 0x02, 0x00, 0x00, 0x99, 0x02, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x9D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x9A, 0x02, 0x00, 0x00, 0x9C, 0x02, 0x00, 0x00, 0xB2, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x9C, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x9F, 0x02, 0x00, 0x00, 0xA2, 0x02, 0x00, 0x00, - 0xA1, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xA9, 0x02, 0x00, 0x00, - 0xA4, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA8, 0x02, 0x00, 0x00, 0xA9, 0x02, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0xAB, 0x02, 0x00, 0x00, 0xAC, 0x02, 0x00, 0x00, 0xA7, 0x02, 0x00, 0x00, - 0xDC, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xAD, 0x02, 0x00, 0x00, - 0xAC, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xAA, 0x02, 0x00, 0x00, 0xAD, 0x02, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0xAE, 0x02, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0xA8, 0x02, 0x00, 0x00, 0xAA, 0x02, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xAF, 0x02, 0x00, 0x00, 0xA2, 0x02, 0x00, 0x00, 0xAE, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xB0, 0x02, 0x00, 0x00, 0xAF, 0x02, 0x00, 0x00, 0x39, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xB1, 0x02, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xB0, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x9B, 0x02, 0x00, 0x00, 0xB1, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x9D, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xB2, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xB3, 0x02, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x9B, 0x02, 0x00, 0x00, - 0xB3, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x9D, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x9D, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xB4, 0x02, 0x00, 0x00, - 0x9B, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x95, 0x02, 0x00, 0x00, 0xB4, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0xB5, 0x02, 0x00, 0x00, 0x55, 0x02, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0xB8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0xB5, 0x02, 0x00, 0x00, 0xB7, 0x02, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0xB7, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xBA, 0x02, 0x00, 0x00, - 0x95, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xB9, 0x02, 0x00, 0x00, 0xBA, 0x02, 0x00, 0x00, - 0x39, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xBB, 0x02, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0xB9, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xB6, 0x02, 0x00, 0x00, 0xBB, 0x02, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0xB8, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xBC, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xBD, 0x02, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xB6, 0x02, 0x00, 0x00, 0xBD, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0xB8, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xB8, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xBE, 0x02, 0x00, 0x00, 0xB6, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x95, 0x02, 0x00, 0x00, 0xBE, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, - 0xBF, 0x02, 0x00, 0x00, 0x5F, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0xC2, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xBF, 0x02, 0x00, 0x00, 0xC1, 0x02, 0x00, 0x00, - 0xC9, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xC1, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xC5, 0x02, 0x00, 0x00, 0xC4, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xC6, 0x02, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC7, 0x02, 0x00, 0x00, 0xC6, 0x02, 0x00, 0x00, - 0x8E, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xC8, 0x02, 0x00, 0x00, 0xC5, 0x02, 0x00, 0x00, - 0xC7, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xC0, 0x02, 0x00, 0x00, 0xC8, 0x02, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0xC2, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xC9, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xCA, 0x02, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xC0, 0x02, 0x00, 0x00, 0xCA, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0xC2, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xC2, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xCB, 0x02, 0x00, 0x00, 0xC0, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x95, 0x02, 0x00, 0x00, 0xCB, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, - 0xCC, 0x02, 0x00, 0x00, 0x7A, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xCD, 0x02, 0x00, 0x00, 0xC4, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xCE, 0x02, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0xE5, 0x00, 0x00, 0x00, - 0xCF, 0x02, 0x00, 0x00, 0xCC, 0x02, 0x00, 0x00, 0xCC, 0x02, 0x00, 0x00, 0xCC, 0x02, 0x00, 0x00, - 0xCC, 0x02, 0x00, 0x00, 0xA9, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0xD0, 0x02, 0x00, 0x00, - 0xCF, 0x02, 0x00, 0x00, 0xCD, 0x02, 0x00, 0x00, 0xCE, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x95, 0x02, 0x00, 0x00, 0xD0, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD1, 0x02, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0xD2, 0x02, 0x00, 0x00, - 0x68, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0xD4, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0xD2, 0x02, 0x00, 0x00, 0xD3, 0x02, 0x00, 0x00, 0xE4, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xD3, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xD9, 0x02, 0x00, 0x00, 0xD5, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD8, 0x02, 0x00, 0x00, - 0xD9, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xDB, 0x02, 0x00, 0x00, - 0xD6, 0x02, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0xDC, 0x02, 0x00, 0x00, - 0xDB, 0x02, 0x00, 0x00, 0xDB, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xDA, 0x02, 0x00, 0x00, 0xDC, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xDE, 0x02, 0x00, 0x00, 0xD6, 0x02, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xDF, 0x02, 0x00, 0x00, 0xDE, 0x02, 0x00, 0x00, 0xDE, 0x02, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xDD, 0x02, 0x00, 0x00, - 0xDF, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xE1, 0x02, 0x00, 0x00, - 0xD7, 0x02, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0xE2, 0x02, 0x00, 0x00, - 0xE1, 0x02, 0x00, 0x00, 0xE1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xE0, 0x02, 0x00, 0x00, 0xE2, 0x02, 0x00, 0x00, 0x39, 0x00, 0x08, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xE3, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0xD8, 0x02, 0x00, 0x00, - 0xDA, 0x02, 0x00, 0x00, 0xDD, 0x02, 0x00, 0x00, 0xE0, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xD1, 0x02, 0x00, 0x00, 0xE3, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xD4, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xE4, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, - 0xE5, 0x02, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0xE7, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xE5, 0x02, 0x00, 0x00, 0xE6, 0x02, 0x00, 0x00, - 0xFC, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xE6, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xE9, 0x02, 0x00, 0x00, 0xD5, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xE8, 0x02, 0x00, 0x00, 0xE9, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xEB, 0x02, 0x00, 0x00, 0xD6, 0x02, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xEC, 0x02, 0x00, 0x00, 0xEB, 0x02, 0x00, 0x00, 0xEB, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xEA, 0x02, 0x00, 0x00, 0xEC, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xEE, 0x02, 0x00, 0x00, 0xD6, 0x02, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0xEF, 0x02, 0x00, 0x00, 0xEE, 0x02, 0x00, 0x00, - 0xEE, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xED, 0x02, 0x00, 0x00, 0xEF, 0x02, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xF0, 0x02, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0xE8, 0x02, 0x00, 0x00, 0xEA, 0x02, 0x00, 0x00, - 0xED, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD1, 0x02, 0x00, 0x00, 0xF0, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF1, 0x02, 0x00, 0x00, 0xD1, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xF3, 0x02, 0x00, 0x00, 0xD5, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xF2, 0x02, 0x00, 0x00, 0xF3, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xF5, 0x02, 0x00, 0x00, 0xD6, 0x02, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xF6, 0x02, 0x00, 0x00, 0xF5, 0x02, 0x00, 0x00, 0xF5, 0x02, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xF4, 0x02, 0x00, 0x00, - 0xF6, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xF8, 0x02, 0x00, 0x00, - 0xD7, 0x02, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0xF9, 0x02, 0x00, 0x00, - 0xF8, 0x02, 0x00, 0x00, 0xF8, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xF7, 0x02, 0x00, 0x00, 0xF9, 0x02, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xFA, 0x02, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0xF2, 0x02, 0x00, 0x00, - 0xF4, 0x02, 0x00, 0x00, 0xF7, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xFB, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0xF1, 0x02, 0x00, 0x00, - 0xFA, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD1, 0x02, 0x00, 0x00, 0xFB, 0x02, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0xE7, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xFC, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0xFD, 0x02, 0x00, 0x00, 0x83, 0x02, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0xFF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0xFD, 0x02, 0x00, 0x00, 0xFE, 0x02, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0xFE, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, - 0xD5, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0xD6, 0x02, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x02, 0x03, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x03, 0x00, 0x00, 0xD6, 0x02, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x07, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x05, 0x03, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0xD7, 0x02, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0A, 0x03, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, - 0x09, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x08, 0x03, 0x00, 0x00, 0x0A, 0x03, 0x00, 0x00, 0x39, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0B, 0x03, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, - 0x05, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD1, 0x02, 0x00, 0x00, - 0x0B, 0x03, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xFF, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x0C, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x0D, 0x03, 0x00, 0x00, - 0x8C, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0x0D, 0x03, 0x00, 0x00, 0x0E, 0x03, 0x00, 0x00, 0x0F, 0x03, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x0E, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x12, 0x03, 0x00, 0x00, 0xD6, 0x02, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x13, 0x03, 0x00, 0x00, 0x12, 0x03, 0x00, 0x00, 0x12, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x14, 0x03, 0x00, 0x00, 0x15, 0x03, 0x00, 0x00, - 0x11, 0x03, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x15, 0x03, 0x00, 0x00, - 0x13, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, - 0xD6, 0x02, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x17, 0x03, 0x00, 0x00, - 0x16, 0x03, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x14, 0x03, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x11, 0x03, 0x00, 0x00, - 0xE9, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x18, 0x03, 0x00, 0x00, 0x17, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1A, 0x03, 0x00, 0x00, 0xD7, 0x02, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1B, 0x03, 0x00, 0x00, 0x1A, 0x03, 0x00, 0x00, - 0x1A, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x14, 0x03, 0x00, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x11, 0x03, 0x00, 0x00, 0x19, 0x03, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x1B, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x1E, 0x03, 0x00, 0x00, 0xD7, 0x02, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1F, 0x03, 0x00, 0x00, 0x1E, 0x03, 0x00, 0x00, 0x1E, 0x03, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x14, 0x03, 0x00, 0x00, - 0x20, 0x03, 0x00, 0x00, 0x11, 0x03, 0x00, 0x00, 0x1D, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x20, 0x03, 0x00, 0x00, 0x1F, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x23, 0x03, 0x00, 0x00, 0x22, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x24, 0x03, 0x00, 0x00, 0x23, 0x03, 0x00, 0x00, 0x23, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x14, 0x03, 0x00, 0x00, 0x25, 0x03, 0x00, 0x00, - 0x11, 0x03, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x25, 0x03, 0x00, 0x00, - 0x24, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x27, 0x03, 0x00, 0x00, - 0x22, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x28, 0x03, 0x00, 0x00, - 0x27, 0x03, 0x00, 0x00, 0x27, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x14, 0x03, 0x00, 0x00, 0x29, 0x03, 0x00, 0x00, 0x11, 0x03, 0x00, 0x00, - 0x26, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x29, 0x03, 0x00, 0x00, 0x28, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2C, 0x03, 0x00, 0x00, 0x2B, 0x03, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2D, 0x03, 0x00, 0x00, 0x2C, 0x03, 0x00, 0x00, - 0x2C, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x14, 0x03, 0x00, 0x00, 0x2E, 0x03, 0x00, 0x00, 0x11, 0x03, 0x00, 0x00, 0x2A, 0x03, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x2E, 0x03, 0x00, 0x00, 0x2D, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x2B, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x31, 0x03, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x14, 0x03, 0x00, 0x00, - 0x32, 0x03, 0x00, 0x00, 0x11, 0x03, 0x00, 0x00, 0x2F, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x32, 0x03, 0x00, 0x00, 0x31, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x36, 0x03, 0x00, 0x00, 0xD5, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x35, 0x03, 0x00, 0x00, - 0x36, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x38, 0x03, 0x00, 0x00, - 0x11, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x37, 0x03, 0x00, 0x00, 0x38, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x3A, 0x03, 0x00, 0x00, 0x34, 0x03, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x39, 0x03, 0x00, 0x00, 0x3A, 0x03, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3B, 0x03, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x35, 0x03, 0x00, 0x00, - 0x37, 0x03, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD1, 0x02, 0x00, 0x00, - 0x3B, 0x03, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x0F, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x0F, 0x03, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xFF, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0xFF, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xE7, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0xE7, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xD4, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0xD4, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x3C, 0x03, 0x00, 0x00, - 0x55, 0x02, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x3D, 0x03, 0x00, 0x00, - 0x3C, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x3E, 0x03, 0x00, 0x00, - 0x5F, 0x02, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x3F, 0x03, 0x00, 0x00, - 0x3E, 0x03, 0x00, 0x00, 0xA7, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00, 0x00, - 0x3D, 0x03, 0x00, 0x00, 0x3F, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x41, 0x03, 0x00, 0x00, 0x7A, 0x02, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x42, 0x03, 0x00, 0x00, 0x41, 0x03, 0x00, 0x00, 0xA7, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x43, 0x03, 0x00, 0x00, 0x40, 0x03, 0x00, 0x00, 0x42, 0x03, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0x46, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x43, 0x03, 0x00, 0x00, - 0x45, 0x03, 0x00, 0x00, 0x51, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x45, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x47, 0x03, 0x00, 0x00, 0xD1, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x49, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4A, 0x03, 0x00, 0x00, 0x47, 0x03, 0x00, 0x00, - 0x49, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4C, 0x03, 0x00, 0x00, - 0x95, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x4B, 0x03, 0x00, 0x00, 0x4C, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4E, 0x03, 0x00, 0x00, 0xC4, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x4D, 0x03, 0x00, 0x00, 0x4E, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x4F, 0x03, 0x00, 0x00, 0x4A, 0x03, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x50, 0x03, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x4B, 0x03, 0x00, 0x00, 0x4D, 0x03, 0x00, 0x00, - 0x4F, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x44, 0x03, 0x00, 0x00, 0x50, 0x03, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x46, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x51, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x44, 0x03, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x46, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x46, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x53, 0x03, 0x00, 0x00, 0x44, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x95, 0x02, 0x00, 0x00, 0x53, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x55, 0x03, 0x00, 0x00, 0x54, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x56, 0x03, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x57, 0x03, 0x00, 0x00, 0x56, 0x03, 0x00, 0x00, 0x55, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x95, 0x02, 0x00, 0x00, 0x57, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x5A, 0x03, 0x00, 0x00, 0x59, 0x03, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x5C, 0x03, 0x00, 0x00, 0x5A, 0x03, 0x00, 0x00, 0x5B, 0x03, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x5D, 0x03, 0x00, 0x00, 0x5C, 0x03, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5E, 0x03, 0x00, 0x00, 0x5D, 0x03, 0x00, 0x00, - 0x5B, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x58, 0x03, 0x00, 0x00, 0x5E, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x60, 0x03, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x63, 0x03, 0x00, 0x00, 0xD5, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x62, 0x03, 0x00, 0x00, 0x63, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x65, 0x03, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x64, 0x03, 0x00, 0x00, - 0x65, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x67, 0x03, 0x00, 0x00, - 0x5F, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x66, 0x03, 0x00, 0x00, 0x67, 0x03, 0x00, 0x00, - 0x39, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x68, 0x03, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, - 0x60, 0x03, 0x00, 0x00, 0x62, 0x03, 0x00, 0x00, 0x64, 0x03, 0x00, 0x00, 0x66, 0x03, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x95, 0x02, 0x00, 0x00, 0x68, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x69, 0x03, 0x00, 0x00, 0x6A, 0x03, 0x00, 0x00, 0xA7, 0x02, 0x00, 0x00, 0xE9, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x6B, 0x03, 0x00, 0x00, 0x6A, 0x03, 0x00, 0x00, - 0xAB, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x6C, 0x03, 0x00, 0x00, 0x6B, 0x03, 0x00, 0x00, - 0xDC, 0x01, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x6E, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0x6C, 0x03, 0x00, 0x00, 0x6D, 0x03, 0x00, 0x00, 0x6E, 0x03, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x6D, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x6F, 0x03, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x70, 0x03, 0x00, 0x00, 0x6F, 0x03, 0x00, 0x00, 0xB4, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x71, 0x03, 0x00, 0x00, 0x70, 0x03, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x6E, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x6E, 0x03, 0x00, 0x00, - 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, 0x72, 0x03, 0x00, 0x00, 0x6C, 0x03, 0x00, 0x00, - 0x46, 0x03, 0x00, 0x00, 0x71, 0x03, 0x00, 0x00, 0x6D, 0x03, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0x74, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x72, 0x03, 0x00, 0x00, - 0x73, 0x03, 0x00, 0x00, 0x74, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x73, 0x03, 0x00, 0x00, - 0xFC, 0x00, 0x01, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x74, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x76, 0x03, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x0B, 0x01, 0x00, 0x00, 0x76, 0x03, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x5F, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x60, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x07, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x6A, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x6A, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x6B, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, - 0x5E, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, - 0x5E, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, - 0x79, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x89, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, - 0x8D, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x90, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, - 0x8E, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x91, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x7C, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x92, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x93, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x94, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, - 0x94, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x9A, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x9E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x9C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x9D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x9E, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, - 0xA0, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x9B, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x9D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x9D, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, - 0xFE, 0x00, 0x02, 0x00, 0xA2, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xA6, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, - 0xA6, 0x00, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA5, 0x00, 0x00, 0x00, - 0xA8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, - 0xA5, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, - 0xA9, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0xAD, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, - 0xAE, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xAB, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAD, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xAF, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xB0, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xAB, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0xAD, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xAD, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, - 0xB1, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, - 0xB5, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, - 0xB6, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xBA, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xBB, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, - 0xBB, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x29, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, - 0xC0, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, - 0xBF, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, - 0xC3, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, - 0xC6, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, - 0xC2, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0xC8, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xCB, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0xCB, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x00, - 0xCE, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, - 0xCC, 0x00, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0xD0, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x2F, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x33, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD5, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD4, 0x00, 0x00, 0x00, 0xD5, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0xD4, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD3, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xD9, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, - 0xDD, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0xD8, 0x00, 0x00, 0x00, 0xD9, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xD7, 0x00, 0x00, 0x00, 0xDF, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xE1, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0xE2, 0x00, 0x00, 0x00, 0xE1, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, - 0xE5, 0x00, 0x00, 0x00, 0xE6, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, - 0xE2, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xE7, 0x00, 0x00, 0x00, 0xE6, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xE0, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, - 0xED, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xEF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, - 0xEA, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE8, 0x00, 0x00, 0x00, - 0xEF, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, - 0x31, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 0x00, - 0xF3, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, - 0xF4, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xF6, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xF7, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0xE5, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, - 0xF5, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xF0, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xFB, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xFC, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xFD, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0xFE, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xFA, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0xA9, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, - 0xF3, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x08, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, - 0x07, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, - 0x08, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x02, 0x01, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0xFA, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, - 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, - 0x0F, 0x01, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x0C, 0x01, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x0B, 0x01, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x12, 0x01, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x12, 0x01, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x15, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x16, 0x01, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x17, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, - 0x18, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x15, 0x01, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1A, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x1A, 0x01, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, - 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x42, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x1E, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x1E, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, - 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x23, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x22, 0x01, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x24, 0x01, 0x00, 0x00, 0x1D, 0x01, 0x00, 0x00, 0x23, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, - 0x24, 0x01, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x3E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x28, 0x01, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3E, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, - 0x3C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00, - 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x2B, 0x01, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00, - 0x39, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2D, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x2B, 0x01, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2E, 0x01, 0x00, 0x00, - 0x2A, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2F, 0x01, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x2D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, 0x2D, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x32, 0x01, 0x00, 0x00, - 0x2E, 0x01, 0x00, 0x00, 0x2F, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x33, 0x01, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, - 0x27, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x32, 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, - 0x54, 0x00, 0x04, 0x00, 0x27, 0x01, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x29, 0x01, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x39, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x27, 0x01, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x3D, 0x01, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x3C, 0x01, 0x00, 0x00, 0x3D, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x3F, 0x01, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x3E, 0x01, 0x00, 0x00, - 0x3F, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x3E, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, - 0x40, 0x01, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x44, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x43, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x47, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x4B, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x52, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x53, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x55, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x5D, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x44, 0x01, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x45, 0x01, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x46, 0x01, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, 0x45, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x43, 0x01, 0x00, 0x00, 0x46, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x48, 0x01, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x49, 0x01, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x4A, 0x01, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x47, 0x01, 0x00, 0x00, 0x4A, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x4C, 0x01, 0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x4D, 0x01, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4E, 0x01, 0x00, 0x00, 0x4C, 0x01, 0x00, 0x00, 0x4D, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x4F, 0x01, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x51, 0x01, 0x00, 0x00, 0x4F, 0x01, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x52, 0x01, 0x00, 0x00, 0x4E, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x53, 0x01, 0x00, 0x00, 0x51, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x54, 0x01, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0x53, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x4B, 0x01, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x56, 0x01, 0x00, 0x00, 0x4B, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x57, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x56, 0x01, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x55, 0x01, 0x00, 0x00, 0x57, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x58, 0x01, 0x00, 0x00, 0x47, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x59, 0x01, 0x00, 0x00, 0x55, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x5A, 0x01, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x5B, 0x01, 0x00, 0x00, 0x5A, 0x01, 0x00, 0x00, 0x59, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x5C, 0x01, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, 0x5B, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x5D, 0x01, 0x00, 0x00, 0x5C, 0x01, 0x00, 0x00, 0x39, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x5E, 0x01, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x5D, 0x01, 0x00, 0x00, - 0xFE, 0x00, 0x02, 0x00, 0x5E, 0x01, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x61, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x65, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x69, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x6D, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x71, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x75, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x79, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x82, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x83, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x88, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x91, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x92, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x97, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0xA0, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0xA1, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0xA6, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xA7, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xA9, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xAC, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xB1, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xB3, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xBC, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xBE, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xC8, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xCA, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x62, 0x01, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x63, 0x01, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x64, 0x01, 0x00, 0x00, 0x62, 0x01, 0x00, 0x00, 0x63, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x61, 0x01, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x66, 0x01, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x67, 0x01, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x68, 0x01, 0x00, 0x00, 0x66, 0x01, 0x00, 0x00, 0x67, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x65, 0x01, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x6A, 0x01, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x6B, 0x01, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x6C, 0x01, 0x00, 0x00, 0x6A, 0x01, 0x00, 0x00, 0x6B, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x69, 0x01, 0x00, 0x00, 0x6C, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x6E, 0x01, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x6F, 0x01, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x70, 0x01, 0x00, 0x00, 0x6E, 0x01, 0x00, 0x00, 0x6F, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x6D, 0x01, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x72, 0x01, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x73, 0x01, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x74, 0x01, 0x00, 0x00, 0x72, 0x01, 0x00, 0x00, 0x73, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x71, 0x01, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x76, 0x01, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x77, 0x01, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x78, 0x01, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, 0x77, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x75, 0x01, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x7A, 0x01, 0x00, 0x00, 0x6D, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x7B, 0x01, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x7C, 0x01, 0x00, 0x00, 0x6D, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x7D, 0x01, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x7E, 0x01, 0x00, 0x00, 0x7C, 0x01, 0x00, 0x00, 0x7D, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x7F, 0x01, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x81, 0x01, 0x00, 0x00, 0x7F, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x82, 0x01, 0x00, 0x00, 0x7E, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x83, 0x01, 0x00, 0x00, 0x81, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x84, 0x01, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x82, 0x01, 0x00, 0x00, 0x83, 0x01, 0x00, 0x00, - 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0x85, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, - 0x8E, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x86, 0x01, 0x00, 0x00, 0x7B, 0x01, 0x00, 0x00, - 0x85, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x87, 0x01, 0x00, 0x00, - 0x7A, 0x01, 0x00, 0x00, 0x86, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x79, 0x01, 0x00, 0x00, - 0x87, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x89, 0x01, 0x00, 0x00, - 0x71, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8A, 0x01, 0x00, 0x00, - 0x65, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, - 0x71, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, - 0x65, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8D, 0x01, 0x00, 0x00, - 0x8B, 0x01, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x8E, 0x01, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x8F, 0x01, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x90, 0x01, 0x00, 0x00, 0x8E, 0x01, 0x00, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x91, 0x01, 0x00, 0x00, 0x8D, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x92, 0x01, 0x00, 0x00, - 0x90, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x93, 0x01, 0x00, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x91, 0x01, 0x00, 0x00, 0x92, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x93, 0x01, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x8A, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x96, 0x01, 0x00, 0x00, 0x89, 0x01, 0x00, 0x00, - 0x95, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x88, 0x01, 0x00, 0x00, 0x96, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x75, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x99, 0x01, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9A, 0x01, 0x00, 0x00, 0x75, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9B, 0x01, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, - 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9C, 0x01, 0x00, 0x00, 0x9A, 0x01, 0x00, 0x00, - 0x9B, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9D, 0x01, 0x00, 0x00, - 0x69, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9E, 0x01, 0x00, 0x00, - 0x69, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9F, 0x01, 0x00, 0x00, - 0x9D, 0x01, 0x00, 0x00, 0x9E, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA0, 0x01, 0x00, 0x00, - 0x9C, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA1, 0x01, 0x00, 0x00, 0x9F, 0x01, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA2, 0x01, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0xA0, 0x01, 0x00, 0x00, 0xA1, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xA3, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xA2, 0x01, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xA4, 0x01, 0x00, 0x00, 0x99, 0x01, 0x00, 0x00, 0xA3, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xA5, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0xA4, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x97, 0x01, 0x00, 0x00, 0xA5, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xA8, 0x01, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xA7, 0x01, 0x00, 0x00, 0xA8, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xAA, 0x01, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA9, 0x01, 0x00, 0x00, - 0xAA, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAB, 0x01, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0xA7, 0x01, 0x00, 0x00, 0xA9, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xA6, 0x01, 0x00, 0x00, 0xAB, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xAD, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xAE, 0x01, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xAF, 0x01, 0x00, 0x00, 0xAD, 0x01, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xA6, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xB2, 0x01, 0x00, 0x00, 0x6D, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xB1, 0x01, 0x00, 0x00, 0xB2, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xB4, 0x01, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xB3, 0x01, 0x00, 0x00, - 0xB4, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB5, 0x01, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0xB1, 0x01, 0x00, 0x00, 0xB3, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xB6, 0x01, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xB5, 0x01, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0xB7, 0x01, 0x00, 0x00, 0xAF, 0x01, 0x00, 0x00, - 0xB6, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xB8, 0x01, 0x00, 0x00, - 0x88, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xB9, 0x01, 0x00, 0x00, - 0x88, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBA, 0x01, 0x00, 0x00, - 0xB8, 0x01, 0x00, 0x00, 0xB9, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xBB, 0x01, 0x00, 0x00, 0xA6, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xBD, 0x01, 0x00, 0x00, 0x71, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xBC, 0x01, 0x00, 0x00, - 0xBD, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xBF, 0x01, 0x00, 0x00, - 0x65, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xBE, 0x01, 0x00, 0x00, 0xBF, 0x01, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0xBC, 0x01, 0x00, 0x00, 0xBE, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xC1, 0x01, 0x00, 0x00, 0xBB, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0xBA, 0x01, 0x00, 0x00, 0xC1, 0x01, 0x00, 0x00, - 0x0C, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0xC3, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0xB7, 0x01, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xC4, 0x01, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xC5, 0x01, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xC6, 0x01, 0x00, 0x00, 0xC4, 0x01, 0x00, 0x00, 0xC5, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0xA6, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xC9, 0x01, 0x00, 0x00, 0x75, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xC9, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xCB, 0x01, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xCA, 0x01, 0x00, 0x00, 0xCB, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xCC, 0x01, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xCA, 0x01, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCD, 0x01, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, - 0xCC, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0xCE, 0x01, 0x00, 0x00, - 0xC6, 0x01, 0x00, 0x00, 0xCD, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xCF, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0xC3, 0x01, 0x00, 0x00, - 0xCE, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xAC, 0x01, 0x00, 0x00, 0xCF, 0x01, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0xD0, 0x01, 0x00, 0x00, 0xAC, 0x01, 0x00, 0x00, - 0xB8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD1, 0x01, 0x00, 0x00, - 0xD0, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD2, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xD1, 0x01, 0x00, 0x00, 0x7F, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xD3, 0x01, 0x00, 0x00, 0xD2, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, 0xAC, 0x01, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD5, 0x01, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, - 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD6, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xD5, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xD7, 0x01, 0x00, 0x00, 0xD3, 0x01, 0x00, 0x00, 0xD6, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, - 0xD7, 0x01, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x4E, 0x00, 0x00, 0x00, - 0x53, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x50, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x56, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0xDA, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0xE5, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, - 0xE6, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, - 0xE7, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xF3, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xFB, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x14, 0x02, 0x00, 0x00, - 0x15, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xDB, 0x01, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xDD, 0x01, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xDE, 0x01, 0x00, 0x00, 0xDD, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xDF, 0x01, 0x00, 0x00, 0xDB, 0x01, 0x00, 0x00, 0xDE, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0xE1, 0x01, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0xDC, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xE2, 0x01, 0x00, 0x00, - 0xE1, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0xE3, 0x01, 0x00, 0x00, - 0xE0, 0x01, 0x00, 0x00, 0xE2, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xE4, 0x01, 0x00, 0x00, 0xDF, 0x01, 0x00, 0x00, 0xE3, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xDA, 0x01, 0x00, 0x00, 0xE4, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE5, 0x01, 0x00, 0x00, - 0xF3, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE6, 0x01, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0xE8, 0x01, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x05, 0x00, 0x4F, 0x00, 0x00, 0x00, 0xEA, 0x01, 0x00, 0x00, 0xE8, 0x01, 0x00, 0x00, - 0xE9, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE7, 0x01, 0x00, 0x00, 0xEA, 0x01, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0xEB, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xEB, 0x01, 0x00, 0x00, - 0xF6, 0x00, 0x04, 0x00, 0xED, 0x01, 0x00, 0x00, 0xEE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0xEF, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xEF, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xE6, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0xF1, 0x01, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, - 0xB1, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0xF2, 0x01, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, - 0xF1, 0x01, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xF2, 0x01, 0x00, 0x00, 0xEC, 0x01, 0x00, 0x00, - 0xED, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xEC, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0xE7, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x11, 0x00, 0x00, 0x00, 0xF5, 0x01, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xF6, 0x01, 0x00, 0x00, 0xF5, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0xF7, 0x01, 0x00, 0x00, 0xE6, 0x01, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0xF7, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xF9, 0x01, 0x00, 0x00, - 0xF8, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0xFA, 0x01, 0x00, 0x00, - 0xF6, 0x01, 0x00, 0x00, 0xF9, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xF3, 0x01, 0x00, 0x00, - 0xFA, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, - 0x52, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0xFD, 0x01, 0x00, 0x00, - 0xE6, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, - 0x53, 0x00, 0x00, 0x00, 0xFD, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xFF, 0x01, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xFB, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x02, 0x02, 0x00, 0x00, 0xFB, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x03, 0x02, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x04, 0x02, 0x00, 0x00, 0xFB, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x05, 0x02, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x06, 0x02, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, - 0x88, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0A, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, - 0x09, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0B, 0x02, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x02, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0xF3, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x00, 0x00, - 0x03, 0x02, 0x00, 0x00, 0x0B, 0x02, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x0D, 0x02, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x0C, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x01, 0x02, 0x00, 0x00, 0x0D, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x0E, 0x02, 0x00, 0x00, 0xDA, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x0F, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x10, 0x02, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x11, 0x02, 0x00, 0x00, 0x0F, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x0E, 0x02, 0x00, 0x00, 0x11, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xDA, 0x01, 0x00, 0x00, - 0x12, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x02, 0x00, 0x00, - 0x52, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x17, 0x02, 0x00, 0x00, 0x16, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0x18, 0x02, 0x00, 0x00, 0xE6, 0x01, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x19, 0x02, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x02, 0x00, 0x00, 0x19, 0x02, 0x00, 0x00, - 0xBE, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x1B, 0x02, 0x00, 0x00, 0x17, 0x02, 0x00, 0x00, - 0x1A, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1C, 0x02, 0x00, 0x00, - 0x52, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1D, 0x02, 0x00, 0x00, 0x1C, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0x1E, 0x02, 0x00, 0x00, 0xE7, 0x01, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x1F, 0x02, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x1E, 0x02, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x1F, 0x02, 0x00, 0x00, - 0xB8, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x1D, 0x02, 0x00, 0x00, - 0x20, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x22, 0x02, 0x00, 0x00, - 0xF3, 0x01, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x23, 0x02, 0x00, 0x00, 0x22, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x24, 0x02, 0x00, 0x00, 0xFB, 0x01, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x25, 0x02, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x26, 0x02, 0x00, 0x00, 0x23, 0x02, 0x00, 0x00, 0x25, 0x02, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x27, 0x02, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, - 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, - 0x27, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x29, 0x02, 0x00, 0x00, - 0xFB, 0x01, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2A, 0x02, 0x00, 0x00, 0x29, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x2B, 0x02, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x2A, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x2C, 0x02, 0x00, 0x00, 0x26, 0x02, 0x00, 0x00, 0x2B, 0x02, 0x00, 0x00, - 0x50, 0x00, 0x06, 0x00, 0x13, 0x02, 0x00, 0x00, 0x2D, 0x02, 0x00, 0x00, 0x1B, 0x02, 0x00, 0x00, - 0x21, 0x02, 0x00, 0x00, 0x2C, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x15, 0x02, 0x00, 0x00, - 0x2D, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x02, 0x00, 0x00, 0x2E, 0x02, 0x00, 0x00, - 0x15, 0x02, 0x00, 0x00, 0x9B, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x2F, 0x02, 0x00, 0x00, - 0x2E, 0x02, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x30, 0x02, 0x00, 0x00, - 0x2F, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x32, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0x30, 0x02, 0x00, 0x00, 0x31, 0x02, 0x00, 0x00, 0x32, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x31, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x02, 0x00, 0x00, - 0x33, 0x02, 0x00, 0x00, 0x15, 0x02, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x13, 0x02, 0x00, 0x00, - 0x34, 0x02, 0x00, 0x00, 0x33, 0x02, 0x00, 0x00, 0x9B, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x35, 0x02, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x32, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x32, 0x02, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x36, 0x02, 0x00, 0x00, 0x2F, 0x02, 0x00, 0x00, 0xEC, 0x01, 0x00, 0x00, 0x35, 0x02, 0x00, 0x00, - 0x31, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x38, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0x36, 0x02, 0x00, 0x00, 0x37, 0x02, 0x00, 0x00, 0x38, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x37, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x39, 0x02, 0x00, 0x00, 0xE5, 0x01, 0x00, 0x00, 0x7F, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3A, 0x02, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE5, 0x01, 0x00, 0x00, - 0x3A, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x38, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x38, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xEE, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0xEE, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x3B, 0x02, 0x00, 0x00, - 0xE6, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE7, 0x01, 0x00, 0x00, 0x3B, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x00, 0x00, 0xE6, 0x01, 0x00, 0x00, - 0x80, 0x00, 0x05, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x3D, 0x02, 0x00, 0x00, 0x3C, 0x02, 0x00, 0x00, - 0xE9, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE6, 0x01, 0x00, 0x00, 0x3D, 0x02, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0xEB, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xED, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x00, 0x00, 0xE5, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3F, 0x02, 0x00, 0x00, 0xDA, 0x01, 0x00, 0x00, - 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x3F, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x41, 0x02, 0x00, 0x00, 0x3E, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, - 0x41, 0x02, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x59, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x5D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x44, 0x02, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x45, 0x02, 0x00, 0x00, - 0x44, 0x02, 0x00, 0x00, 0x44, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x46, 0x02, 0x00, 0x00, - 0x5B, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x47, 0x02, 0x00, 0x00, - 0x46, 0x02, 0x00, 0x00, 0x46, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x48, 0x02, 0x00, 0x00, - 0x5B, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x49, 0x02, 0x00, 0x00, 0x48, 0x02, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x4A, 0x02, 0x00, 0x00, 0x49, 0x02, 0x00, 0x00, 0x49, 0x02, 0x00, 0x00, 0x49, 0x02, 0x00, 0x00, - 0x0C, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x4B, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x45, 0x02, 0x00, 0x00, 0x47, 0x02, 0x00, 0x00, 0x4A, 0x02, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x4C, 0x02, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4D, 0x02, 0x00, 0x00, - 0x4C, 0x02, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4E, 0x02, 0x00, 0x00, - 0x4B, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4F, 0x02, 0x00, 0x00, 0x4B, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x4B, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x51, 0x02, 0x00, 0x00, 0x4E, 0x02, 0x00, 0x00, - 0x4F, 0x02, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x4D, 0x02, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, - 0x51, 0x02, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, +#ifndef __EMSCRIPTEN__ +static const uint8_t s_flash_shd_bytecode_draw_content[21720] = { + 0x03,0x02,0x23,0x07,0x00,0x03,0x01,0x00,0x0B,0x00,0x08,0x00,0x8F,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0B,0x00,0x06,0x00, + 0x01,0x00,0x00,0x00,0x47,0x4C,0x53,0x4C,0x2E,0x73,0x74,0x64,0x2E,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0F,0x00,0x11,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, + 0x00,0x00,0x00,0x00,0x0B,0x01,0x00,0x00,0x59,0x02,0x00,0x00,0x60,0x02,0x00,0x00, + 0x6D,0x02,0x00,0x00,0xD6,0x02,0x00,0x00,0xE7,0x02,0x00,0x00,0xE8,0x02,0x00,0x00, + 0x31,0x03,0x00,0x00,0x39,0x03,0x00,0x00,0x42,0x03,0x00,0x00,0x71,0x03,0x00,0x00, + 0x7A,0x03,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xC2,0x01,0x00,0x00,0x04,0x00,0x09,0x00, + 0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x68,0x61,0x64,0x69,0x6E,0x67,0x5F,0x6C, + 0x61,0x6E,0x67,0x75,0x61,0x67,0x65,0x5F,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x00, + 0x04,0x00,0x0A,0x00,0x47,0x4C,0x5F,0x47,0x4F,0x4F,0x47,0x4C,0x45,0x5F,0x63,0x70, + 0x70,0x5F,0x73,0x74,0x79,0x6C,0x65,0x5F,0x6C,0x69,0x6E,0x65,0x5F,0x64,0x69,0x72, + 0x65,0x63,0x74,0x69,0x76,0x65,0x00,0x00,0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00, + 0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0B,0x00,0x00,0x00, + 0x67,0x61,0x6D,0x6D,0x61,0x28,0x76,0x66,0x34,0x3B,0x00,0x00,0x05,0x00,0x03,0x00, + 0x0A,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x0E,0x00,0x00,0x00, + 0x64,0x65,0x5F,0x67,0x61,0x6D,0x6D,0x61,0x28,0x76,0x66,0x34,0x3B,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x0D,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x07,0x00, + 0x15,0x00,0x00,0x00,0x73,0x6D,0x6F,0x6F,0x74,0x68,0x5F,0x75,0x76,0x28,0x76,0x66, + 0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x13,0x00,0x00,0x00, + 0x75,0x76,0x00,0x00,0x05,0x00,0x06,0x00,0x14,0x00,0x00,0x00,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x1B,0x00,0x00,0x00,0x73,0x61,0x66,0x65,0x5F,0x64,0x69,0x76,0x28,0x66,0x31,0x3B, + 0x66,0x31,0x3B,0x00,0x05,0x00,0x03,0x00,0x19,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x1A,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x1F,0x00,0x00,0x00,0x73,0x61,0x66,0x65,0x5F,0x6C,0x65,0x6E,0x28,0x76,0x66,0x32, + 0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x1E,0x00,0x00,0x00,0x76,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x23,0x00,0x00,0x00,0x73,0x6B,0x65,0x77,0x28,0x76,0x66,0x32, + 0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x22,0x00,0x00,0x00,0x76,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x28,0x00,0x00,0x00,0x64,0x65,0x74,0x32,0x28,0x76,0x66,0x32, + 0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x26,0x00,0x00,0x00, + 0x61,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x27,0x00,0x00,0x00,0x62,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x2C,0x00,0x00,0x00,0x73,0x64,0x66,0x5F,0x73,0x74,0x72,0x6F, + 0x6B,0x65,0x28,0x66,0x31,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x2B,0x00,0x00,0x00, + 0x64,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x32,0x00,0x00,0x00,0x73,0x64,0x66,0x28, + 0x76,0x66,0x34,0x3B,0x76,0x66,0x34,0x3B,0x66,0x31,0x3B,0x00,0x05,0x00,0x03,0x00, + 0x2F,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x30,0x00,0x00,0x00, + 0x62,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x31,0x00,0x00,0x00,0x64,0x00,0x00,0x00, + 0x05,0x00,0x08,0x00,0x36,0x00,0x00,0x00,0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65, + 0x5F,0x61,0x61,0x62,0x62,0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00, + 0x05,0x00,0x03,0x00,0x34,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x35,0x00,0x00,0x00,0x68,0x65,0x00,0x00,0x05,0x00,0x0A,0x00,0x3D,0x00,0x00,0x00, + 0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x5F,0x62,0x6F,0x78,0x28,0x76,0x66,0x32, + 0x3B,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x39,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x3A,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x3B,0x00,0x00,0x00, + 0x68,0x65,0x00,0x00,0x05,0x00,0x03,0x00,0x3C,0x00,0x00,0x00,0x75,0x00,0x00,0x00, + 0x05,0x00,0x0A,0x00,0x43,0x00,0x00,0x00,0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65, + 0x5F,0x73,0x65,0x67,0x6D,0x65,0x6E,0x74,0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32, + 0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x40,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x41,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x42,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x05,0x00,0x0B,0x00, + 0x49,0x00,0x00,0x00,0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x5F,0x74,0x72,0x69, + 0x61,0x6E,0x67,0x6C,0x65,0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x76,0x66, + 0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x45,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x46,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x47,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x48,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x0A,0x00,0x55,0x00,0x00,0x00, + 0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x5F,0x70,0x6F,0x6C,0x79,0x67,0x6F,0x6E, + 0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x5B,0x38,0x5D,0x3B,0x69,0x31,0x3B,0x00, + 0x05,0x00,0x03,0x00,0x52,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x53,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x54,0x00,0x00,0x00, + 0x4E,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x57,0x00,0x00,0x00,0x53,0x68,0x61,0x64, + 0x65,0x72,0x50,0x61,0x72,0x61,0x6D,0x73,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00, + 0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x76,0x69,0x65,0x77,0x5F,0x70,0x6F,0x73, + 0x00,0x00,0x00,0x00,0x06,0x00,0x04,0x00,0x57,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x75,0x76,0x00,0x00,0x06,0x00,0x05,0x00,0x57,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x75,0x76,0x5F,0x6D,0x69,0x6E,0x00,0x00,0x06,0x00,0x05,0x00,0x57,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x75,0x76,0x5F,0x6D,0x61,0x78,0x00,0x00,0x06,0x00,0x06,0x00, + 0x57,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75, + 0x76,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x57,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x73,0x00,0x00,0x05,0x00,0x11,0x00, + 0x5C,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x65,0x72,0x28,0x76,0x66,0x34,0x3B,0x73, + 0x74,0x72,0x75,0x63,0x74,0x2D,0x53,0x68,0x61,0x64,0x65,0x72,0x50,0x61,0x72,0x61, + 0x6D,0x73,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76, + 0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x34,0x31,0x3B,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x5A,0x00,0x00,0x00,0x63,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x5B,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x73,0x00,0x00, + 0x05,0x00,0x04,0x00,0x7C,0x00,0x00,0x00,0x70,0x69,0x78,0x65,0x6C,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x80,0x00,0x00,0x00,0x73,0x65,0x61,0x6D,0x00,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0xA5,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0xCE,0x00,0x00,0x00,0x76,0x5F,0x73,0x74,0x72,0x6F,0x6B,0x65,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xD3,0x00,0x00,0x00,0x77,0x69,0x72,0x65,0x5F,0x64,0x00,0x00, + 0x05,0x00,0x04,0x00,0xD4,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0xD7,0x00,0x00,0x00,0x73,0x74,0x72,0x6F,0x6B,0x65,0x5F,0x61, + 0x61,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xDA,0x00,0x00,0x00,0x76,0x5F,0x61,0x61, + 0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0xE0,0x00,0x00,0x00,0x73,0x74,0x72,0x6F, + 0x6B,0x65,0x5F,0x6E,0x6F,0x5F,0x61,0x61,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xE8,0x00,0x00,0x00,0x66,0x69,0x6C,0x6C,0x5F,0x61,0x61,0x00,0x05,0x00,0x05,0x00, + 0xF0,0x00,0x00,0x00,0x66,0x69,0x6C,0x6C,0x5F,0x6E,0x6F,0x5F,0x61,0x61,0x00,0x00, + 0x05,0x00,0x04,0x00,0xFA,0x00,0x00,0x00,0x73,0x74,0x72,0x6F,0x6B,0x65,0x00,0x00, + 0x05,0x00,0x04,0x00,0x02,0x01,0x00,0x00,0x66,0x69,0x6C,0x6C,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x0B,0x01,0x00,0x00,0x72,0x65,0x73,0x75,0x6C,0x74,0x00,0x00, + 0x05,0x00,0x04,0x00,0x0E,0x01,0x00,0x00,0x76,0x5F,0x66,0x69,0x6C,0x6C,0x00,0x00, + 0x05,0x00,0x03,0x00,0x15,0x01,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x29,0x01,0x00,0x00,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x2B,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x3C,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x3E,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x43,0x01,0x00,0x00, + 0x6E,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x47,0x01,0x00,0x00,0x70,0x61,0x00,0x00, + 0x05,0x00,0x03,0x00,0x4B,0x01,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x52,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x53,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x55,0x01,0x00,0x00,0x68,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x5D,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x61,0x01,0x00,0x00, + 0x65,0x30,0x00,0x00,0x05,0x00,0x03,0x00,0x65,0x01,0x00,0x00,0x65,0x31,0x00,0x00, + 0x05,0x00,0x03,0x00,0x69,0x01,0x00,0x00,0x65,0x32,0x00,0x00,0x05,0x00,0x03,0x00, + 0x6D,0x01,0x00,0x00,0x76,0x30,0x00,0x00,0x05,0x00,0x03,0x00,0x71,0x01,0x00,0x00, + 0x76,0x31,0x00,0x00,0x05,0x00,0x03,0x00,0x75,0x01,0x00,0x00,0x76,0x32,0x00,0x00, + 0x05,0x00,0x03,0x00,0x79,0x01,0x00,0x00,0x70,0x71,0x30,0x00,0x05,0x00,0x04,0x00, + 0x82,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x83,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x88,0x01,0x00,0x00,0x70,0x71,0x31,0x00,0x05,0x00,0x04,0x00,0x91,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x92,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x97,0x01,0x00,0x00, + 0x70,0x71,0x32,0x00,0x05,0x00,0x04,0x00,0xA0,0x01,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xA1,0x01,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xA6,0x01,0x00,0x00,0x73,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xA7,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xA9,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0xAC,0x01,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xB1,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xB3,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xBC,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xBE,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xC8,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xCA,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0xDA,0x01,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xE5,0x01,0x00,0x00, + 0x73,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xE6,0x01,0x00,0x00,0x69,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0xE7,0x01,0x00,0x00,0x6A,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0xF3,0x01,0x00,0x00,0x65,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xFB,0x01,0x00,0x00, + 0x77,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x01,0x02,0x00,0x00,0x62,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x15,0x02,0x00,0x00,0x63,0x6F,0x6E,0x64,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x57,0x02,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x59,0x02,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x5F,0x75,0x76, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x5C,0x02,0x00,0x00,0x76,0x5F,0x75,0x76, + 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x5F,0x02,0x00,0x00,0x76,0x5F,0x72,0x61, + 0x64,0x69,0x75,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x60,0x02,0x00,0x00, + 0x76,0x5F,0x73,0x68,0x61,0x70,0x65,0x00,0x05,0x00,0x04,0x00,0x69,0x02,0x00,0x00, + 0x76,0x5F,0x74,0x79,0x70,0x65,0x00,0x00,0x05,0x00,0x04,0x00,0x6C,0x02,0x00,0x00, + 0x76,0x5F,0x61,0x6C,0x70,0x68,0x61,0x00,0x05,0x00,0x06,0x00,0x6D,0x02,0x00,0x00, + 0x76,0x5F,0x62,0x6C,0x65,0x6E,0x64,0x5F,0x70,0x6F,0x73,0x48,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x72,0x02,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x48,0x00,0x00, + 0x05,0x00,0x05,0x00,0x76,0x02,0x00,0x00,0x69,0x73,0x5F,0x73,0x70,0x72,0x69,0x74, + 0x65,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x7C,0x02,0x00,0x00,0x69,0x73,0x5F,0x74, + 0x65,0x78,0x74,0x00,0x05,0x00,0x04,0x00,0x83,0x02,0x00,0x00,0x69,0x73,0x5F,0x62, + 0x6F,0x78,0x00,0x00,0x05,0x00,0x04,0x00,0x8A,0x02,0x00,0x00,0x69,0x73,0x5F,0x73, + 0x65,0x67,0x00,0x00,0x05,0x00,0x04,0x00,0x91,0x02,0x00,0x00,0x69,0x73,0x5F,0x74, + 0x72,0x69,0x00,0x00,0x05,0x00,0x05,0x00,0x98,0x02,0x00,0x00,0x69,0x73,0x5F,0x74, + 0x72,0x69,0x5F,0x73,0x64,0x66,0x00,0x00,0x05,0x00,0x04,0x00,0x9F,0x02,0x00,0x00, + 0x69,0x73,0x5F,0x70,0x6F,0x6C,0x79,0x00,0x05,0x00,0x03,0x00,0xA6,0x02,0x00,0x00, + 0x63,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xA8,0x02,0x00,0x00,0x75,0x76,0x00,0x00, + 0x05,0x00,0x06,0x00,0xA9,0x02,0x00,0x00,0x75,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x5F, + 0x62,0x6C,0x6F,0x63,0x6B,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0xA9,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x75,0x5F,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5F,0x73,0x69, + 0x7A,0x65,0x00,0x00,0x06,0x00,0x07,0x00,0xA9,0x02,0x00,0x00,0x01,0x00,0x00,0x00, + 0x75,0x5F,0x61,0x6C,0x70,0x68,0x61,0x5F,0x64,0x69,0x73,0x63,0x61,0x72,0x64,0x00, + 0x06,0x00,0x07,0x00,0xA9,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x75,0x5F,0x75,0x73, + 0x65,0x5F,0x73,0x6D,0x6F,0x6F,0x74,0x68,0x5F,0x75,0x76,0x00,0x05,0x00,0x03,0x00, + 0xAB,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xB4,0x02,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xB6,0x02,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xBE,0x02,0x00,0x00, + 0x74,0x65,0x78,0x5F,0x63,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xC2,0x02,0x00,0x00, + 0x75,0x5F,0x69,0x6D,0x61,0x67,0x65,0x00,0x05,0x00,0x04,0x00,0xC6,0x02,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xCC,0x02,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xD6,0x02,0x00,0x00, + 0x76,0x5F,0x63,0x6F,0x6C,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xE3,0x02,0x00,0x00, + 0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xE7,0x02,0x00,0x00,0x76,0x5F,0x61,0x62, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xE8,0x02,0x00,0x00,0x76,0x5F,0x63,0x64, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xE9,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xEB,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xEE,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xF1,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xF9,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xFB,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xFE,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x03,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x05,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x08,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x11,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x13,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x16,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x19,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x22,0x03,0x00,0x00,0x70,0x74,0x73,0x00, + 0x05,0x00,0x04,0x00,0x31,0x03,0x00,0x00,0x76,0x5F,0x65,0x66,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x39,0x03,0x00,0x00,0x76,0x5F,0x67,0x68,0x00,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x42,0x03,0x00,0x00,0x76,0x5F,0x6E,0x00,0x05,0x00,0x04,0x00, + 0x43,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x45,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x47,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x58,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x5A,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x5C,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x64,0x03,0x00,0x00,0x70,0x6F,0x73,0x00,0x05,0x00,0x05,0x00,0x66,0x03,0x00,0x00, + 0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75,0x76,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x6C,0x03,0x00,0x00,0x73,0x70,0x00,0x00,0x05,0x00,0x05,0x00,0x71,0x03,0x00,0x00, + 0x76,0x5F,0x75,0x76,0x5F,0x62,0x6F,0x75,0x6E,0x64,0x73,0x00,0x05,0x00,0x04,0x00, + 0x7A,0x03,0x00,0x00,0x76,0x5F,0x75,0x73,0x65,0x72,0x00,0x00,0x05,0x00,0x04,0x00, + 0x7D,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x7F,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x0B,0x01,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x59,0x02,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x60,0x02,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x6D,0x02,0x00,0x00,0x1E,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0xA9,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0xA9,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0xA9,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0xA9,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x0C,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xAB,0x02,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xAB,0x02,0x00,0x00,0x22,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xC2,0x02,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xC2,0x02,0x00,0x00,0x22,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xD6,0x02,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xE7,0x02,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xE8,0x02,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x31,0x03,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x39,0x03,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x42,0x03,0x00,0x00,0x0E,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x42,0x03,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x71,0x03,0x00,0x00,0x1E,0x00,0x00,0x00,0x0A,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x7A,0x03,0x00,0x00,0x1E,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x21,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x21,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x21,0x00,0x05,0x00,0x18,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x21,0x00,0x04,0x00,0x1D,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x04,0x00,0x21,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x05,0x00,0x25,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x04,0x00, + 0x2A,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x21,0x00,0x06,0x00, + 0x2E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x21,0x00,0x07,0x00,0x38,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x21,0x00,0x06,0x00,0x3F,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x4B,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4B,0x00,0x00,0x00, + 0x4C,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x1C,0x00,0x04,0x00,0x4D,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x4E,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x4D,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x50,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x21,0x00,0x06,0x00,0x51,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x4E,0x00,0x00,0x00,0x50,0x00,0x00,0x00, + 0x1E,0x00,0x08,0x00,0x57,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x58,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x57,0x00,0x00,0x00, + 0x21,0x00,0x05,0x00,0x59,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x5E,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x62,0x00,0x00,0x00, + 0x2F,0xBA,0xE8,0x3E,0x2C,0x00,0x06,0x00,0x5E,0x00,0x00,0x00,0x63,0x00,0x00,0x00, + 0x62,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4B,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0xCD,0xCC,0x0C,0x40,0x2C,0x00,0x06,0x00, + 0x5E,0x00,0x00,0x00,0x72,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x71,0x00,0x00,0x00, + 0x71,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x82,0x00,0x00,0x00, + 0x00,0x00,0x00,0x3F,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x8D,0x00,0x00,0x00, + 0x00,0x00,0x00,0xBF,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x14,0x00,0x02,0x00,0x99,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4B,0x00,0x00,0x00,0xB4,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4B,0x00,0x00,0x00,0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0xCE,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0xDA,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0xE5,0x00,0x00,0x00,0x99,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xF2,0x00,0x00,0x00,0x00,0x00,0x80,0xBF,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xF3,0x00,0x00,0x00,0x00,0x00,0x80,0x3F,0x20,0x00,0x04,0x00, + 0x0A,0x01,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x0A,0x01,0x00,0x00,0x0B,0x01,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0x0E,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x18,0x00,0x04,0x00, + 0x27,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x28,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x27,0x01,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xDC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xE9,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x13,0x02,0x00,0x00,0x99,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x14,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x13,0x02,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x46,0x02,0x00,0x00,0x05,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x56,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x56,0x02,0x00,0x00,0x57,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x58,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x58,0x02,0x00,0x00,0x59,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x56,0x02,0x00,0x00,0x5C,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0x5F,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x58,0x02,0x00,0x00,0x60,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x61,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4B,0x00,0x00,0x00,0x66,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0x69,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0x6C,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x58,0x02,0x00,0x00,0x6D,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x56,0x02,0x00,0x00,0x72,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x75,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x99,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x80,0x02,0x00,0x00,0x00,0x00,0xC0,0x3F,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x87,0x02,0x00,0x00,0x00,0x00,0x20,0x40,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x8E,0x02,0x00,0x00,0x00,0x00,0x60,0x40,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x95,0x02,0x00,0x00,0x00,0x00,0x90,0x40,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x9C,0x02,0x00,0x00,0x00,0x00,0xB0,0x40,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xA3,0x02,0x00,0x00,0x00,0x00,0xD0,0x40,0x2C,0x00,0x07,0x00, + 0x07,0x00,0x00,0x00,0xA7,0x02,0x00,0x00,0x98,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x98,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x1E,0x00,0x05,0x00,0xA9,0x02,0x00,0x00, + 0x10,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0xAA,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0xA9,0x02,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xAA,0x02,0x00,0x00,0xAB,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xAC,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0xAD,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0xB7,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x19,0x00,0x09,0x00, + 0xBF,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x1B,0x00,0x03,0x00,0xC0,0x02,0x00,0x00,0xBF,0x02,0x00,0x00,0x20,0x00,0x04,0x00, + 0xC1,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x02,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xC1,0x02,0x00,0x00,0xC2,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x58,0x02,0x00,0x00,0xD6,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x58,0x02,0x00,0x00,0xE7,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x58,0x02,0x00,0x00,0xE8,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x21,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x4D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x21,0x03,0x00,0x00,0x22,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x2C,0x03,0x00,0x00,0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x30,0x03,0x00,0x00,0x04,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x58,0x02,0x00,0x00,0x31,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x38,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x58,0x02,0x00,0x00,0x39,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x3D,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x41,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x41,0x03,0x00,0x00,0x42,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x56,0x02,0x00,0x00,0x64,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x56,0x02,0x00,0x00,0x66,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x2C,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x68,0x03,0x00,0x00,0xF3,0x00,0x00,0x00,0xF2,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x58,0x02,0x00,0x00,0x71,0x03,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x58,0x02,0x00,0x00,0x7A,0x03,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x75,0x02,0x00,0x00,0x76,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x75,0x02,0x00,0x00,0x7C,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x75,0x02,0x00,0x00,0x83,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x75,0x02,0x00,0x00,0x8A,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x75,0x02,0x00,0x00,0x91,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x75,0x02,0x00,0x00,0x98,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x75,0x02,0x00,0x00,0x9F,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0xA6,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xA8,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xB1,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xB4,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xB6,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0xBE,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0xC6,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0xC9,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0xCC,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0xD3,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x17,0x00,0x00,0x00,0xE3,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xE9,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xEB,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xEE,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xF1,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xF9,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xFB,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xFE,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x05,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x08,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x11,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x13,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x16,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x19,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x43,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x4E,0x00,0x00,0x00,0x45,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x50,0x00,0x00,0x00,0x47,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x52,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x58,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x5A,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x17,0x00,0x00,0x00,0x5C,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x58,0x00,0x00,0x00,0x6C,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x7D,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x58,0x00,0x00,0x00,0x7F,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x5A,0x02,0x00,0x00,0x59,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x5B,0x02,0x00,0x00,0x5A,0x02,0x00,0x00,0x5A,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x57,0x02,0x00,0x00, + 0x5B,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x5D,0x02,0x00,0x00, + 0x59,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x5E,0x02,0x00,0x00, + 0x5D,0x02,0x00,0x00,0x5D,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x5C,0x02,0x00,0x00,0x5E,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x61,0x02,0x00,0x00,0x62,0x02,0x00,0x00,0x60,0x02,0x00,0x00,0xB8,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x63,0x02,0x00,0x00,0x62,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x5F,0x02,0x00,0x00,0x63,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x61,0x02,0x00,0x00,0x64,0x02,0x00,0x00,0x60,0x02,0x00,0x00,0xB4,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x65,0x02,0x00,0x00,0x64,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xCE,0x00,0x00,0x00,0x65,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x61,0x02,0x00,0x00,0x67,0x02,0x00,0x00,0x60,0x02,0x00,0x00,0x66,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x68,0x02,0x00,0x00,0x67,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xDA,0x00,0x00,0x00,0x68,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x61,0x02,0x00,0x00,0x6A,0x02,0x00,0x00,0x60,0x02,0x00,0x00,0x65,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x6B,0x02,0x00,0x00,0x6A,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x69,0x02,0x00,0x00,0x6B,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x61,0x02,0x00,0x00,0x6E,0x02,0x00,0x00,0x6D,0x02,0x00,0x00,0xB8,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x6F,0x02,0x00,0x00,0x6E,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x6C,0x02,0x00,0x00,0x6F,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x61,0x02,0x00,0x00,0x70,0x02,0x00,0x00,0x6D,0x02,0x00,0x00,0xB4,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x71,0x02,0x00,0x00,0x70,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x0E,0x01,0x00,0x00,0x71,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x73,0x02,0x00,0x00,0x6D,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x74,0x02,0x00,0x00,0x73,0x02,0x00,0x00,0x73,0x02,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x72,0x02,0x00,0x00, + 0x74,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x77,0x02,0x00,0x00, + 0x69,0x02,0x00,0x00,0xBE,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x78,0x02,0x00,0x00, + 0x77,0x02,0x00,0x00,0x98,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x79,0x02,0x00,0x00,0x69,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0x7A,0x02,0x00,0x00,0x79,0x02,0x00,0x00,0x82,0x00,0x00,0x00,0xA7,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x7B,0x02,0x00,0x00,0x78,0x02,0x00,0x00,0x7A,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x76,0x02,0x00,0x00,0x7B,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x7D,0x02,0x00,0x00,0x69,0x02,0x00,0x00,0xBA,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x7E,0x02,0x00,0x00,0x7D,0x02,0x00,0x00,0x82,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x7F,0x02,0x00,0x00,0x69,0x02,0x00,0x00, + 0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x81,0x02,0x00,0x00,0x7F,0x02,0x00,0x00, + 0x80,0x02,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x82,0x02,0x00,0x00, + 0x7E,0x02,0x00,0x00,0x81,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x7C,0x02,0x00,0x00, + 0x82,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x84,0x02,0x00,0x00, + 0x69,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x85,0x02,0x00,0x00, + 0x84,0x02,0x00,0x00,0x80,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x86,0x02,0x00,0x00,0x69,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0x88,0x02,0x00,0x00,0x86,0x02,0x00,0x00,0x87,0x02,0x00,0x00,0xA7,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x89,0x02,0x00,0x00,0x85,0x02,0x00,0x00,0x88,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x83,0x02,0x00,0x00,0x89,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x8B,0x02,0x00,0x00,0x69,0x02,0x00,0x00,0xBA,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x8C,0x02,0x00,0x00,0x8B,0x02,0x00,0x00,0x87,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x8D,0x02,0x00,0x00,0x69,0x02,0x00,0x00, + 0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x8F,0x02,0x00,0x00,0x8D,0x02,0x00,0x00, + 0x8E,0x02,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x90,0x02,0x00,0x00, + 0x8C,0x02,0x00,0x00,0x8F,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x8A,0x02,0x00,0x00, + 0x90,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x92,0x02,0x00,0x00, + 0x69,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x93,0x02,0x00,0x00, + 0x92,0x02,0x00,0x00,0x8E,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x94,0x02,0x00,0x00,0x69,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0x96,0x02,0x00,0x00,0x94,0x02,0x00,0x00,0x95,0x02,0x00,0x00,0xA7,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x97,0x02,0x00,0x00,0x93,0x02,0x00,0x00,0x96,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x91,0x02,0x00,0x00,0x97,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x99,0x02,0x00,0x00,0x69,0x02,0x00,0x00,0xBA,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x9A,0x02,0x00,0x00,0x99,0x02,0x00,0x00,0x95,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x9B,0x02,0x00,0x00,0x69,0x02,0x00,0x00, + 0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x9D,0x02,0x00,0x00,0x9B,0x02,0x00,0x00, + 0x9C,0x02,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x9E,0x02,0x00,0x00, + 0x9A,0x02,0x00,0x00,0x9D,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x98,0x02,0x00,0x00, + 0x9E,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xA0,0x02,0x00,0x00, + 0x69,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xA1,0x02,0x00,0x00, + 0xA0,0x02,0x00,0x00,0x9C,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xA2,0x02,0x00,0x00,0x69,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0xA4,0x02,0x00,0x00,0xA2,0x02,0x00,0x00,0xA3,0x02,0x00,0x00,0xA7,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0xA5,0x02,0x00,0x00,0xA1,0x02,0x00,0x00,0xA4,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x9F,0x02,0x00,0x00,0xA5,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xA6,0x02,0x00,0x00,0xA7,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0xAD,0x02,0x00,0x00, + 0xAE,0x02,0x00,0x00,0xAB,0x02,0x00,0x00,0xAC,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xAF,0x02,0x00,0x00,0xAE,0x02,0x00,0x00,0xAA,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0xB0,0x02,0x00,0x00,0xAF,0x02,0x00,0x00,0xDC,0x01,0x00,0x00, + 0xF7,0x00,0x03,0x00,0xB3,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0xB0,0x02,0x00,0x00,0xB2,0x02,0x00,0x00,0xBB,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xB2,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xB5,0x02,0x00,0x00, + 0x5C,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xB4,0x02,0x00,0x00,0xB5,0x02,0x00,0x00, + 0x41,0x00,0x05,0x00,0xB7,0x02,0x00,0x00,0xB8,0x02,0x00,0x00,0xAB,0x02,0x00,0x00, + 0xDC,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xB9,0x02,0x00,0x00, + 0xB8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xB6,0x02,0x00,0x00,0xB9,0x02,0x00,0x00, + 0x39,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0xBA,0x02,0x00,0x00,0x15,0x00,0x00,0x00, + 0xB4,0x02,0x00,0x00,0xB6,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xB1,0x02,0x00,0x00, + 0xBA,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xB3,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xBB,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xBC,0x02,0x00,0x00, + 0x5C,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xB1,0x02,0x00,0x00,0xBC,0x02,0x00,0x00, + 0xF9,0x00,0x02,0x00,0xB3,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xB3,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xBD,0x02,0x00,0x00,0xB1,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xA8,0x02,0x00,0x00,0xBD,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0xC0,0x02,0x00,0x00,0xC3,0x02,0x00,0x00,0xC2,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xC4,0x02,0x00,0x00,0xA8,0x02,0x00,0x00,0x57,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0xC5,0x02,0x00,0x00,0xC3,0x02,0x00,0x00,0xC4,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xC6,0x02,0x00,0x00,0xC5,0x02,0x00,0x00,0x39,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0xC7,0x02,0x00,0x00,0x0E,0x00,0x00,0x00,0xC6,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xBE,0x02,0x00,0x00,0xC7,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x99,0x00,0x00,0x00,0xC8,0x02,0x00,0x00,0x76,0x02,0x00,0x00,0xF7,0x00,0x03,0x00, + 0xCB,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0xC8,0x02,0x00,0x00, + 0xCA,0x02,0x00,0x00,0xCF,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xCA,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xCD,0x02,0x00,0x00,0xBE,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xCC,0x02,0x00,0x00,0xCD,0x02,0x00,0x00,0x39,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0xCE,0x02,0x00,0x00,0x0B,0x00,0x00,0x00,0xCC,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xC9,0x02,0x00,0x00,0xCE,0x02,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xCB,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xCF,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xD0,0x02,0x00,0x00,0xA6,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xC9,0x02,0x00,0x00,0xD0,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xCB,0x02,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xCB,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xD1,0x02,0x00,0x00,0xC9,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xA6,0x02,0x00,0x00, + 0xD1,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0xD2,0x02,0x00,0x00, + 0x7C,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0xD5,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFA,0x00,0x04,0x00,0xD2,0x02,0x00,0x00,0xD4,0x02,0x00,0x00,0xDB,0x02,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xD4,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xD7,0x02,0x00,0x00,0xD6,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0xD8,0x02,0x00,0x00,0xBE,0x02,0x00,0x00,0x65,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xD9,0x02,0x00,0x00,0xD8,0x02,0x00,0x00,0x8E,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0xDA,0x02,0x00,0x00,0xD7,0x02,0x00,0x00,0xD9,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xD3,0x02,0x00,0x00,0xDA,0x02,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xD5,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xDB,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xDC,0x02,0x00,0x00,0xA6,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xD3,0x02,0x00,0x00,0xDC,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xD5,0x02,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xD5,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xDD,0x02,0x00,0x00,0xD3,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xA6,0x02,0x00,0x00, + 0xDD,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0xDE,0x02,0x00,0x00, + 0x91,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xDF,0x02,0x00,0x00, + 0xD6,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xE0,0x02,0x00,0x00, + 0xA6,0x02,0x00,0x00,0x50,0x00,0x07,0x00,0xE5,0x00,0x00,0x00,0xE1,0x02,0x00,0x00, + 0xDE,0x02,0x00,0x00,0xDE,0x02,0x00,0x00,0xDE,0x02,0x00,0x00,0xDE,0x02,0x00,0x00, + 0xA9,0x00,0x06,0x00,0x07,0x00,0x00,0x00,0xE2,0x02,0x00,0x00,0xE1,0x02,0x00,0x00, + 0xDF,0x02,0x00,0x00,0xE0,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xA6,0x02,0x00,0x00, + 0xE2,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xE3,0x02,0x00,0x00,0x98,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0xE4,0x02,0x00,0x00,0x83,0x02,0x00,0x00, + 0xF7,0x00,0x03,0x00,0xE6,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0xE4,0x02,0x00,0x00,0xE5,0x02,0x00,0x00,0xF5,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xE5,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xEA,0x02,0x00,0x00, + 0x57,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xE9,0x02,0x00,0x00,0xEA,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xEC,0x02,0x00,0x00,0xE7,0x02,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xED,0x02,0x00,0x00,0xEC,0x02,0x00,0x00, + 0xEC,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xEB,0x02,0x00,0x00,0xED,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xEF,0x02,0x00,0x00,0xE7,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0xF0,0x02,0x00,0x00,0xEF,0x02,0x00,0x00,0xEF,0x02,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xEE,0x02,0x00,0x00,0xF0,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xF2,0x02,0x00,0x00,0xE8,0x02,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xF3,0x02,0x00,0x00,0xF2,0x02,0x00,0x00, + 0xF2,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xF1,0x02,0x00,0x00,0xF3,0x02,0x00,0x00,0x39,0x00,0x08,0x00,0x06,0x00,0x00,0x00, + 0xF4,0x02,0x00,0x00,0x3D,0x00,0x00,0x00,0xE9,0x02,0x00,0x00,0xEB,0x02,0x00,0x00, + 0xEE,0x02,0x00,0x00,0xF1,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xE3,0x02,0x00,0x00, + 0xF4,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xE6,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xF5,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0xF6,0x02,0x00,0x00, + 0x8A,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0xF8,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFA,0x00,0x04,0x00,0xF6,0x02,0x00,0x00,0xF7,0x02,0x00,0x00,0x0D,0x03,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xF7,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xFA,0x02,0x00,0x00,0x57,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xF9,0x02,0x00,0x00, + 0xFA,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xFC,0x02,0x00,0x00, + 0xE7,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xFD,0x02,0x00,0x00, + 0xFC,0x02,0x00,0x00,0xFC,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xFB,0x02,0x00,0x00,0xFD,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xFF,0x02,0x00,0x00,0xE7,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0xFF,0x02,0x00,0x00,0xFF,0x02,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xFE,0x02,0x00,0x00, + 0x00,0x03,0x00,0x00,0x39,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0x01,0x03,0x00,0x00, + 0x43,0x00,0x00,0x00,0xF9,0x02,0x00,0x00,0xFB,0x02,0x00,0x00,0xFE,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xE3,0x02,0x00,0x00,0x01,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x02,0x03,0x00,0x00,0xE3,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x04,0x03,0x00,0x00,0x57,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x03,0x03,0x00,0x00,0x04,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x06,0x03,0x00,0x00,0xE7,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x07,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x05,0x03,0x00,0x00,0x07,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x09,0x03,0x00,0x00,0xE8,0x02,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x0A,0x03,0x00,0x00,0x09,0x03,0x00,0x00, + 0x09,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x08,0x03,0x00,0x00,0x0A,0x03,0x00,0x00,0x39,0x00,0x07,0x00,0x06,0x00,0x00,0x00, + 0x0B,0x03,0x00,0x00,0x43,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x05,0x03,0x00,0x00, + 0x08,0x03,0x00,0x00,0x0C,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0x0C,0x03,0x00,0x00, + 0x01,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x02,0x03,0x00,0x00,0x0B,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xE3,0x02,0x00,0x00,0x0C,0x03,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xF8,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0x0D,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x99,0x00,0x00,0x00,0x0E,0x03,0x00,0x00,0x98,0x02,0x00,0x00,0xF7,0x00,0x03,0x00, + 0x10,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x0E,0x03,0x00,0x00, + 0x0F,0x03,0x00,0x00,0x1D,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x0F,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x12,0x03,0x00,0x00,0x57,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x11,0x03,0x00,0x00,0x12,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x14,0x03,0x00,0x00,0xE7,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x15,0x03,0x00,0x00,0x14,0x03,0x00,0x00,0x14,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x13,0x03,0x00,0x00, + 0x15,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x17,0x03,0x00,0x00, + 0xE7,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x18,0x03,0x00,0x00, + 0x17,0x03,0x00,0x00,0x17,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x16,0x03,0x00,0x00,0x18,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x1A,0x03,0x00,0x00,0xE8,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x1B,0x03,0x00,0x00,0x1A,0x03,0x00,0x00,0x1A,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x19,0x03,0x00,0x00, + 0x1B,0x03,0x00,0x00,0x39,0x00,0x08,0x00,0x06,0x00,0x00,0x00,0x1C,0x03,0x00,0x00, + 0x49,0x00,0x00,0x00,0x11,0x03,0x00,0x00,0x13,0x03,0x00,0x00,0x16,0x03,0x00,0x00, + 0x19,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0xE3,0x02,0x00,0x00,0x1C,0x03,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x10,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x1D,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x1E,0x03,0x00,0x00,0x9F,0x02,0x00,0x00, + 0xF7,0x00,0x03,0x00,0x20,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0x1E,0x03,0x00,0x00,0x1F,0x03,0x00,0x00,0x20,0x03,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x1F,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x23,0x03,0x00,0x00, + 0xE7,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x24,0x03,0x00,0x00, + 0x23,0x03,0x00,0x00,0x23,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x56,0x02,0x00,0x00,0x25,0x03,0x00,0x00,0x22,0x03,0x00,0x00, + 0xDC,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x25,0x03,0x00,0x00,0x24,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x26,0x03,0x00,0x00,0xE7,0x02,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x27,0x03,0x00,0x00,0x26,0x03,0x00,0x00, + 0x26,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x56,0x02,0x00,0x00,0x28,0x03,0x00,0x00,0x22,0x03,0x00,0x00,0xE9,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x28,0x03,0x00,0x00,0x27,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x29,0x03,0x00,0x00,0xE8,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x2A,0x03,0x00,0x00,0x29,0x03,0x00,0x00,0x29,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x56,0x02,0x00,0x00, + 0x2B,0x03,0x00,0x00,0x22,0x03,0x00,0x00,0xAC,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x2B,0x03,0x00,0x00,0x2A,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x2D,0x03,0x00,0x00,0xE8,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x2E,0x03,0x00,0x00,0x2D,0x03,0x00,0x00,0x2D,0x03,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x56,0x02,0x00,0x00,0x2F,0x03,0x00,0x00, + 0x22,0x03,0x00,0x00,0x2C,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x2F,0x03,0x00,0x00, + 0x2E,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x32,0x03,0x00,0x00, + 0x31,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x33,0x03,0x00,0x00, + 0x32,0x03,0x00,0x00,0x32,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x56,0x02,0x00,0x00,0x34,0x03,0x00,0x00,0x22,0x03,0x00,0x00, + 0x30,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x34,0x03,0x00,0x00,0x33,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x35,0x03,0x00,0x00,0x31,0x03,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x36,0x03,0x00,0x00,0x35,0x03,0x00,0x00, + 0x35,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x56,0x02,0x00,0x00,0x37,0x03,0x00,0x00,0x22,0x03,0x00,0x00,0x46,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x37,0x03,0x00,0x00,0x36,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x3A,0x03,0x00,0x00,0x39,0x03,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x3B,0x03,0x00,0x00,0x3A,0x03,0x00,0x00,0x3A,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x56,0x02,0x00,0x00, + 0x3C,0x03,0x00,0x00,0x22,0x03,0x00,0x00,0x38,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x3C,0x03,0x00,0x00,0x3B,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x3E,0x03,0x00,0x00,0x39,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x3F,0x03,0x00,0x00,0x3E,0x03,0x00,0x00,0x3E,0x03,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x56,0x02,0x00,0x00,0x40,0x03,0x00,0x00, + 0x22,0x03,0x00,0x00,0x3D,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x40,0x03,0x00,0x00, + 0x3F,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x44,0x03,0x00,0x00, + 0x57,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x43,0x03,0x00,0x00,0x44,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x4D,0x00,0x00,0x00,0x46,0x03,0x00,0x00,0x22,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x45,0x03,0x00,0x00,0x46,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x48,0x03,0x00,0x00,0x42,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x47,0x03,0x00,0x00,0x48,0x03,0x00,0x00,0x39,0x00,0x07,0x00,0x06,0x00,0x00,0x00, + 0x49,0x03,0x00,0x00,0x55,0x00,0x00,0x00,0x43,0x03,0x00,0x00,0x45,0x03,0x00,0x00, + 0x47,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0xE3,0x02,0x00,0x00,0x49,0x03,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x20,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x20,0x03,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x10,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x10,0x03,0x00,0x00, + 0xF9,0x00,0x02,0x00,0xF8,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xF8,0x02,0x00,0x00, + 0xF9,0x00,0x02,0x00,0xE6,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xE6,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x4A,0x03,0x00,0x00,0x76,0x02,0x00,0x00, + 0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x4B,0x03,0x00,0x00,0x4A,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x4C,0x03,0x00,0x00,0x7C,0x02,0x00,0x00, + 0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x4D,0x03,0x00,0x00,0x4C,0x03,0x00,0x00, + 0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x4E,0x03,0x00,0x00,0x4B,0x03,0x00,0x00, + 0x4D,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x4F,0x03,0x00,0x00, + 0x91,0x02,0x00,0x00,0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x50,0x03,0x00,0x00, + 0x4F,0x03,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x51,0x03,0x00,0x00, + 0x4E,0x03,0x00,0x00,0x50,0x03,0x00,0x00,0xF7,0x00,0x03,0x00,0x54,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x51,0x03,0x00,0x00,0x53,0x03,0x00,0x00, + 0x5E,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x53,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x55,0x03,0x00,0x00,0xE3,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x56,0x03,0x00,0x00,0x5F,0x02,0x00,0x00,0x83,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x57,0x03,0x00,0x00,0x55,0x03,0x00,0x00,0x56,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x59,0x03,0x00,0x00,0xA6,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x58,0x03,0x00,0x00,0x59,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x5B,0x03,0x00,0x00,0xD6,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x5A,0x03,0x00,0x00,0x5B,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x5C,0x03,0x00,0x00, + 0x57,0x03,0x00,0x00,0x39,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x5D,0x03,0x00,0x00, + 0x32,0x00,0x00,0x00,0x58,0x03,0x00,0x00,0x5A,0x03,0x00,0x00,0x5C,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x52,0x03,0x00,0x00,0x5D,0x03,0x00,0x00,0xF9,0x00,0x02,0x00, + 0x54,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x5E,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x5F,0x03,0x00,0x00,0xA6,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x52,0x03,0x00,0x00,0x5F,0x03,0x00,0x00,0xF9,0x00,0x02,0x00,0x54,0x03,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x54,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x60,0x03,0x00,0x00,0x52,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0xA6,0x02,0x00,0x00, + 0x60,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x61,0x03,0x00,0x00, + 0x6C,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x62,0x03,0x00,0x00, + 0xA6,0x02,0x00,0x00,0x8E,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x63,0x03,0x00,0x00, + 0x62,0x03,0x00,0x00,0x61,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0xA6,0x02,0x00,0x00, + 0x63,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x65,0x03,0x00,0x00, + 0x57,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x64,0x03,0x00,0x00,0x65,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x67,0x03,0x00,0x00,0x72,0x02,0x00,0x00, + 0x81,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x69,0x03,0x00,0x00,0x67,0x03,0x00,0x00, + 0x68,0x03,0x00,0x00,0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x6A,0x03,0x00,0x00, + 0x69,0x03,0x00,0x00,0x82,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x6B,0x03,0x00,0x00,0x6A,0x03,0x00,0x00,0x68,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x66,0x03,0x00,0x00,0x6B,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x6D,0x03,0x00,0x00,0x64,0x03,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00, + 0x6E,0x03,0x00,0x00,0x6C,0x03,0x00,0x00,0xDC,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x6E,0x03,0x00,0x00,0x6D,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x6F,0x03,0x00,0x00,0x5C,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00, + 0x70,0x03,0x00,0x00,0x6C,0x03,0x00,0x00,0xE9,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x70,0x03,0x00,0x00,0x6F,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x72,0x03,0x00,0x00,0x71,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x73,0x03,0x00,0x00,0x72,0x03,0x00,0x00,0x72,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x74,0x03,0x00,0x00, + 0x6C,0x03,0x00,0x00,0xAC,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x74,0x03,0x00,0x00, + 0x73,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x75,0x03,0x00,0x00, + 0x71,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x76,0x03,0x00,0x00, + 0x75,0x03,0x00,0x00,0x75,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x77,0x03,0x00,0x00,0x6C,0x03,0x00,0x00, + 0x2C,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x77,0x03,0x00,0x00,0x76,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x78,0x03,0x00,0x00,0x66,0x03,0x00,0x00, + 0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x79,0x03,0x00,0x00,0x6C,0x03,0x00,0x00, + 0x30,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x79,0x03,0x00,0x00,0x78,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x7B,0x03,0x00,0x00,0x7A,0x03,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x7C,0x03,0x00,0x00,0x6C,0x03,0x00,0x00, + 0x46,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x7C,0x03,0x00,0x00,0x7B,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x7E,0x03,0x00,0x00,0xA6,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x7D,0x03,0x00,0x00,0x7E,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x57,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x6C,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x7F,0x03,0x00,0x00,0x80,0x03,0x00,0x00,0x39,0x00,0x06,0x00,0x07,0x00,0x00,0x00, + 0x81,0x03,0x00,0x00,0x5C,0x00,0x00,0x00,0x7D,0x03,0x00,0x00,0x7F,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xA6,0x02,0x00,0x00,0x81,0x03,0x00,0x00,0x41,0x00,0x05,0x00, + 0xAD,0x02,0x00,0x00,0x82,0x03,0x00,0x00,0xAB,0x02,0x00,0x00,0xE9,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x83,0x03,0x00,0x00,0x82,0x03,0x00,0x00, + 0xAB,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x84,0x03,0x00,0x00,0x83,0x03,0x00,0x00, + 0xDC,0x01,0x00,0x00,0xF7,0x00,0x03,0x00,0x86,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFA,0x00,0x04,0x00,0x84,0x03,0x00,0x00,0x85,0x03,0x00,0x00,0x86,0x03,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x85,0x03,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0x87,0x03,0x00,0x00,0xA6,0x02,0x00,0x00,0x65,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x88,0x03,0x00,0x00,0x87,0x03,0x00,0x00,0xB4,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x89,0x03,0x00,0x00,0x88,0x03,0x00,0x00,0x98,0x00,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x86,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x86,0x03,0x00,0x00, + 0xF5,0x00,0x07,0x00,0x99,0x00,0x00,0x00,0x8A,0x03,0x00,0x00,0x84,0x03,0x00,0x00, + 0x54,0x03,0x00,0x00,0x89,0x03,0x00,0x00,0x85,0x03,0x00,0x00,0xF7,0x00,0x03,0x00, + 0x8C,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x8A,0x03,0x00,0x00, + 0x8B,0x03,0x00,0x00,0x8C,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x8B,0x03,0x00,0x00, + 0xFC,0x00,0x01,0x00,0xF8,0x00,0x02,0x00,0x8C,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x8E,0x03,0x00,0x00,0xA6,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x0B,0x01,0x00,0x00,0x8E,0x03,0x00,0x00,0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00, + 0x36,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x0A,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x0C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x5F,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x4F,0x00,0x08,0x00,0x5E,0x00,0x00,0x00, + 0x60,0x00,0x00,0x00,0x5F,0x00,0x00,0x00,0x5F,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0C,0x00,0x06,0x00,0x5E,0x00,0x00,0x00, + 0x61,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x0C,0x00,0x07,0x00,0x5E,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x1A,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x65,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x66,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x64,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x69,0x00,0x00,0x00, + 0x64,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x6A,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0x07,0x00,0x00,0x00,0x6B,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x69,0x00,0x00,0x00, + 0x6A,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0x6B,0x00,0x00,0x00, + 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x0E,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00, + 0x0D,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x0F,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x6E,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x4F,0x00,0x08,0x00, + 0x5E,0x00,0x00,0x00,0x6F,0x00,0x00,0x00,0x6E,0x00,0x00,0x00,0x6E,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0C,0x00,0x06,0x00, + 0x5E,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x6F,0x00,0x00,0x00,0x0C,0x00,0x07,0x00,0x5E,0x00,0x00,0x00,0x73,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x72,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x74,0x00,0x00,0x00,0x0D,0x00,0x00,0x00, + 0x65,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x75,0x00,0x00,0x00, + 0x74,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x76,0x00,0x00,0x00, + 0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x77,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0x76,0x00,0x00,0x00, + 0x77,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x75,0x00,0x00,0x00,0xFE,0x00,0x02,0x00, + 0x79,0x00,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x11,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x14,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x16,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x7D,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x85,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x7D,0x00,0x00,0x00,0x7E,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x7C,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x81,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x50,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0x82,0x00,0x00,0x00,0x82,0x00,0x00,0x00, + 0x81,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x81,0x00,0x00,0x00, + 0x83,0x00,0x00,0x00,0x0C,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0x85,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x80,0x00,0x00,0x00,0x85,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x86,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x87,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x88,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x89,0x00,0x00,0x00,0x87,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x8A,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xD1,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x8B,0x00,0x00,0x00,0x8A,0x00,0x00,0x00,0x88,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x8C,0x00,0x00,0x00,0x89,0x00,0x00,0x00,0x8B,0x00,0x00,0x00, + 0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x8E,0x00,0x00,0x00,0x8D,0x00,0x00,0x00, + 0x8D,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x8F,0x00,0x00,0x00, + 0x82,0x00,0x00,0x00,0x82,0x00,0x00,0x00,0x0C,0x00,0x08,0x00,0x10,0x00,0x00,0x00, + 0x90,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x8C,0x00,0x00,0x00, + 0x8E,0x00,0x00,0x00,0x8F,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x91,0x00,0x00,0x00,0x86,0x00,0x00,0x00,0x90,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x7C,0x00,0x00,0x00,0x91,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x92,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x93,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x88,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x94,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x93,0x00,0x00,0x00,0xFE,0x00,0x02,0x00, + 0x94,0x00,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x1B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x17,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x17,0x00,0x00,0x00, + 0x1A,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x1C,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x17,0x00,0x00,0x00,0x9B,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x97,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0xB4,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x9A,0x00,0x00,0x00,0x97,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0xF7,0x00,0x03,0x00,0x9D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0x9A,0x00,0x00,0x00,0x9C,0x00,0x00,0x00,0x9E,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x9C,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x9B,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x9D,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x9E,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x9F,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xA0,0x00,0x00,0x00,0x1A,0x00,0x00,0x00, + 0x88,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xA1,0x00,0x00,0x00,0x9F,0x00,0x00,0x00, + 0xA0,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x9B,0x00,0x00,0x00,0xA1,0x00,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x9D,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x9D,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xA2,0x00,0x00,0x00,0x9B,0x00,0x00,0x00, + 0xFE,0x00,0x02,0x00,0xA2,0x00,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1D,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x20,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xA5,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xAB,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xA6,0x00,0x00,0x00, + 0x1E,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xA7,0x00,0x00,0x00, + 0x1E,0x00,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xA8,0x00,0x00,0x00, + 0xA6,0x00,0x00,0x00,0xA7,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xA5,0x00,0x00,0x00, + 0xA8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xA9,0x00,0x00,0x00, + 0xA5,0x00,0x00,0x00,0xB4,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xAA,0x00,0x00,0x00, + 0xA9,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0xF7,0x00,0x03,0x00,0xAD,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0xAA,0x00,0x00,0x00,0xAC,0x00,0x00,0x00, + 0xAE,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0xAC,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xAB,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0xF9,0x00,0x02,0x00,0xAD,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xAE,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xAF,0x00,0x00,0x00,0xA5,0x00,0x00,0x00,0x0C,0x00,0x06,0x00,0x06,0x00,0x00,0x00, + 0xB0,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0xAF,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xAB,0x00,0x00,0x00,0xB0,0x00,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xAD,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0xAD,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xB1,0x00,0x00,0x00,0xAB,0x00,0x00,0x00,0xFE,0x00,0x02,0x00, + 0xB1,0x00,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x11,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x24,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0xB5,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xB6,0x00,0x00,0x00, + 0xB5,0x00,0x00,0x00,0x7F,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xB7,0x00,0x00,0x00, + 0xB6,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0xB9,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xBA,0x00,0x00,0x00,0xB9,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0xBB,0x00,0x00,0x00,0xB7,0x00,0x00,0x00,0xBA,0x00,0x00,0x00,0xFE,0x00,0x02,0x00, + 0xBB,0x00,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x28,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x11,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x27,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x29,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0xBE,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0xB8,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xBF,0x00,0x00,0x00,0xBE,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x27,0x00,0x00,0x00, + 0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xC1,0x00,0x00,0x00, + 0xC0,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xC2,0x00,0x00,0x00, + 0xBF,0x00,0x00,0x00,0xC1,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0xC3,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xC4,0x00,0x00,0x00,0xC3,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0xC5,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0xB8,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xC6,0x00,0x00,0x00,0xC5,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xC7,0x00,0x00,0x00,0xC4,0x00,0x00,0x00, + 0xC6,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xC8,0x00,0x00,0x00, + 0xC2,0x00,0x00,0x00,0xC7,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0xC8,0x00,0x00,0x00, + 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x2C,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2A,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x17,0x00,0x00,0x00, + 0x2B,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x2D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xCB,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x0C,0x00,0x06,0x00, + 0x06,0x00,0x00,0x00,0xCC,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0xCB,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xCF,0x00,0x00,0x00, + 0xCE,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xD0,0x00,0x00,0x00, + 0xCC,0x00,0x00,0x00,0xCF,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0xD0,0x00,0x00,0x00, + 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x32,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00, + 0x2F,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x17,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x33,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xD3,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xD4,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xD7,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xE0,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xE8,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xF0,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xFA,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x02,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xD5,0x00,0x00,0x00, + 0x31,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xD4,0x00,0x00,0x00,0xD5,0x00,0x00,0x00, + 0x39,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xD6,0x00,0x00,0x00,0x2C,0x00,0x00,0x00, + 0xD4,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xD3,0x00,0x00,0x00,0xD6,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xD9,0x00,0x00,0x00,0x2F,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xDB,0x00,0x00,0x00,0xDA,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xDC,0x00,0x00,0x00,0xD3,0x00,0x00,0x00, + 0x0C,0x00,0x08,0x00,0x06,0x00,0x00,0x00,0xDD,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x31,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0xDB,0x00,0x00,0x00,0xDC,0x00,0x00,0x00, + 0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0xDE,0x00,0x00,0x00,0xDD,0x00,0x00,0x00, + 0xDD,0x00,0x00,0x00,0xDD,0x00,0x00,0x00,0xDD,0x00,0x00,0x00,0x0C,0x00,0x08,0x00, + 0x07,0x00,0x00,0x00,0xDF,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2E,0x00,0x00,0x00, + 0xD8,0x00,0x00,0x00,0xD9,0x00,0x00,0x00,0xDE,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xD7,0x00,0x00,0x00,0xDF,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xE1,0x00,0x00,0x00,0xD3,0x00,0x00,0x00,0xBC,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0xE2,0x00,0x00,0x00,0xE1,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xE3,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xE4,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0xE5,0x00,0x00,0x00,0xE6,0x00,0x00,0x00,0xE2,0x00,0x00,0x00,0xE2,0x00,0x00,0x00, + 0xE2,0x00,0x00,0x00,0xE2,0x00,0x00,0x00,0xA9,0x00,0x06,0x00,0x07,0x00,0x00,0x00, + 0xE7,0x00,0x00,0x00,0xE6,0x00,0x00,0x00,0xE3,0x00,0x00,0x00,0xE4,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xE0,0x00,0x00,0x00,0xE7,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xE9,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xEA,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xEB,0x00,0x00,0x00,0xDA,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xEC,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x0C,0x00,0x08,0x00, + 0x06,0x00,0x00,0x00,0xED,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x31,0x00,0x00,0x00, + 0x98,0x00,0x00,0x00,0xEB,0x00,0x00,0x00,0xEC,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0x07,0x00,0x00,0x00,0xEE,0x00,0x00,0x00,0xED,0x00,0x00,0x00,0xED,0x00,0x00,0x00, + 0xED,0x00,0x00,0x00,0xED,0x00,0x00,0x00,0x0C,0x00,0x08,0x00,0x07,0x00,0x00,0x00, + 0xEF,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0xE9,0x00,0x00,0x00, + 0xEA,0x00,0x00,0x00,0xEE,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xE8,0x00,0x00,0x00, + 0xEF,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xF1,0x00,0x00,0x00, + 0x31,0x00,0x00,0x00,0x0C,0x00,0x08,0x00,0x06,0x00,0x00,0x00,0xF4,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0xF1,0x00,0x00,0x00,0xF2,0x00,0x00,0x00, + 0xF3,0x00,0x00,0x00,0xBC,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xF5,0x00,0x00,0x00, + 0xF4,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xF6,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xF7,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0xE5,0x00,0x00,0x00, + 0xF8,0x00,0x00,0x00,0xF5,0x00,0x00,0x00,0xF5,0x00,0x00,0x00,0xF5,0x00,0x00,0x00, + 0xF5,0x00,0x00,0x00,0xA9,0x00,0x06,0x00,0x07,0x00,0x00,0x00,0xF9,0x00,0x00,0x00, + 0xF8,0x00,0x00,0x00,0xF6,0x00,0x00,0x00,0xF7,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xF0,0x00,0x00,0x00,0xF9,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xFB,0x00,0x00,0x00,0xD7,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xFC,0x00,0x00,0x00,0xD7,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xFD,0x00,0x00,0x00,0xDA,0x00,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0xFE,0x00,0x00,0x00,0xFD,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0xA9,0x00,0x06,0x00, + 0x06,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xF3,0x00,0x00,0x00, + 0x98,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x01,0x00,0x00, + 0xFF,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0xFF,0x00,0x00,0x00, + 0x0C,0x00,0x08,0x00,0x07,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2E,0x00,0x00,0x00,0xFB,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0x00,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xFA,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x03,0x01,0x00,0x00,0xF0,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x04,0x01,0x00,0x00,0xE8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x05,0x01,0x00,0x00,0xDA,0x00,0x00,0x00,0xBA,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x06,0x01,0x00,0x00,0x05,0x01,0x00,0x00,0x98,0x00,0x00,0x00, + 0xA9,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x07,0x01,0x00,0x00,0x06,0x01,0x00,0x00, + 0xF3,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00, + 0x08,0x01,0x00,0x00,0x07,0x01,0x00,0x00,0x07,0x01,0x00,0x00,0x07,0x01,0x00,0x00, + 0x07,0x01,0x00,0x00,0x0C,0x00,0x08,0x00,0x07,0x00,0x00,0x00,0x09,0x01,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0x03,0x01,0x00,0x00,0x04,0x01,0x00,0x00, + 0x08,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x02,0x01,0x00,0x00,0x09,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0C,0x01,0x00,0x00,0xFA,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0D,0x01,0x00,0x00,0x02,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x0F,0x01,0x00,0x00,0x0E,0x01,0x00,0x00, + 0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x0F,0x01,0x00,0x00, + 0x0F,0x01,0x00,0x00,0x0F,0x01,0x00,0x00,0x0F,0x01,0x00,0x00,0x0C,0x00,0x08,0x00, + 0x07,0x00,0x00,0x00,0x11,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2E,0x00,0x00,0x00, + 0x0C,0x01,0x00,0x00,0x0D,0x01,0x00,0x00,0x10,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x0B,0x01,0x00,0x00,0x11,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x12,0x01,0x00,0x00,0x0B,0x01,0x00,0x00,0xFE,0x00,0x02,0x00,0x12,0x01,0x00,0x00, + 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x36,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x35,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x37,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x15,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x16,0x01,0x00,0x00,0x34,0x00,0x00,0x00,0x0C,0x00,0x06,0x00,0x10,0x00,0x00,0x00, + 0x17,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x16,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x18,0x01,0x00,0x00,0x35,0x00,0x00,0x00, + 0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x19,0x01,0x00,0x00,0x17,0x01,0x00,0x00, + 0x18,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x15,0x01,0x00,0x00,0x19,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x1A,0x01,0x00,0x00,0x15,0x01,0x00,0x00, + 0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x1B,0x01,0x00,0x00,0x98,0x00,0x00,0x00, + 0x98,0x00,0x00,0x00,0x0C,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x1C,0x01,0x00,0x00, + 0x01,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x1A,0x01,0x00,0x00,0x1B,0x01,0x00,0x00, + 0x0C,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x1D,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x42,0x00,0x00,0x00,0x1C,0x01,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0x1E,0x01,0x00,0x00,0x15,0x01,0x00,0x00,0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x1F,0x01,0x00,0x00,0x1E,0x01,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0x20,0x01,0x00,0x00,0x15,0x01,0x00,0x00,0xB4,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x21,0x01,0x00,0x00,0x20,0x01,0x00,0x00, + 0x0C,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0x22,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x28,0x00,0x00,0x00,0x1F,0x01,0x00,0x00,0x21,0x01,0x00,0x00,0x0C,0x00,0x07,0x00, + 0x06,0x00,0x00,0x00,0x23,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x22,0x01,0x00,0x00,0x98,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x24,0x01,0x00,0x00,0x1D,0x01,0x00,0x00,0x23,0x01,0x00,0x00,0xFE,0x00,0x02,0x00, + 0x24,0x01,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x3D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x11,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x3A,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x3B,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x3E,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x28,0x01,0x00,0x00,0x29,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x2B,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x3C,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x3E,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x2A,0x01,0x00,0x00, + 0x3C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x2C,0x01,0x00,0x00, + 0x3C,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x2B,0x01,0x00,0x00,0x2C,0x01,0x00,0x00, + 0x39,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x2D,0x01,0x00,0x00,0x23,0x00,0x00,0x00, + 0x2B,0x01,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x2E,0x01,0x00,0x00, + 0x2A,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x2F,0x01,0x00,0x00,0x2A,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x30,0x01,0x00,0x00,0x2D,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x31,0x01,0x00,0x00,0x2D,0x01,0x00,0x00, + 0x01,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x32,0x01,0x00,0x00, + 0x2E,0x01,0x00,0x00,0x2F,0x01,0x00,0x00,0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x33,0x01,0x00,0x00,0x30,0x01,0x00,0x00,0x31,0x01,0x00,0x00,0x50,0x00,0x05,0x00, + 0x27,0x01,0x00,0x00,0x34,0x01,0x00,0x00,0x32,0x01,0x00,0x00,0x33,0x01,0x00,0x00, + 0x54,0x00,0x04,0x00,0x27,0x01,0x00,0x00,0x35,0x01,0x00,0x00,0x34,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x29,0x01,0x00,0x00,0x35,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x36,0x01,0x00,0x00,0x39,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x37,0x01,0x00,0x00,0x3A,0x00,0x00,0x00,0x83,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x38,0x01,0x00,0x00,0x36,0x01,0x00,0x00,0x37,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x39,0x00,0x00,0x00,0x38,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x27,0x01,0x00,0x00,0x39,0x01,0x00,0x00,0x29,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x3A,0x01,0x00,0x00,0x39,0x00,0x00,0x00,0x91,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x3B,0x01,0x00,0x00,0x39,0x01,0x00,0x00,0x3A,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x39,0x00,0x00,0x00,0x3B,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x3D,0x01,0x00,0x00,0x39,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x3C,0x01,0x00,0x00,0x3D,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x3F,0x01,0x00,0x00,0x3B,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x3E,0x01,0x00,0x00, + 0x3F,0x01,0x00,0x00,0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x40,0x01,0x00,0x00, + 0x36,0x00,0x00,0x00,0x3C,0x01,0x00,0x00,0x3E,0x01,0x00,0x00,0xFE,0x00,0x02,0x00, + 0x40,0x01,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x11,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x41,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x42,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x44,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x43,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x47,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x4B,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x52,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x53,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x55,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x5D,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x44,0x01,0x00,0x00,0x42,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x45,0x01,0x00,0x00,0x41,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x46,0x01,0x00,0x00,0x44,0x01,0x00,0x00,0x45,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x43,0x01,0x00,0x00,0x46,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x48,0x01,0x00,0x00,0x40,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x49,0x01,0x00,0x00,0x41,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x4A,0x01,0x00,0x00,0x48,0x01,0x00,0x00,0x49,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x47,0x01,0x00,0x00,0x4A,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x4C,0x01,0x00,0x00,0x47,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x4D,0x01,0x00,0x00,0x43,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x4E,0x01,0x00,0x00,0x4C,0x01,0x00,0x00,0x4D,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x4F,0x01,0x00,0x00,0x43,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x50,0x01,0x00,0x00,0x43,0x01,0x00,0x00,0x94,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x51,0x01,0x00,0x00,0x4F,0x01,0x00,0x00,0x50,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x52,0x01,0x00,0x00,0x4E,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x53,0x01,0x00,0x00,0x51,0x01,0x00,0x00,0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00, + 0x54,0x01,0x00,0x00,0x1B,0x00,0x00,0x00,0x52,0x01,0x00,0x00,0x53,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x4B,0x01,0x00,0x00,0x54,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x56,0x01,0x00,0x00,0x4B,0x01,0x00,0x00,0x0C,0x00,0x08,0x00, + 0x06,0x00,0x00,0x00,0x57,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00, + 0x56,0x01,0x00,0x00,0x98,0x00,0x00,0x00,0xF3,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x55,0x01,0x00,0x00,0x57,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x58,0x01,0x00,0x00,0x47,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x59,0x01,0x00,0x00,0x55,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x5A,0x01,0x00,0x00,0x43,0x01,0x00,0x00,0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x5B,0x01,0x00,0x00,0x5A,0x01,0x00,0x00,0x59,0x01,0x00,0x00,0x83,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x5C,0x01,0x00,0x00,0x58,0x01,0x00,0x00,0x5B,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x5D,0x01,0x00,0x00,0x5C,0x01,0x00,0x00,0x39,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x5E,0x01,0x00,0x00,0x1F,0x00,0x00,0x00,0x5D,0x01,0x00,0x00, + 0xFE,0x00,0x02,0x00,0x5E,0x01,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x11,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x47,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x48,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x4A,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x61,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x65,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x69,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x6D,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x71,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x75,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x79,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x82,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x83,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x88,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x91,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x92,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x97,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0xA0,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0xA1,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0xA6,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xA7,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xA9,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xAC,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xB1,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xB3,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xBC,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xBE,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xC8,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xCA,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x62,0x01,0x00,0x00,0x47,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x63,0x01,0x00,0x00,0x46,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x64,0x01,0x00,0x00,0x62,0x01,0x00,0x00,0x63,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x61,0x01,0x00,0x00,0x64,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x66,0x01,0x00,0x00,0x48,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x67,0x01,0x00,0x00,0x47,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x68,0x01,0x00,0x00,0x66,0x01,0x00,0x00,0x67,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x65,0x01,0x00,0x00,0x68,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x6A,0x01,0x00,0x00,0x46,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x6B,0x01,0x00,0x00,0x48,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x6C,0x01,0x00,0x00,0x6A,0x01,0x00,0x00,0x6B,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x69,0x01,0x00,0x00,0x6C,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x6E,0x01,0x00,0x00,0x45,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x6F,0x01,0x00,0x00,0x46,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x70,0x01,0x00,0x00,0x6E,0x01,0x00,0x00,0x6F,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x6D,0x01,0x00,0x00,0x70,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x72,0x01,0x00,0x00,0x45,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x73,0x01,0x00,0x00,0x47,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x74,0x01,0x00,0x00,0x72,0x01,0x00,0x00,0x73,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x71,0x01,0x00,0x00,0x74,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x76,0x01,0x00,0x00,0x45,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x77,0x01,0x00,0x00,0x48,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x78,0x01,0x00,0x00,0x76,0x01,0x00,0x00,0x77,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x75,0x01,0x00,0x00,0x78,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x7A,0x01,0x00,0x00,0x6D,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x7B,0x01,0x00,0x00,0x61,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x7C,0x01,0x00,0x00,0x6D,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x7D,0x01,0x00,0x00,0x61,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x7E,0x01,0x00,0x00,0x7C,0x01,0x00,0x00,0x7D,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x7F,0x01,0x00,0x00,0x61,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0x61,0x01,0x00,0x00,0x94,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x81,0x01,0x00,0x00,0x7F,0x01,0x00,0x00,0x80,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x82,0x01,0x00,0x00,0x7E,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x83,0x01,0x00,0x00,0x81,0x01,0x00,0x00,0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00, + 0x84,0x01,0x00,0x00,0x1B,0x00,0x00,0x00,0x82,0x01,0x00,0x00,0x83,0x01,0x00,0x00, + 0x0C,0x00,0x08,0x00,0x06,0x00,0x00,0x00,0x85,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2B,0x00,0x00,0x00,0x84,0x01,0x00,0x00,0x98,0x00,0x00,0x00,0xF3,0x00,0x00,0x00, + 0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x86,0x01,0x00,0x00,0x7B,0x01,0x00,0x00, + 0x85,0x01,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x87,0x01,0x00,0x00, + 0x7A,0x01,0x00,0x00,0x86,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x79,0x01,0x00,0x00, + 0x87,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x89,0x01,0x00,0x00, + 0x71,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x8A,0x01,0x00,0x00, + 0x65,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x8B,0x01,0x00,0x00, + 0x71,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x8C,0x01,0x00,0x00, + 0x65,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x8D,0x01,0x00,0x00, + 0x8B,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x8E,0x01,0x00,0x00,0x65,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x8F,0x01,0x00,0x00,0x65,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x90,0x01,0x00,0x00,0x8E,0x01,0x00,0x00,0x8F,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x91,0x01,0x00,0x00,0x8D,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x92,0x01,0x00,0x00, + 0x90,0x01,0x00,0x00,0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x93,0x01,0x00,0x00, + 0x1B,0x00,0x00,0x00,0x91,0x01,0x00,0x00,0x92,0x01,0x00,0x00,0x0C,0x00,0x08,0x00, + 0x06,0x00,0x00,0x00,0x94,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00, + 0x93,0x01,0x00,0x00,0x98,0x00,0x00,0x00,0xF3,0x00,0x00,0x00,0x8E,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x95,0x01,0x00,0x00,0x8A,0x01,0x00,0x00,0x94,0x01,0x00,0x00, + 0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x96,0x01,0x00,0x00,0x89,0x01,0x00,0x00, + 0x95,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x88,0x01,0x00,0x00,0x96,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x98,0x01,0x00,0x00,0x75,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x99,0x01,0x00,0x00,0x69,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x9A,0x01,0x00,0x00,0x75,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x9B,0x01,0x00,0x00,0x69,0x01,0x00,0x00, + 0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x9C,0x01,0x00,0x00,0x9A,0x01,0x00,0x00, + 0x9B,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x9D,0x01,0x00,0x00, + 0x69,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x9E,0x01,0x00,0x00, + 0x69,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x9F,0x01,0x00,0x00, + 0x9D,0x01,0x00,0x00,0x9E,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xA0,0x01,0x00,0x00, + 0x9C,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xA1,0x01,0x00,0x00,0x9F,0x01,0x00,0x00, + 0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0xA2,0x01,0x00,0x00,0x1B,0x00,0x00,0x00, + 0xA0,0x01,0x00,0x00,0xA1,0x01,0x00,0x00,0x0C,0x00,0x08,0x00,0x06,0x00,0x00,0x00, + 0xA3,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0xA2,0x01,0x00,0x00, + 0x98,0x00,0x00,0x00,0xF3,0x00,0x00,0x00,0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0xA4,0x01,0x00,0x00,0x99,0x01,0x00,0x00,0xA3,0x01,0x00,0x00,0x83,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0xA5,0x01,0x00,0x00,0x98,0x01,0x00,0x00,0xA4,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x97,0x01,0x00,0x00,0xA5,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xA8,0x01,0x00,0x00,0x61,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xA7,0x01,0x00,0x00,0xA8,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xAA,0x01,0x00,0x00,0x69,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xA9,0x01,0x00,0x00, + 0xAA,0x01,0x00,0x00,0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0xAB,0x01,0x00,0x00, + 0x28,0x00,0x00,0x00,0xA7,0x01,0x00,0x00,0xA9,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xA6,0x01,0x00,0x00,0xAB,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xAD,0x01,0x00,0x00,0x79,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xAE,0x01,0x00,0x00,0x79,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0xAF,0x01,0x00,0x00,0xAD,0x01,0x00,0x00,0xAE,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xB0,0x01,0x00,0x00,0xA6,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xB2,0x01,0x00,0x00,0x6D,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xB1,0x01,0x00,0x00,0xB2,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xB4,0x01,0x00,0x00,0x61,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xB3,0x01,0x00,0x00, + 0xB4,0x01,0x00,0x00,0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0xB5,0x01,0x00,0x00, + 0x28,0x00,0x00,0x00,0xB1,0x01,0x00,0x00,0xB3,0x01,0x00,0x00,0x85,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0xB6,0x01,0x00,0x00,0xB0,0x01,0x00,0x00,0xB5,0x01,0x00,0x00, + 0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0xB7,0x01,0x00,0x00,0xAF,0x01,0x00,0x00, + 0xB6,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xB8,0x01,0x00,0x00, + 0x88,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xB9,0x01,0x00,0x00, + 0x88,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xBA,0x01,0x00,0x00, + 0xB8,0x01,0x00,0x00,0xB9,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xBB,0x01,0x00,0x00,0xA6,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xBD,0x01,0x00,0x00,0x71,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xBC,0x01,0x00,0x00, + 0xBD,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xBF,0x01,0x00,0x00, + 0x65,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xBE,0x01,0x00,0x00,0xBF,0x01,0x00,0x00, + 0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x28,0x00,0x00,0x00, + 0xBC,0x01,0x00,0x00,0xBE,0x01,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0xC1,0x01,0x00,0x00,0xBB,0x01,0x00,0x00,0xC0,0x01,0x00,0x00,0x50,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0xC2,0x01,0x00,0x00,0xBA,0x01,0x00,0x00,0xC1,0x01,0x00,0x00, + 0x0C,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xC3,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0xB7,0x01,0x00,0x00,0xC2,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xC4,0x01,0x00,0x00,0x97,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xC5,0x01,0x00,0x00,0x97,0x01,0x00,0x00,0x94,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0xC6,0x01,0x00,0x00,0xC4,0x01,0x00,0x00,0xC5,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xC7,0x01,0x00,0x00,0xA6,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xC9,0x01,0x00,0x00,0x75,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xC8,0x01,0x00,0x00,0xC9,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xCB,0x01,0x00,0x00,0x69,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xCA,0x01,0x00,0x00,0xCB,0x01,0x00,0x00,0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00, + 0xCC,0x01,0x00,0x00,0x28,0x00,0x00,0x00,0xC8,0x01,0x00,0x00,0xCA,0x01,0x00,0x00, + 0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xCD,0x01,0x00,0x00,0xC7,0x01,0x00,0x00, + 0xCC,0x01,0x00,0x00,0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0xCE,0x01,0x00,0x00, + 0xC6,0x01,0x00,0x00,0xCD,0x01,0x00,0x00,0x0C,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0xCF,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0xC3,0x01,0x00,0x00, + 0xCE,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xAC,0x01,0x00,0x00,0xCF,0x01,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0xD0,0x01,0x00,0x00,0xAC,0x01,0x00,0x00, + 0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xD1,0x01,0x00,0x00, + 0xD0,0x01,0x00,0x00,0x0C,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0xD2,0x01,0x00,0x00, + 0x01,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0xD1,0x01,0x00,0x00,0x7F,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xD3,0x01,0x00,0x00,0xD2,0x01,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0xD4,0x01,0x00,0x00,0xAC,0x01,0x00,0x00,0xB4,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xD5,0x01,0x00,0x00,0xD4,0x01,0x00,0x00, + 0x0C,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0xD6,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0xD5,0x01,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0xD7,0x01,0x00,0x00,0xD3,0x01,0x00,0x00,0xD6,0x01,0x00,0x00,0xFE,0x00,0x02,0x00, + 0xD7,0x01,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x55,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x11,0x00,0x00,0x00,0x52,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x4E,0x00,0x00,0x00, + 0x53,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x50,0x00,0x00,0x00,0x54,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x56,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0xDA,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0xE5,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x50,0x00,0x00,0x00, + 0xE6,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x50,0x00,0x00,0x00, + 0xE7,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xF3,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xFB,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x01,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x14,0x02,0x00,0x00, + 0x15,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xDB,0x01,0x00,0x00,0x52,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00, + 0xDD,0x01,0x00,0x00,0x53,0x00,0x00,0x00,0xDC,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xDE,0x01,0x00,0x00,0xDD,0x01,0x00,0x00,0x83,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0xDF,0x01,0x00,0x00,0xDB,0x01,0x00,0x00,0xDE,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0x52,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0xE1,0x01,0x00,0x00,0x53,0x00,0x00,0x00, + 0xDC,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xE2,0x01,0x00,0x00, + 0xE1,0x01,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0xE3,0x01,0x00,0x00, + 0xE0,0x01,0x00,0x00,0xE2,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0xE4,0x01,0x00,0x00,0xDF,0x01,0x00,0x00,0xE3,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xDA,0x01,0x00,0x00,0xE4,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xE5,0x01,0x00,0x00, + 0xF3,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xE6,0x01,0x00,0x00,0xDC,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0xE8,0x01,0x00,0x00,0x54,0x00,0x00,0x00, + 0x82,0x00,0x05,0x00,0x4F,0x00,0x00,0x00,0xEA,0x01,0x00,0x00,0xE8,0x01,0x00,0x00, + 0xE9,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xE7,0x01,0x00,0x00,0xEA,0x01,0x00,0x00, + 0xF9,0x00,0x02,0x00,0xEB,0x01,0x00,0x00,0xF8,0x00,0x02,0x00,0xEB,0x01,0x00,0x00, + 0xF6,0x00,0x04,0x00,0xED,0x01,0x00,0x00,0xEE,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0xF9,0x00,0x02,0x00,0xEF,0x01,0x00,0x00,0xF8,0x00,0x02,0x00,0xEF,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0xF0,0x01,0x00,0x00,0xE6,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0xF1,0x01,0x00,0x00,0x54,0x00,0x00,0x00, + 0xB1,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xF2,0x01,0x00,0x00,0xF0,0x01,0x00,0x00, + 0xF1,0x01,0x00,0x00,0xFA,0x00,0x04,0x00,0xF2,0x01,0x00,0x00,0xEC,0x01,0x00,0x00, + 0xED,0x01,0x00,0x00,0xF8,0x00,0x02,0x00,0xEC,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xF4,0x01,0x00,0x00,0xE7,0x01,0x00,0x00,0x41,0x00,0x05,0x00, + 0x11,0x00,0x00,0x00,0xF5,0x01,0x00,0x00,0x53,0x00,0x00,0x00,0xF4,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xF6,0x01,0x00,0x00,0xF5,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0xF7,0x01,0x00,0x00,0xE6,0x01,0x00,0x00, + 0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0xF8,0x01,0x00,0x00,0x53,0x00,0x00,0x00, + 0xF7,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xF9,0x01,0x00,0x00, + 0xF8,0x01,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0xFA,0x01,0x00,0x00, + 0xF6,0x01,0x00,0x00,0xF9,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xF3,0x01,0x00,0x00, + 0xFA,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xFC,0x01,0x00,0x00, + 0x52,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0xFD,0x01,0x00,0x00, + 0xE6,0x01,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0xFE,0x01,0x00,0x00, + 0x53,0x00,0x00,0x00,0xFD,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xFF,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x00,0x02,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xFB,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x02,0x02,0x00,0x00,0xFB,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x03,0x02,0x00,0x00,0xF3,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x04,0x02,0x00,0x00,0xFB,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x05,0x02,0x00,0x00,0xF3,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x06,0x02,0x00,0x00,0x04,0x02,0x00,0x00,0x05,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x07,0x02,0x00,0x00,0xF3,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x08,0x02,0x00,0x00,0xF3,0x01,0x00,0x00,0x94,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x09,0x02,0x00,0x00,0x07,0x02,0x00,0x00,0x08,0x02,0x00,0x00, + 0x88,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x0A,0x02,0x00,0x00,0x06,0x02,0x00,0x00, + 0x09,0x02,0x00,0x00,0x0C,0x00,0x08,0x00,0x06,0x00,0x00,0x00,0x0B,0x02,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x0A,0x02,0x00,0x00,0x98,0x00,0x00,0x00, + 0xF3,0x00,0x00,0x00,0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x0C,0x02,0x00,0x00, + 0x03,0x02,0x00,0x00,0x0B,0x02,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x0D,0x02,0x00,0x00,0x02,0x02,0x00,0x00,0x0C,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x01,0x02,0x00,0x00,0x0D,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x0E,0x02,0x00,0x00,0xDA,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x0F,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x10,0x02,0x00,0x00,0x01,0x02,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x11,0x02,0x00,0x00,0x0F,0x02,0x00,0x00,0x10,0x02,0x00,0x00,0x0C,0x00,0x07,0x00, + 0x06,0x00,0x00,0x00,0x12,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x0E,0x02,0x00,0x00,0x11,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xDA,0x01,0x00,0x00, + 0x12,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x16,0x02,0x00,0x00, + 0x52,0x00,0x00,0x00,0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x17,0x02,0x00,0x00,0x16,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0x18,0x02,0x00,0x00,0xE6,0x01,0x00,0x00,0x41,0x00,0x06,0x00,0x17,0x00,0x00,0x00, + 0x19,0x02,0x00,0x00,0x53,0x00,0x00,0x00,0x18,0x02,0x00,0x00,0xB4,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x1A,0x02,0x00,0x00,0x19,0x02,0x00,0x00, + 0xBE,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x1B,0x02,0x00,0x00,0x17,0x02,0x00,0x00, + 0x1A,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x1C,0x02,0x00,0x00, + 0x52,0x00,0x00,0x00,0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x1D,0x02,0x00,0x00,0x1C,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0x1E,0x02,0x00,0x00,0xE7,0x01,0x00,0x00,0x41,0x00,0x06,0x00,0x17,0x00,0x00,0x00, + 0x1F,0x02,0x00,0x00,0x53,0x00,0x00,0x00,0x1E,0x02,0x00,0x00,0xB4,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x20,0x02,0x00,0x00,0x1F,0x02,0x00,0x00, + 0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x21,0x02,0x00,0x00,0x1D,0x02,0x00,0x00, + 0x20,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x22,0x02,0x00,0x00, + 0xF3,0x01,0x00,0x00,0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x23,0x02,0x00,0x00,0x22,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0x24,0x02,0x00,0x00,0xFB,0x01,0x00,0x00,0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x25,0x02,0x00,0x00,0x24,0x02,0x00,0x00,0x85,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x26,0x02,0x00,0x00,0x23,0x02,0x00,0x00,0x25,0x02,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x27,0x02,0x00,0x00,0xF3,0x01,0x00,0x00, + 0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x28,0x02,0x00,0x00, + 0x27,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x29,0x02,0x00,0x00, + 0xFB,0x01,0x00,0x00,0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x2A,0x02,0x00,0x00,0x29,0x02,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x2B,0x02,0x00,0x00,0x28,0x02,0x00,0x00,0x2A,0x02,0x00,0x00,0xBA,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x2C,0x02,0x00,0x00,0x26,0x02,0x00,0x00,0x2B,0x02,0x00,0x00, + 0x50,0x00,0x06,0x00,0x13,0x02,0x00,0x00,0x2D,0x02,0x00,0x00,0x1B,0x02,0x00,0x00, + 0x21,0x02,0x00,0x00,0x2C,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x15,0x02,0x00,0x00, + 0x2D,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x13,0x02,0x00,0x00,0x2E,0x02,0x00,0x00, + 0x15,0x02,0x00,0x00,0x9B,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x2F,0x02,0x00,0x00, + 0x2E,0x02,0x00,0x00,0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x30,0x02,0x00,0x00, + 0x2F,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0x32,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFA,0x00,0x04,0x00,0x30,0x02,0x00,0x00,0x31,0x02,0x00,0x00,0x32,0x02,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x31,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x13,0x02,0x00,0x00, + 0x33,0x02,0x00,0x00,0x15,0x02,0x00,0x00,0xA8,0x00,0x04,0x00,0x13,0x02,0x00,0x00, + 0x34,0x02,0x00,0x00,0x33,0x02,0x00,0x00,0x9B,0x00,0x04,0x00,0x99,0x00,0x00,0x00, + 0x35,0x02,0x00,0x00,0x34,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0x32,0x02,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x32,0x02,0x00,0x00,0xF5,0x00,0x07,0x00,0x99,0x00,0x00,0x00, + 0x36,0x02,0x00,0x00,0x2F,0x02,0x00,0x00,0xEC,0x01,0x00,0x00,0x35,0x02,0x00,0x00, + 0x31,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0x38,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFA,0x00,0x04,0x00,0x36,0x02,0x00,0x00,0x37,0x02,0x00,0x00,0x38,0x02,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x37,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x39,0x02,0x00,0x00,0xE5,0x01,0x00,0x00,0x7F,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x3A,0x02,0x00,0x00,0x39,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xE5,0x01,0x00,0x00, + 0x3A,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0x38,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x38,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xEE,0x01,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xEE,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x3B,0x02,0x00,0x00, + 0xE6,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xE7,0x01,0x00,0x00,0x3B,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x3C,0x02,0x00,0x00,0xE6,0x01,0x00,0x00, + 0x80,0x00,0x05,0x00,0x4F,0x00,0x00,0x00,0x3D,0x02,0x00,0x00,0x3C,0x02,0x00,0x00, + 0xE9,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xE6,0x01,0x00,0x00,0x3D,0x02,0x00,0x00, + 0xF9,0x00,0x02,0x00,0xEB,0x01,0x00,0x00,0xF8,0x00,0x02,0x00,0xED,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x3E,0x02,0x00,0x00,0xE5,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x3F,0x02,0x00,0x00,0xDA,0x01,0x00,0x00, + 0x0C,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x40,0x02,0x00,0x00,0x01,0x00,0x00,0x00, + 0x1F,0x00,0x00,0x00,0x3F,0x02,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x41,0x02,0x00,0x00,0x3E,0x02,0x00,0x00,0x40,0x02,0x00,0x00,0xFE,0x00,0x02,0x00, + 0x41,0x02,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x5C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x08,0x00,0x00,0x00,0x5A,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x58,0x00,0x00,0x00, + 0x5B,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x5D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x44,0x02,0x00,0x00,0x5A,0x00,0x00,0x00,0x4F,0x00,0x08,0x00, + 0x5E,0x00,0x00,0x00,0x45,0x02,0x00,0x00,0x44,0x02,0x00,0x00,0x44,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x47,0x02,0x00,0x00,0x5B,0x00,0x00,0x00,0x46,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x48,0x02,0x00,0x00,0x47,0x02,0x00,0x00, + 0x4F,0x00,0x08,0x00,0x5E,0x00,0x00,0x00,0x49,0x02,0x00,0x00,0x48,0x02,0x00,0x00, + 0x48,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x41,0x00,0x06,0x00,0x17,0x00,0x00,0x00,0x4A,0x02,0x00,0x00,0x5B,0x00,0x00,0x00, + 0x46,0x02,0x00,0x00,0x65,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x4B,0x02,0x00,0x00,0x4A,0x02,0x00,0x00,0x50,0x00,0x06,0x00,0x5E,0x00,0x00,0x00, + 0x4C,0x02,0x00,0x00,0x4B,0x02,0x00,0x00,0x4B,0x02,0x00,0x00,0x4B,0x02,0x00,0x00, + 0x0C,0x00,0x08,0x00,0x5E,0x00,0x00,0x00,0x4D,0x02,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2E,0x00,0x00,0x00,0x45,0x02,0x00,0x00,0x49,0x02,0x00,0x00,0x4C,0x02,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x4E,0x02,0x00,0x00,0x5A,0x00,0x00,0x00, + 0x65,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x4F,0x02,0x00,0x00, + 0x4E,0x02,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x50,0x02,0x00,0x00, + 0x4D,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x51,0x02,0x00,0x00,0x4D,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x52,0x02,0x00,0x00,0x4D,0x02,0x00,0x00,0x02,0x00,0x00,0x00, + 0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x53,0x02,0x00,0x00,0x50,0x02,0x00,0x00, + 0x51,0x02,0x00,0x00,0x52,0x02,0x00,0x00,0x4F,0x02,0x00,0x00,0xFE,0x00,0x02,0x00, + 0x53,0x02,0x00,0x00,0x38,0x00,0x01,0x00, }; +#endif +static const char s_flash_shd_bytecode_draw_glsl300_src[9886] = +"#version 300 es\n" +"precision mediump float;\n" +"precision highp int;\n" +"\n" +"struct ShaderParams\n" +"{\n" +" highp vec2 view_pos;\n" +" highp vec2 uv;\n" +" highp vec2 uv_min;\n" +" highp vec2 uv_max;\n" +" highp vec2 screen_uv;\n" +" highp vec4 attributes;\n" +"};\n" +"\n" +"layout(std140) uniform uniform_block\n" +"{\n" +" highp vec2 u_texture_size;\n" +" int u_alpha_discard;\n" +" int u_use_smooth_uv;\n" +"} _683;\n" +"\n" +"uniform highp sampler2D u_image;\n" +"\n" +"layout(location = 0) out highp vec4 result;\n" +"in highp vec4 v_pos_uv;\n" +"in highp vec4 v_shape;\n" +"in highp vec4 v_blend_posH;\n" +"in highp vec4 v_col;\n" +"in highp vec4 v_ab;\n" +"in highp vec4 v_cd;\n" +"in highp vec4 v_ef;\n" +"in highp vec4 v_gh;\n" +"flat in int v_n;\n" +"in highp vec4 v_uv_bounds;\n" +"in highp vec4 v_user;\n" +"highp float v_stroke;\n" +"highp float v_aa;\n" +"highp float v_fill;\n" +"highp vec2 v_pos;\n" +"highp vec2 v_uv;\n" +"highp float v_radius;\n" +"highp float v_type;\n" +"highp float v_alpha;\n" +"highp vec2 v_posH;\n" +"highp vec2 pts[8];\n" +"highp vec2 pos;\n" +"highp vec2 screen_uv;\n" +"\n" +"highp vec2 smooth_uv(highp vec2 uv, highp vec2 texture_size)\n" +"{\n" +" highp vec2 pixel = uv * texture_size;\n" +" highp vec2 seam = floor(pixel + vec2(0.5));\n" +" pixel = seam + clamp((pixel - seam) / fwidth(pixel), vec2(-0.5), vec2(0.5));\n" +" return pixel / texture_size;\n" +"}\n" +"\n" +"highp vec4 de_gamma(highp vec4 c)\n" +"{\n" +" return vec4(pow(abs(c.xyz), vec3(2.2000000476837158203125)), c.w);\n" +"}\n" +"\n" +"highp vec4 gamma(highp vec4 c)\n" +"{\n" +" return vec4(pow(abs(c.xyz), vec3(0.4545454680919647216796875)), c.w);\n" +"}\n" +"\n" +"highp vec2 skew(highp vec2 v)\n" +"{\n" +" return vec2(-v.y, v.x);\n" +"}\n" +"\n" +"highp float distance_aabb(highp vec2 p, highp vec2 he)\n" +"{\n" +" highp vec2 d = abs(p) - he;\n" +" return length(max(d, vec2(0.0))) + min(max(d.x, d.y), 0.0);\n" +"}\n" +"\n" +"highp float distance_box(inout highp vec2 p, highp vec2 c, highp vec2 he, highp vec2 u)\n" +"{\n" +" highp vec2 param = u;\n" +" highp mat2 m = transpose(mat2(vec2(u), vec2(skew(param))));\n" +" p -= c;\n" +" p = m * p;\n" +" highp vec2 param_1 = p;\n" +" highp vec2 param_2 = he;\n" +" return distance_aabb(param_1, param_2);\n" +"}\n" +"\n" +"highp float safe_div(highp float a, highp float b)\n" +"{\n" +" highp float _155;\n" +" if (b == 0.0)\n" +" {\n" +" _155 = 0.0;\n" +" }\n" +" else\n" +" {\n" +" _155 = a / b;\n" +" }\n" +" return _155;\n" +"}\n" +"\n" +"highp float safe_len(highp vec2 v)\n" +"{\n" +" highp float d = dot(v, v);\n" +" highp float _171;\n" +" if (d == 0.0)\n" +" {\n" +" _171 = 0.0;\n" +" }\n" +" else\n" +" {\n" +" _171 = sqrt(d);\n" +" }\n" +" return _171;\n" +"}\n" +"\n" +"highp float distance_segment(highp vec2 p, highp vec2 a, highp vec2 b)\n" +"{\n" +" highp vec2 n = b - a;\n" +" highp vec2 pa = p - a;\n" +" highp float param = dot(pa, n);\n" +" highp float param_1 = dot(n, n);\n" +" highp float d = safe_div(param, param_1);\n" +" highp float h = clamp(d, 0.0, 1.0);\n" +" highp vec2 param_2 = pa - (n * h);\n" +" return safe_len(param_2);\n" +"}\n" +"\n" +"highp float det2(highp vec2 a, highp vec2 b)\n" +"{\n" +" return (a.x * b.y) - (a.y * b.x);\n" +"}\n" +"\n" +"highp float distance_triangle(highp vec2 p, highp vec2 a, highp vec2 b, highp vec2 c)\n" +"{\n" +" highp vec2 e0 = b - a;\n" +" highp vec2 e1 = c - b;\n" +" highp vec2 e2 = a - c;\n" +" highp vec2 v0 = p - a;\n" +" highp vec2 v1 = p - b;\n" +" highp vec2 v2 = p - c;\n" +" highp float param = dot(v0, e0);\n" +" highp float param_1 = dot(e0, e0);\n" +" highp vec2 pq0 = v0 - (e0 * clamp(safe_div(param, param_1), 0.0, 1.0));\n" +" highp float param_2 = dot(v1, e1);\n" +" highp float param_3 = dot(e1, e1);\n" +" highp vec2 pq1 = v1 - (e1 * clamp(safe_div(param_2, param_3), 0.0, 1.0));\n" +" highp float param_4 = dot(v2, e2);\n" +" highp float param_5 = dot(e2, e2);\n" +" highp vec2 pq2 = v2 - (e2 * clamp(safe_div(param_4, param_5), 0.0, 1.0));\n" +" highp vec2 param_6 = e0;\n" +" highp vec2 param_7 = e2;\n" +" highp float s = det2(param_6, param_7);\n" +" highp vec2 param_8 = v0;\n" +" highp vec2 param_9 = e0;\n" +" highp vec2 param_10 = v1;\n" +" highp vec2 param_11 = e1;\n" +" highp vec2 param_12 = v2;\n" +" highp vec2 param_13 = e2;\n" +" highp vec2 d = min(min(vec2(dot(pq0, pq0), s * det2(param_8, param_9)), vec2(dot(pq1, pq1), s * det2(param_10, param_11))), vec2(dot(pq2, pq2), s * det2(param_12, param_13)));\n" +" return (-sqrt(d.x)) * sign(d.y);\n" +"}\n" +"\n" +"highp float distance_polygon(highp vec2 p, highp vec2 v[8], int N)\n" +"{\n" +" highp float d = dot(p - v[0], p - v[0]);\n" +" highp float s = 1.0;\n" +" int _490 = N - 1;\n" +" for (int i = 0, j = _490; i < N; j = i, i++)\n" +" {\n" +" highp vec2 e = v[j] - v[i];\n" +" highp vec2 w = p - v[i];\n" +" highp vec2 b = w - (e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0));\n" +" d = min(d, dot(b, b));\n" +" bvec3 cond = bvec3(p.y >= v[i].y, p.y < v[j].y, (e.x * w.y) > (e.y * w.x));\n" +" bool _559 = all(cond);\n" +" bool _566;\n" +" if (!_559)\n" +" {\n" +" _566 = all(not(cond));\n" +" }\n" +" else\n" +" {\n" +" _566 = _559;\n" +" }\n" +" if (_566)\n" +" {\n" +" s = -s;\n" +" }\n" +" }\n" +" return s * sqrt(d);\n" +"}\n" +"\n" +"highp float sdf_stroke(highp float d)\n" +"{\n" +" return abs(d) - v_stroke;\n" +"}\n" +"\n" +"highp vec4 sdf(highp vec4 a, highp vec4 b, highp float d)\n" +"{\n" +" highp float param = d;\n" +" highp float wire_d = sdf_stroke(param);\n" +" highp vec4 stroke_aa = mix(b, a, vec4(smoothstep(0.0, v_aa, wire_d)));\n" +" bvec4 _230 = bvec4(wire_d <= 0.0);\n" +" highp vec4 stroke_no_aa = vec4(_230.x ? b.x : a.x, _230.y ? b.y : a.y, _230.z ? b.z : a.z, _230.w ? b.w : a.w);\n" +" highp vec4 fill_aa = mix(b, a, vec4(smoothstep(0.0, v_aa, d)));\n" +" bvec4 _248 = bvec4(clamp(d, -1.0, 1.0) <= 0.0);\n" +" highp vec4 fill_no_aa = vec4(_248.x ? b.x : a.x, _248.y ? b.y : a.y, _248.z ? b.z : a.z, _248.w ? b.w : a.w);\n" +" highp vec4 stroke = mix(stroke_aa, stroke_aa, vec4(float(v_aa > 0.0)));\n" +" highp vec4 fill = mix(fill_no_aa, fill_aa, vec4(float(v_aa > 0.0)));\n" +" result = mix(stroke, fill, vec4(v_fill));\n" +" return result;\n" +"}\n" +"\n" +"highp vec4 shader(highp vec4 color, ShaderParams params)\n" +"{\n" +" return vec4(mix(color.xyz, params.attributes.xyz, vec3(params.attributes.w)), color.w);\n" +"}\n" +"\n" +"void main()\n" +"{\n" +" v_pos = v_pos_uv.xy;\n" +" v_uv = v_pos_uv.zw;\n" +" v_radius = v_shape.x;\n" +" v_stroke = v_shape.y;\n" +" v_aa = v_shape.z;\n" +" v_type = v_shape.w;\n" +" v_alpha = v_blend_posH.x;\n" +" v_fill = v_blend_posH.y;\n" +" v_posH = v_blend_posH.zw;\n" +" bool is_sprite = (v_type >= 0.0) && (v_type < 0.5);\n" +" bool is_text = (v_type > 0.5) && (v_type < 1.5);\n" +" bool is_box = (v_type > 1.5) && (v_type < 2.5);\n" +" bool is_seg = (v_type > 2.5) && (v_type < 3.5);\n" +" bool is_tri = (v_type > 3.5) && (v_type < 4.5);\n" +" bool is_tri_sdf = (v_type > 4.5) && (v_type < 5.5);\n" +" bool is_poly = (v_type > 5.5) && (v_type < 6.5);\n" +" highp vec4 c = vec4(0.0);\n" +" highp vec2 _689;\n" +" if (_683.u_use_smooth_uv == 0)\n" +" {\n" +" highp vec2 param = v_uv;\n" +" highp vec2 param_1 = _683.u_texture_size;\n" +" _689 = smooth_uv(param, param_1);\n" +" }\n" +" else\n" +" {\n" +" _689 = v_uv;\n" +" }\n" +" highp vec2 uv = _689;\n" +" highp vec4 param_2 = texture(u_image, uv);\n" +" highp vec4 tex_c = de_gamma(param_2);\n" +" highp vec4 _713;\n" +" if (is_sprite)\n" +" {\n" +" highp vec4 param_3 = tex_c;\n" +" _713 = gamma(param_3);\n" +" }\n" +" else\n" +" {\n" +" _713 = c;\n" +" }\n" +" c = _713;\n" +" highp vec4 _723;\n" +" if (is_text)\n" +" {\n" +" _723 = v_col * tex_c.w;\n" +" }\n" +" else\n" +" {\n" +" _723 = c;\n" +" }\n" +" c = _723;\n" +" bvec4 _737 = bvec4(is_tri);\n" +" c = vec4(_737.x ? v_col.x : c.x, _737.y ? v_col.y : c.y, _737.z ? v_col.z : c.z, _737.w ? v_col.w : c.w);\n" +" highp float d = 0.0;\n" +" if (is_box)\n" +" {\n" +" highp vec2 param_4 = v_pos;\n" +" highp vec2 param_5 = v_ab.xy;\n" +" highp vec2 param_6 = v_ab.zw;\n" +" highp vec2 param_7 = v_cd.xy;\n" +" highp float _756 = distance_box(param_4, param_5, param_6, param_7);\n" +" d = _756;\n" +" }\n" +" else\n" +" {\n" +" if (is_seg)\n" +" {\n" +" highp vec2 param_8 = v_pos;\n" +" highp vec2 param_9 = v_ab.xy;\n" +" highp vec2 param_10 = v_ab.zw;\n" +" d = distance_segment(param_8, param_9, param_10);\n" +" highp vec2 param_11 = v_pos;\n" +" highp vec2 param_12 = v_ab.zw;\n" +" highp vec2 param_13 = v_cd.xy;\n" +" d = min(d, distance_segment(param_11, param_12, param_13));\n" +" }\n" +" else\n" +" {\n" +" if (is_tri_sdf)\n" +" {\n" +" highp vec2 param_14 = v_pos;\n" +" highp vec2 param_15 = v_ab.xy;\n" +" highp vec2 param_16 = v_ab.zw;\n" +" highp vec2 param_17 = v_cd.xy;\n" +" d = distance_triangle(param_14, param_15, param_16, param_17);\n" +" }\n" +" else\n" +" {\n" +" if (is_poly)\n" +" {\n" +" pts[0] = v_ab.xy;\n" +" pts[1] = v_ab.zw;\n" +" pts[2] = v_cd.xy;\n" +" pts[3] = v_cd.zw;\n" +" pts[4] = v_ef.xy;\n" +" pts[5] = v_ef.zw;\n" +" pts[6] = v_gh.xy;\n" +" pts[7] = v_gh.zw;\n" +" highp vec2 param_18 = v_pos;\n" +" highp vec2 param_19[8] = pts;\n" +" int param_20 = v_n;\n" +" d = distance_polygon(param_18, param_19, param_20);\n" +" }\n" +" }\n" +" }\n" +" }\n" +" highp vec4 _850;\n" +" if (((!is_sprite) && (!is_text)) && (!is_tri))\n" +" {\n" +" highp vec4 param_21 = c;\n" +" highp vec4 param_22 = v_col;\n" +" highp float param_23 = d - v_radius;\n" +" highp vec4 _861 = sdf(param_21, param_22, param_23);\n" +" _850 = _861;\n" +" }\n" +" else\n" +" {\n" +" _850 = c;\n" +" }\n" +" c = _850;\n" +" c *= v_alpha;\n" +" pos = v_pos;\n" +" screen_uv = ((v_posH + vec2(1.0, -1.0)) * 0.5) * vec2(1.0, -1.0);\n" +" ShaderParams sp;\n" +" sp.view_pos = pos;\n" +" sp.uv = v_uv;\n" +" sp.uv_min = v_uv_bounds.xy;\n" +" sp.uv_max = v_uv_bounds.zw;\n" +" sp.screen_uv = screen_uv;\n" +" sp.attributes = v_user;\n" +" highp vec4 param_24 = c;\n" +" ShaderParams param_25 = sp;\n" +" c = shader(param_24, param_25);\n" +" bool _900 = _683.u_alpha_discard != 0;\n" +" bool _906;\n" +" if (_900)\n" +" {\n" +" _906 = c.w == 0.0;\n" +" }\n" +" else\n" +" {\n" +" _906 = _900;\n" +" }\n" +" if (_906)\n" +" {\n" +" discard;\n" +" }\n" +" result = c;\n" +"}\n" +"\n" +""; static const char* s_flash_shd_bytecode_draw_image_names[1] = { "u_image", }; +static int s_flash_shd_bytecode_draw_image_binding_slots[1] = { 0, }; static CF_ShaderUniformInfo s_flash_shd_bytecode_draw_uniforms[1] = { - { - .block_name = "uniform_block", - .block_index = 0, - .block_size = 16, - .num_members = 2, - }, + { + .block_name = "uniform_block", + .block_index = 0, + .block_size = 16, + .num_members = 3, + }, }; -static CF_ShaderUniformMemberInfo s_flash_shd_bytecode_draw_uniform_members[2] = { - { - .name = "u_texture_size", - .type = CF_SHADER_INFO_TYPE_FLOAT2, - .offset = 0, - .array_length = 1, - }, - { - .name = "u_alpha_discard", - .type = CF_SHADER_INFO_TYPE_SINT, - .offset = 8, - .array_length = 1, - }, -}; -static CF_ShaderInputInfo* s_flash_shd_bytecode_draw_inputs = NULL; -static const CF_ShaderBytecode s_flash_shd_bytecode_draw = { - .content = s_flash_shd_bytecode_draw_content, - .size = 21016, - .shader_info = { - .num_samplers = 1, - .num_storage_textures = 0, - .num_storage_buffers = 0, - .num_images = 1, - .image_names = s_flash_shd_bytecode_draw_image_names, - .num_uniforms = 1, - .uniforms = s_flash_shd_bytecode_draw_uniforms, - .num_uniform_members = 2, - .uniform_members = s_flash_shd_bytecode_draw_uniform_members, - .num_inputs = 0, - .inputs = s_flash_shd_bytecode_draw_inputs, - }, +static CF_ShaderUniformMemberInfo s_flash_shd_bytecode_draw_uniform_members[3] = { + { + .name = "u_texture_size", + .type = CF_SHADER_INFO_TYPE_FLOAT2, + .offset = 0, + .array_length = 1, + }, + { + .name = "u_alpha_discard", + .type = CF_SHADER_INFO_TYPE_SINT, + .offset = 8, + .array_length = 1, + }, + { + .name = "u_use_smooth_uv", + .type = CF_SHADER_INFO_TYPE_SINT, + .offset = 12, + .array_length = 1, + }, }; +#define s_flash_shd_bytecode_draw_inputs NULL /* #extension GL_ARB_shading_language_include : require @@ -1688,6 +2140,11 @@ layout(location = 0) out vec4 result; layout(set = 2, binding = 0) uniform sampler2D u_image; +layout(set = 3, binding = 0) uniform uniform_block { + vec2 u_texture_size; + int u_alpha_discard; +}; + #line 1 "smooth_uv.shd" vec2 smooth_uv(vec2 uv, vec2 texture_size) @@ -1697,285 +2154,444 @@ vec2 smooth_uv(vec2 uv, vec2 texture_size) pixel = seam + clamp( (pixel - seam) / fwidth(pixel), - 0.5, 0.5); return pixel / texture_size; } -#line 12 0 +#line 17 0 + +vec2 pos; +vec2 screen_uv; + +struct ShaderParams +{ + vec2 view_pos; + vec2 uv; + vec2 uv_min; + vec2 uv_max; + vec2 screen_uv; + vec4 attributes; +}; + #line 1 "shader_stub.shd" - vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) + vec4 shader(vec4 color, ShaderParams params) { - return vec4(mix(color.rgb, params.rgb, params.a), color.a); + return vec4(mix(color.rgb, params.attributes.rgb, params.attributes.a), color.a); } -#line 13 0 - -layout(set = 3, binding = 0) uniform uniform_block { - vec2 u_texture_size; - int u_alpha_discard; -}; +#line 32 0 void main() { vec4 color = texture(u_image, smooth_uv(v_uv, u_texture_size)); - vec2 screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1); - vec4 c = shader(color, v_pos, screen_uv, v_params); + pos = v_pos; + screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1); + ShaderParams sp; + sp.view_pos = pos; + sp.uv = v_uv; + sp.uv_min = vec2(0); + sp.uv_max = vec2(0); + sp.screen_uv = screen_uv; + sp.attributes = v_params; + vec4 c = shader(color, sp); if (u_alpha_discard != 0 && c.a == 0) discard; result = c; } */ -static const uint8_t s_flash_shd_bytecode_blit_content[3364] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x0A, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x67, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, - 0x04, 0x00, 0x09, 0x00, 0x47, 0x4C, 0x5F, 0x41, 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x69, - 0x6E, 0x67, 0x5F, 0x6C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5F, 0x69, 0x6E, 0x63, 0x6C, - 0x75, 0x64, 0x65, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, - 0x45, 0x5F, 0x63, 0x70, 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, - 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x73, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x5F, 0x75, 0x76, 0x28, 0x76, 0x66, - 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x75, 0x76, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, - 0x75, 0x72, 0x65, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x28, 0x76, 0x66, 0x34, 0x3B, 0x76, - 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x34, 0x3B, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x70, 0x6F, 0x73, 0x00, 0x05, 0x00, 0x05, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x73, 0x63, 0x72, 0x65, 0x65, 0x6E, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x73, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x70, 0x69, 0x78, 0x65, 0x6C, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x73, 0x65, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x4A, 0x00, 0x00, 0x00, 0x75, 0x5F, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x4D, 0x00, 0x00, 0x00, 0x76, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x75, 0x6E, 0x69, 0x66, 0x6F, 0x72, 0x6D, 0x5F, 0x62, 0x6C, 0x6F, 0x63, - 0x6B, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x75, 0x5F, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, - 0x06, 0x00, 0x07, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x5F, 0x61, 0x6C, - 0x70, 0x68, 0x61, 0x5F, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x73, 0x63, 0x72, 0x65, 0x65, 0x6E, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x5C, 0x00, 0x00, 0x00, 0x76, 0x5F, 0x70, 0x6F, 0x73, 0x48, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x76, 0x5F, 0x70, 0x6F, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x67, 0x00, 0x00, 0x00, - 0x76, 0x5F, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x73, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x6A, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x6C, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x6E, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x82, 0x00, 0x00, 0x00, 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x4A, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x4A, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x4D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x51, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x51, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x65, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x67, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x82, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x21, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, - 0x17, 0x00, 0x04, 0x00, 0x32, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x39, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x09, 0x00, 0x47, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x49, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x04, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x56, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBF, - 0x2C, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x5F, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x66, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x71, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x4E, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x53, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x5B, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x6A, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x6C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x6E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x48, 0x00, 0x00, 0x00, - 0x4B, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x56, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x46, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x8E, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x68, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x6C, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x6E, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x00, 0x00, 0x39, 0x00, 0x08, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, - 0x6E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x64, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x73, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x05, 0x00, 0x71, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, - 0x75, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x76, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x77, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x39, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, - 0xB4, 0x00, 0x05, 0x00, 0x71, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x7A, 0x00, 0x00, 0x00, - 0x7B, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x78, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x78, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x71, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, - 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x7D, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x7E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x01, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7F, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x82, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x1C, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x2C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0x2D, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x08, 0x00, 0x32, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x08, 0x00, 0x32, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, - 0x3A, 0x00, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x39, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x42, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, - 0x42, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, +#ifndef __EMSCRIPTEN__ +static const uint8_t s_flash_shd_bytecode_blit_content[3872] = { + 0x03,0x02,0x23,0x07,0x00,0x03,0x01,0x00,0x0B,0x00,0x08,0x00,0x94,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0B,0x00,0x06,0x00, + 0x01,0x00,0x00,0x00,0x47,0x4C,0x53,0x4C,0x2E,0x73,0x74,0x64,0x2E,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0F,0x00,0x0A,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, + 0x00,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x5F,0x00,0x00,0x00,0x62,0x00,0x00,0x00, + 0x7A,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xC2,0x01,0x00,0x00, + 0x04,0x00,0x09,0x00,0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x68,0x61,0x64,0x69, + 0x6E,0x67,0x5F,0x6C,0x61,0x6E,0x67,0x75,0x61,0x67,0x65,0x5F,0x69,0x6E,0x63,0x6C, + 0x75,0x64,0x65,0x00,0x04,0x00,0x0A,0x00,0x47,0x4C,0x5F,0x47,0x4F,0x4F,0x47,0x4C, + 0x45,0x5F,0x63,0x70,0x70,0x5F,0x73,0x74,0x79,0x6C,0x65,0x5F,0x6C,0x69,0x6E,0x65, + 0x5F,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x76,0x65,0x00,0x00,0x05,0x00,0x04,0x00, + 0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00, + 0x0C,0x00,0x00,0x00,0x73,0x6D,0x6F,0x6F,0x74,0x68,0x5F,0x75,0x76,0x28,0x76,0x66, + 0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x0A,0x00,0x00,0x00, + 0x75,0x76,0x00,0x00,0x05,0x00,0x06,0x00,0x0B,0x00,0x00,0x00,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x10,0x00,0x00,0x00,0x53,0x68,0x61,0x64,0x65,0x72,0x50,0x61,0x72,0x61,0x6D,0x73, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x76,0x69,0x65,0x77,0x5F,0x70,0x6F,0x73,0x00,0x00,0x00,0x00,0x06,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x06,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x75,0x76,0x5F,0x6D,0x69,0x6E,0x00,0x00, + 0x06,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x75,0x76,0x5F,0x6D, + 0x61,0x78,0x00,0x00,0x06,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75,0x76,0x00,0x00,0x00,0x06,0x00,0x06,0x00, + 0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74, + 0x65,0x73,0x00,0x00,0x05,0x00,0x11,0x00,0x15,0x00,0x00,0x00,0x73,0x68,0x61,0x64, + 0x65,0x72,0x28,0x76,0x66,0x34,0x3B,0x73,0x74,0x72,0x75,0x63,0x74,0x2D,0x53,0x68, + 0x61,0x64,0x65,0x72,0x50,0x61,0x72,0x61,0x6D,0x73,0x2D,0x76,0x66,0x32,0x2D,0x76, + 0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76, + 0x66,0x34,0x31,0x3B,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x13,0x00,0x00,0x00, + 0x63,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x14,0x00,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x73,0x00,0x00,0x05,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x70,0x69,0x78,0x65,0x6C,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x1B,0x00,0x00,0x00, + 0x73,0x65,0x61,0x6D,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x49,0x00,0x00,0x00, + 0x63,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x4D,0x00,0x00,0x00, + 0x75,0x5F,0x69,0x6D,0x61,0x67,0x65,0x00,0x05,0x00,0x04,0x00,0x50,0x00,0x00,0x00, + 0x76,0x5F,0x75,0x76,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x51,0x00,0x00,0x00, + 0x75,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x5F,0x62,0x6C,0x6F,0x63,0x6B,0x00,0x00,0x00, + 0x06,0x00,0x07,0x00,0x51,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x75,0x5F,0x74,0x65, + 0x78,0x74,0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00,0x06,0x00,0x07,0x00, + 0x51,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x75,0x5F,0x61,0x6C,0x70,0x68,0x61,0x5F, + 0x64,0x69,0x73,0x63,0x61,0x72,0x64,0x00,0x05,0x00,0x03,0x00,0x53,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x55,0x00,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x57,0x00,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x5E,0x00,0x00,0x00,0x70,0x6F,0x73,0x00, + 0x05,0x00,0x04,0x00,0x5F,0x00,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x61,0x00,0x00,0x00,0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75, + 0x76,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x62,0x00,0x00,0x00,0x76,0x5F,0x70,0x6F, + 0x73,0x48,0x00,0x00,0x05,0x00,0x03,0x00,0x6A,0x00,0x00,0x00,0x73,0x70,0x00,0x00, + 0x05,0x00,0x05,0x00,0x7A,0x00,0x00,0x00,0x76,0x5F,0x70,0x61,0x72,0x61,0x6D,0x73, + 0x00,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x7D,0x00,0x00,0x00,0x63,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x7E,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x80,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x92,0x00,0x00,0x00,0x72,0x65,0x73,0x75,0x6C,0x74,0x00,0x00, + 0x47,0x00,0x04,0x00,0x4D,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x4D,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x50,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x03,0x00,0x51,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x51,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x51,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x53,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x53,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x5F,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x62,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x7A,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x92,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x21,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x0E,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0F,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x1E,0x00,0x08,0x00,0x10,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x21,0x00,0x05,0x00,0x12,0x00,0x00,0x00, + 0x0E,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0xBF,0x17,0x00,0x04,0x00, + 0x32,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x35,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x35,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x3A,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x3A,0x00,0x00,0x00,0x3B,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x3C,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x19,0x00,0x09,0x00, + 0x4A,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x1B,0x00,0x03,0x00,0x4B,0x00,0x00,0x00,0x4A,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x4C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x4B,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x4C,0x00,0x00,0x00,0x4D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1E,0x00,0x04,0x00, + 0x51,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x52,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x51,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x52,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x35,0x00,0x00,0x00,0x54,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x58,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x5D,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x5D,0x00,0x00,0x00,0x5E,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x5F,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x5D,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x00,0x00,0x80,0x3F,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0x00,0x00,0x80,0xBF,0x2C,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x65,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x35,0x00,0x00,0x00,0x6D,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x35,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2C,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x72,0x00,0x00,0x00,0x71,0x00,0x00,0x00, + 0x71,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x35,0x00,0x00,0x00,0x74,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x35,0x00,0x00,0x00,0x76,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x79,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0E,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x79,0x00,0x00,0x00,0x7A,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x14,0x00,0x02,0x00,0x83,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x84,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x91,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x91,0x00,0x00,0x00,0x92,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00, + 0x49,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x55,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x57,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x6A,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00, + 0x7D,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00, + 0x7E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x4B,0x00,0x00,0x00, + 0x4E,0x00,0x00,0x00,0x4D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x56,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x55,0x00,0x00,0x00, + 0x56,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x58,0x00,0x00,0x00,0x59,0x00,0x00,0x00, + 0x53,0x00,0x00,0x00,0x54,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x5A,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x57,0x00,0x00,0x00, + 0x5A,0x00,0x00,0x00,0x39,0x00,0x06,0x00,0x07,0x00,0x00,0x00,0x5B,0x00,0x00,0x00, + 0x0C,0x00,0x00,0x00,0x55,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x57,0x00,0x05,0x00, + 0x0E,0x00,0x00,0x00,0x5C,0x00,0x00,0x00,0x4E,0x00,0x00,0x00,0x5B,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x49,0x00,0x00,0x00,0x5C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x5F,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x5E,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x63,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x67,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x8E,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x1D,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x68,0x00,0x00,0x00, + 0x66,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x61,0x00,0x00,0x00,0x69,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x6B,0x00,0x00,0x00,0x5E,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x6C,0x00,0x00,0x00,0x6A,0x00,0x00,0x00, + 0x54,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x6C,0x00,0x00,0x00,0x6B,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x6E,0x00,0x00,0x00,0x50,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x6F,0x00,0x00,0x00,0x6A,0x00,0x00,0x00, + 0x6D,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x6F,0x00,0x00,0x00,0x6E,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x6A,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x73,0x00,0x00,0x00,0x72,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x75,0x00,0x00,0x00,0x6A,0x00,0x00,0x00, + 0x74,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x75,0x00,0x00,0x00,0x72,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x77,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x6A,0x00,0x00,0x00, + 0x76,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x78,0x00,0x00,0x00,0x77,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x7B,0x00,0x00,0x00,0x7A,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x0F,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x6A,0x00,0x00,0x00, + 0x36,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x7C,0x00,0x00,0x00,0x7B,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x49,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x7E,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x81,0x00,0x00,0x00,0x6A,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x80,0x00,0x00,0x00,0x81,0x00,0x00,0x00,0x39,0x00,0x06,0x00,0x0E,0x00,0x00,0x00, + 0x82,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x80,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x7D,0x00,0x00,0x00,0x82,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x84,0x00,0x00,0x00,0x85,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x6D,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x35,0x00,0x00,0x00,0x86,0x00,0x00,0x00,0x85,0x00,0x00,0x00, + 0xAB,0x00,0x05,0x00,0x83,0x00,0x00,0x00,0x87,0x00,0x00,0x00,0x86,0x00,0x00,0x00, + 0x54,0x00,0x00,0x00,0xF7,0x00,0x03,0x00,0x89,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFA,0x00,0x04,0x00,0x87,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x89,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x88,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3C,0x00,0x00,0x00, + 0x8A,0x00,0x00,0x00,0x7D,0x00,0x00,0x00,0x3B,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x8B,0x00,0x00,0x00,0x8A,0x00,0x00,0x00,0xB4,0x00,0x05,0x00, + 0x83,0x00,0x00,0x00,0x8C,0x00,0x00,0x00,0x8B,0x00,0x00,0x00,0x71,0x00,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x89,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x89,0x00,0x00,0x00, + 0xF5,0x00,0x07,0x00,0x83,0x00,0x00,0x00,0x8D,0x00,0x00,0x00,0x87,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x8C,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0xF7,0x00,0x03,0x00, + 0x8F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x8D,0x00,0x00,0x00, + 0x8E,0x00,0x00,0x00,0x8F,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x8E,0x00,0x00,0x00, + 0xFC,0x00,0x01,0x00,0xF8,0x00,0x02,0x00,0x8F,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x0E,0x00,0x00,0x00,0x93,0x00,0x00,0x00,0x7D,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x92,0x00,0x00,0x00,0x93,0x00,0x00,0x00,0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00, + 0x36,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x0A,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x0D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x1B,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x0A,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x0B,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1A,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x17,0x00,0x00,0x00, + 0x1A,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1C,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x1D,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x1F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0C,0x00,0x06,0x00, + 0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x1F,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x1B,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x1B,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x1B,0x00,0x00,0x00, + 0x83,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0xD1,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x26,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x88,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x27,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x29,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x50,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x2A,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x1D,0x00,0x00,0x00, + 0x0C,0x00,0x08,0x00,0x07,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2B,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x2A,0x00,0x00,0x00, + 0x81,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x2C,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x2B,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x17,0x00,0x00,0x00,0x2C,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x2D,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0x0B,0x00,0x00,0x00, + 0x88,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x2D,0x00,0x00,0x00, + 0x2E,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0x2F,0x00,0x00,0x00,0x38,0x00,0x01,0x00, + 0x36,0x00,0x05,0x00,0x0E,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x12,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x0F,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x16,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x33,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x4F,0x00,0x08,0x00,0x32,0x00,0x00,0x00,0x34,0x00,0x00,0x00, + 0x33,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x0F,0x00,0x00,0x00,0x37,0x00,0x00,0x00, + 0x14,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00, + 0x38,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x4F,0x00,0x08,0x00,0x32,0x00,0x00,0x00, + 0x39,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x41,0x00,0x06,0x00,0x3C,0x00,0x00,0x00, + 0x3D,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x3B,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x3D,0x00,0x00,0x00, + 0x50,0x00,0x06,0x00,0x32,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00, + 0x3E,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x0C,0x00,0x08,0x00,0x32,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0x34,0x00,0x00,0x00, + 0x39,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3C,0x00,0x00,0x00, + 0x41,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x3B,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x45,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x0E,0x00,0x00,0x00, + 0x46,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x45,0x00,0x00,0x00, + 0x42,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0x46,0x00,0x00,0x00,0x38,0x00,0x01,0x00, }; +#endif +static const char s_flash_shd_bytecode_blit_glsl300_src[1705] = +"#version 300 es\n" +"precision mediump float;\n" +"precision highp int;\n" +"\n" +"struct ShaderParams\n" +"{\n" +" highp vec2 view_pos;\n" +" highp vec2 uv;\n" +" highp vec2 uv_min;\n" +" highp vec2 uv_max;\n" +" highp vec2 screen_uv;\n" +" highp vec4 attributes;\n" +"};\n" +"\n" +"layout(std140) uniform uniform_block\n" +"{\n" +" highp vec2 u_texture_size;\n" +" int u_alpha_discard;\n" +"} _83;\n" +"\n" +"uniform highp sampler2D u_image;\n" +"\n" +"in highp vec2 v_uv;\n" +"in highp vec2 v_pos;\n" +"in highp vec2 v_posH;\n" +"in highp vec4 v_params;\n" +"layout(location = 0) out highp vec4 result;\n" +"highp vec2 pos;\n" +"highp vec2 screen_uv;\n" +"\n" +"highp vec2 smooth_uv(highp vec2 uv, highp vec2 texture_size)\n" +"{\n" +" highp vec2 pixel = uv * texture_size;\n" +" highp vec2 seam = floor(pixel + vec2(0.5));\n" +" pixel = seam + clamp((pixel - seam) / fwidth(pixel), vec2(-0.5), vec2(0.5));\n" +" return pixel / texture_size;\n" +"}\n" +"\n" +"highp vec4 shader(highp vec4 color, ShaderParams params)\n" +"{\n" +" return vec4(mix(color.xyz, params.attributes.xyz, vec3(params.attributes.w)), color.w);\n" +"}\n" +"\n" +"void main()\n" +"{\n" +" highp vec2 param = v_uv;\n" +" highp vec2 param_1 = _83.u_texture_size;\n" +" highp vec4 color = texture(u_image, smooth_uv(param, param_1));\n" +" pos = v_pos;\n" +" screen_uv = ((v_posH + vec2(1.0, -1.0)) * 0.5) * vec2(1.0, -1.0);\n" +" ShaderParams sp;\n" +" sp.view_pos = pos;\n" +" sp.uv = v_uv;\n" +" sp.uv_min = vec2(0.0);\n" +" sp.uv_max = vec2(0.0);\n" +" sp.screen_uv = screen_uv;\n" +" sp.attributes = v_params;\n" +" highp vec4 param_2 = color;\n" +" ShaderParams param_3 = sp;\n" +" highp vec4 c = shader(param_2, param_3);\n" +" bool _135 = _83.u_alpha_discard != 0;\n" +" bool _141;\n" +" if (_135)\n" +" {\n" +" _141 = c.w == 0.0;\n" +" }\n" +" else\n" +" {\n" +" _141 = _135;\n" +" }\n" +" if (_141)\n" +" {\n" +" discard;\n" +" }\n" +" result = c;\n" +"}\n" +"\n" +""; static const char* s_flash_shd_bytecode_blit_image_names[1] = { "u_image", }; +static int s_flash_shd_bytecode_blit_image_binding_slots[1] = { 0, }; static CF_ShaderUniformInfo s_flash_shd_bytecode_blit_uniforms[1] = { - { - .block_name = "uniform_block", - .block_index = 0, - .block_size = 16, - .num_members = 2, - }, + { + .block_name = "uniform_block", + .block_index = 0, + .block_size = 16, + .num_members = 2, + }, }; static CF_ShaderUniformMemberInfo s_flash_shd_bytecode_blit_uniform_members[2] = { - { - .name = "u_texture_size", - .type = CF_SHADER_INFO_TYPE_FLOAT2, - .offset = 0, - .array_length = 1, - }, - { - .name = "u_alpha_discard", - .type = CF_SHADER_INFO_TYPE_SINT, - .offset = 8, - .array_length = 1, - }, -}; -static CF_ShaderInputInfo* s_flash_shd_bytecode_blit_inputs = NULL; -static const CF_ShaderBytecode s_flash_shd_bytecode_blit = { - .content = s_flash_shd_bytecode_blit_content, - .size = 3364, - .shader_info = { - .num_samplers = 1, - .num_storage_textures = 0, - .num_storage_buffers = 0, - .num_images = 1, - .image_names = s_flash_shd_bytecode_blit_image_names, - .num_uniforms = 1, - .uniforms = s_flash_shd_bytecode_blit_uniforms, - .num_uniform_members = 2, - .uniform_members = s_flash_shd_bytecode_blit_uniform_members, - .num_inputs = 0, - .inputs = s_flash_shd_bytecode_blit_inputs, - }, + { + .name = "u_texture_size", + .type = CF_SHADER_INFO_TYPE_FLOAT2, + .offset = 0, + .array_length = 1, + }, + { + .name = "u_alpha_discard", + .type = CF_SHADER_INFO_TYPE_SINT, + .offset = 8, + .array_length = 1, + }, }; +#define s_flash_shd_bytecode_blit_inputs NULL static const CF_DrawShaderBytecode s_flash_shd_bytecode = { - .draw_shader = s_flash_shd_bytecode_draw, - .blit_shader = s_flash_shd_bytecode_blit, + .draw_shader = { +#ifndef __EMSCRIPTEN__ + .content = s_flash_shd_bytecode_draw_content, + .size = 21720, +#endif + .glsl300_src = s_flash_shd_bytecode_draw_glsl300_src, + .glsl300_src_size = 9885, + .shader_info = { + .num_samplers = 1, + .num_storage_textures = 0, + .num_storage_buffers = 0, + .num_readwrite_storage_textures = 0, + .num_readwrite_storage_buffers = 0, + .num_images = 1, + .image_names = s_flash_shd_bytecode_draw_image_names, + .image_binding_slots = s_flash_shd_bytecode_draw_image_binding_slots, + .num_uniforms = 1, + .uniforms = s_flash_shd_bytecode_draw_uniforms, + .num_uniform_members = 3, + .uniform_members = s_flash_shd_bytecode_draw_uniform_members, + .num_inputs = 0, + .inputs = s_flash_shd_bytecode_draw_inputs, + }, + }, + .blit_shader = { +#ifndef __EMSCRIPTEN__ + .content = s_flash_shd_bytecode_blit_content, + .size = 3872, +#endif + .glsl300_src = s_flash_shd_bytecode_blit_glsl300_src, + .glsl300_src_size = 1704, + .shader_info = { + .num_samplers = 1, + .num_storage_textures = 0, + .num_storage_buffers = 0, + .num_readwrite_storage_textures = 0, + .num_readwrite_storage_buffers = 0, + .num_images = 1, + .image_names = s_flash_shd_bytecode_blit_image_names, + .image_binding_slots = s_flash_shd_bytecode_blit_image_binding_slots, + .num_uniforms = 1, + .uniforms = s_flash_shd_bytecode_blit_uniforms, + .num_uniform_members = 2, + .uniform_members = s_flash_shd_bytecode_blit_uniform_members, + .num_inputs = 0, + .inputs = s_flash_shd_bytecode_blit_inputs, + }, + }, }; diff --git a/samples/sprite_shatter.cpp b/samples/sprite_shatter.cpp index 5a31ca663..8733d81fe 100644 --- a/samples/sprite_shatter.cpp +++ b/samples/sprite_shatter.cpp @@ -4,7 +4,7 @@ #define STR(X) #X const char* shader_str = STR( -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { return texture(u_image, smooth_uv(v_uv, u_texture_size)); } @@ -52,20 +52,17 @@ void init(int w, int h) CF_VertexAttribute* attrs = NULL; cf_array_fit(attrs, 32); - add_vertex_attribute(attrs, "in_pos", CF_VERTEX_FORMAT_FLOAT2, CF_OFFSET_OF(CF_Vertex, p)); - add_vertex_attribute(attrs, "in_posH", CF_VERTEX_FORMAT_FLOAT2, CF_OFFSET_OF(CF_Vertex, posH)); - add_vertex_attribute(attrs, "in_n", CF_VERTEX_FORMAT_INT, CF_OFFSET_OF(CF_Vertex, n)); - add_vertex_attribute(attrs, "in_ab", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[0])); - add_vertex_attribute(attrs, "in_cd", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[2])); - add_vertex_attribute(attrs, "in_ef", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[4])); - add_vertex_attribute(attrs, "in_gh", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[6])); - add_vertex_attribute(attrs, "in_uv", CF_VERTEX_FORMAT_FLOAT2, CF_OFFSET_OF(CF_Vertex, uv)); - add_vertex_attribute(attrs, "in_col", CF_VERTEX_FORMAT_UBYTE4_NORM, CF_OFFSET_OF(CF_Vertex, color)); - add_vertex_attribute(attrs, "in_radius", CF_VERTEX_FORMAT_FLOAT, CF_OFFSET_OF(CF_Vertex, radius)); - add_vertex_attribute(attrs, "in_stroke", CF_VERTEX_FORMAT_FLOAT, CF_OFFSET_OF(CF_Vertex, stroke)); - add_vertex_attribute(attrs, "in_aa", CF_VERTEX_FORMAT_FLOAT, CF_OFFSET_OF(CF_Vertex, aa)); - add_vertex_attribute(attrs, "in_params", CF_VERTEX_FORMAT_UBYTE4_NORM, CF_OFFSET_OF(CF_Vertex, type)); - add_vertex_attribute(attrs, "in_user_params", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, attributes)); + add_vertex_attribute(attrs, "in_pos_uv", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, p)); + add_vertex_attribute(attrs, "in_n", CF_VERTEX_FORMAT_INT, CF_OFFSET_OF(CF_Vertex, n)); + add_vertex_attribute(attrs, "in_ab", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[0])); + add_vertex_attribute(attrs, "in_cd", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[2])); + add_vertex_attribute(attrs, "in_ef", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[4])); + add_vertex_attribute(attrs, "in_gh", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[6])); + add_vertex_attribute(attrs, "in_col", CF_VERTEX_FORMAT_UBYTE4_NORM, CF_OFFSET_OF(CF_Vertex, color)); + add_vertex_attribute(attrs, "in_shape", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, radius)); + add_vertex_attribute(attrs, "in_blend_posH", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, alpha)); + add_vertex_attribute(attrs, "in_user", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, attributes)); + add_vertex_attribute(attrs, "in_uv_bounds", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, uv_bounds)); draw.mesh = cf_make_mesh(CF_MB * 5, attrs, cf_array_count(attrs), sizeof(CF_Vertex)); draw.verts = NULL; @@ -222,13 +219,13 @@ void draw_sprite_chunks(SpriteChunk* chunks) verts[3].aa = false; verts[4].aa = false; verts[5].aa = false; - - verts[0].alpha = chunk->opacity; - verts[1].alpha = chunk->opacity; - verts[2].alpha = chunk->opacity; - verts[3].alpha = chunk->opacity; - verts[4].alpha = chunk->opacity; - verts[5].alpha = chunk->opacity; + + verts[0].alpha = chunk->opacity / 255.0f; + verts[1].alpha = chunk->opacity / 255.0f; + verts[2].alpha = chunk->opacity / 255.0f; + verts[3].alpha = chunk->opacity / 255.0f; + verts[4].alpha = chunk->opacity / 255.0f; + verts[5].alpha = chunk->opacity / 255.0f; verts[0].color = chunk->color; verts[1].color = chunk->color; @@ -269,7 +266,7 @@ int main(int argc, char *argv[]) CF_Sprite demo_sprite = cf_make_demo_sprite(); cf_sprite_play(&demo_sprite, "spin"); - int frame_count = cf_array_count(demo_sprite.animation->frames); + int frame_count = cf_sprite_frame_count(&demo_sprite); demo_sprite.scale = cf_v2(4, 4); CF_Canvas app_canvas = cf_app_get_canvas(); diff --git a/samples/sprite_slice.cpp b/samples/sprite_slice.cpp index ddda57381..34d9453ae 100644 --- a/samples/sprite_slice.cpp +++ b/samples/sprite_slice.cpp @@ -4,7 +4,7 @@ #define STR(X) #X const char* shader_str = STR( -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { return texture(u_image, smooth_uv(v_uv, u_texture_size)); } @@ -53,20 +53,17 @@ void init(int w, int h) CF_VertexAttribute* attrs = NULL; cf_array_fit(attrs, 32); - add_vertex_attribute(attrs, "in_pos", CF_VERTEX_FORMAT_FLOAT2, CF_OFFSET_OF(CF_Vertex, p)); - add_vertex_attribute(attrs, "in_posH", CF_VERTEX_FORMAT_FLOAT2, CF_OFFSET_OF(CF_Vertex, posH)); - add_vertex_attribute(attrs, "in_n", CF_VERTEX_FORMAT_INT, CF_OFFSET_OF(CF_Vertex, n)); - add_vertex_attribute(attrs, "in_ab", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[0])); - add_vertex_attribute(attrs, "in_cd", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[2])); - add_vertex_attribute(attrs, "in_ef", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[4])); - add_vertex_attribute(attrs, "in_gh", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[6])); - add_vertex_attribute(attrs, "in_uv", CF_VERTEX_FORMAT_FLOAT2, CF_OFFSET_OF(CF_Vertex, uv)); - add_vertex_attribute(attrs, "in_col", CF_VERTEX_FORMAT_UBYTE4_NORM, CF_OFFSET_OF(CF_Vertex, color)); - add_vertex_attribute(attrs, "in_radius", CF_VERTEX_FORMAT_FLOAT, CF_OFFSET_OF(CF_Vertex, radius)); - add_vertex_attribute(attrs, "in_stroke", CF_VERTEX_FORMAT_FLOAT, CF_OFFSET_OF(CF_Vertex, stroke)); - add_vertex_attribute(attrs, "in_aa", CF_VERTEX_FORMAT_FLOAT, CF_OFFSET_OF(CF_Vertex, aa)); - add_vertex_attribute(attrs, "in_params", CF_VERTEX_FORMAT_UBYTE4_NORM, CF_OFFSET_OF(CF_Vertex, type)); - add_vertex_attribute(attrs, "in_user_params", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, attributes)); + add_vertex_attribute(attrs, "in_pos_uv", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, p)); + add_vertex_attribute(attrs, "in_n", CF_VERTEX_FORMAT_INT, CF_OFFSET_OF(CF_Vertex, n)); + add_vertex_attribute(attrs, "in_ab", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[0])); + add_vertex_attribute(attrs, "in_cd", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[2])); + add_vertex_attribute(attrs, "in_ef", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[4])); + add_vertex_attribute(attrs, "in_gh", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, shape[6])); + add_vertex_attribute(attrs, "in_col", CF_VERTEX_FORMAT_UBYTE4_NORM, CF_OFFSET_OF(CF_Vertex, color)); + add_vertex_attribute(attrs, "in_shape", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, radius)); + add_vertex_attribute(attrs, "in_blend_posH", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, alpha)); + add_vertex_attribute(attrs, "in_user", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, attributes)); + add_vertex_attribute(attrs, "in_uv_bounds", CF_VERTEX_FORMAT_FLOAT4, CF_OFFSET_OF(CF_Vertex, uv_bounds)); draw.mesh = cf_make_mesh(CF_MB * 5, attrs, cf_array_count(attrs), sizeof(CF_Vertex)); draw.verts = NULL; @@ -201,7 +198,7 @@ void draw_sprite_chunks(SpriteChunk* chunks) vert->posH = cf_mul_m32_v2(mvp, vert->p); vert->uv = chunk->uvs[vertex]; vert->aa = false; - vert->alpha = chunk->opacity; + vert->alpha = chunk->opacity / 255.0f; vert->color = chunk->color; } @@ -240,7 +237,7 @@ int main(int argc, char *argv[]) CF_Sprite demo_sprite = cf_make_demo_sprite(); cf_sprite_play(&demo_sprite, "spin"); - int frame_count = cf_array_count(demo_sprite.animation->frames); + int frame_count = cf_sprite_frame_count(&demo_sprite); demo_sprite.scale = cf_v2(8, 8); CF_Canvas app_canvas = cf_app_get_canvas(); diff --git a/samples/stencil_pie_chart.c b/samples/stencil_pie_chart.c index 931f7af95..863238775 100644 --- a/samples/stencil_pie_chart.c +++ b/samples/stencil_pie_chart.c @@ -6,7 +6,7 @@ void update() CF_RenderState state = cf_render_state_defaults(); state.stencil.enabled = true; - cf_draw_push_antialias(false); + cf_draw_push_shape_aa(0); cf_draw_push_alpha_discard(true); // Render a full white circle. @@ -60,7 +60,7 @@ void update() cf_draw_pop_render_state(); // Draw yellow border. - cf_draw_push_antialias(true); + cf_draw_push_shape_aa(1.5f); cf_draw_push_alpha_discard(true); cf_draw_push_color(cf_color_yellow()); cf_draw_circle2(cf_v2(0,0), 100, 3); diff --git a/samples/text_drawing.cpp b/samples/text_drawing.cpp index b61840dcd..92a68c70b 100644 --- a/samples/text_drawing.cpp +++ b/samples/text_drawing.cpp @@ -23,7 +23,7 @@ int main(int argc, char* argv[]) { make_app("Text Drawing", 0, 0, 0, 640, 480, CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT | CF_APP_OPTIONS_RESIZABLE_BIT, argv[0]); - draw_push_antialias(true); + draw_push_shape_aa(1.5f); make_font_from_memory(proggy_data, proggy_sz, "ProggyClean"); set_fixed_timestep(); int draw_calls = 0; diff --git a/samples/timestep.cpp b/samples/timestep.cpp index bdec11805..7ff400180 100644 --- a/samples/timestep.cpp +++ b/samples/timestep.cpp @@ -31,7 +31,7 @@ int main(int argc, char* argv[]) s = String::fmt("dt %f", CF_DELTA_TIME); draw_text(s.c_str(), V2(-40,-15)); - app_draw_onto_screen(); + app_draw_onto_screen(true); } destroy_app(); diff --git a/samples/waves.cpp b/samples/waves.cpp index 210aa26ce..8c1a052ef 100644 --- a/samples/waves.cpp +++ b/samples/waves.cpp @@ -23,7 +23,7 @@ int main(int argc, char* argv[]) app_init_imgui(); CF_Png png; - cf_png_cache_load("/noise.png", &png); + cf_custom_sprite_load_png("/noise.png", &png); CF_ASSERT(png.w == 128); CF_ASSERT(png.h == 128); @@ -85,7 +85,7 @@ int main(int argc, char* argv[]) draw_set_uniform("show_noise", show_noise ? 1.0f : 0.0f); draw_set_texture("water_tex", canvas_get_target(offscreen)); draw_set_texture("noise_tex", tex); - draw_push_antialias(false); + draw_push_shape_aa(0); draw_box_fill(make_aabb(V2(0,0), 640, 480)); app_draw_onto_screen(true); diff --git a/samples/waves_data/waves.shd b/samples/waves_data/waves.shd index 900d64bab..bf990242e 100644 --- a/samples/waves_data/waves.shd +++ b/samples/waves_data/waves.shd @@ -7,14 +7,14 @@ layout (set = 3, binding = 1) uniform shd_uniforms { float show_noise; }; -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { - vec2 noise_uv = screen_uv; + vec2 noise_uv = params.screen_uv; noise_uv.x *= 640.0 / 128.0; noise_uv.y *= 480.0 / 128.0; vec4 noise_c = vec4(texture(noise_tex, noise_uv * 0.5 + time).r - 0.5); vec2 offset = vec2(1.0/640.0, 1.0/480.0) * noise_c.r * amplitude; - vec4 c = texture(water_tex, screen_uv + offset); + vec4 c = texture(water_tex, params.screen_uv + offset); c = show_noise > 0 ? noise_c + vec4(0.5) : c; return c; } diff --git a/samples/waves_data/waves_shd.h b/samples/waves_data/waves_shd.h index cc4f98ef3..a1e001efe 100644 --- a/samples/waves_data/waves_shd.h +++ b/samples/waves_data/waves_shd.h @@ -5,22 +5,17 @@ #line 1 0 -layout(location = 0) in vec2 v_pos; +layout(location = 0) in vec4 v_pos_uv; layout(location = 1) in flat int v_n; layout(location = 2) in vec4 v_ab; layout(location = 3) in vec4 v_cd; layout(location = 4) in vec4 v_ef; layout(location = 5) in vec4 v_gh; -layout(location = 6) in vec2 v_uv; -layout(location = 7) in vec4 v_col; -layout(location = 8) in float v_radius; -layout(location = 9) in float v_stroke; -layout(location = 10) in float v_aa; -layout(location = 11) in float v_type; -layout(location = 12) in float v_alpha; -layout(location = 13) in float v_fill; -layout(location = 14) in vec2 v_posH; -layout(location = 15) in vec4 v_user; +layout(location = 6) in vec4 v_col; +layout(location = 7) in vec4 v_shape; +layout(location = 8) in vec4 v_blend_posH; +layout(location = 9) in vec4 v_user; +layout(location = 10) in vec4 v_uv_bounds; layout(location = 0) out vec4 result; @@ -29,8 +24,26 @@ layout(set = 2, binding = 0) uniform sampler2D u_image; layout(set = 3, binding = 0) uniform uniform_block { vec2 u_texture_size; int u_alpha_discard; + int u_use_smooth_uv; }; + +vec2 pts[8]; + + + +vec2 v_pos; +vec2 v_uv; +vec2 v_posH; +float v_radius; +float v_stroke; +float v_aa; +float v_type; +float v_alpha; +float v_fill; +vec2 pos; +vec2 screen_uv; + #line 1 "blend.shd" @@ -95,7 +108,7 @@ vec4 softlight(vec4 base, vec4 blend) { return vec4(softlight(base.rgb, blend.rgb), base.a); } -#line 29 0 +#line 42 0 #line 1 "gamma.shd" vec4 gamma(vec4 c) @@ -107,7 +120,7 @@ vec4 de_gamma(vec4 c) { return vec4(pow(abs(c.rgb), vec3(2.2)), c.a); } -#line 30 0 +#line 43 0 #line 1 "smooth_uv.shd" vec2 smooth_uv(vec2 uv, vec2 texture_size) @@ -117,7 +130,7 @@ vec2 smooth_uv(vec2 uv, vec2 texture_size) pixel = seam + clamp( (pixel - seam) / fwidth(pixel), - 0.5, 0.5); return pixel / texture_size; } -#line 31 0 +#line 44 0 #line 1 "distance.shd" float safe_div(float a, float b) @@ -257,7 +270,18 @@ float distance_polygon(vec2 p, vec2[8] v, int N) return s * sqrt(d); } -#line 32 0 +#line 45 0 + +struct ShaderParams +{ + vec2 view_pos; + vec2 uv; + vec2 uv_min; + vec2 uv_max; + vec2 screen_uv; + vec4 attributes; +}; + #line 1 "shader_stub.shd" layout(set = 2, binding = 1) uniform sampler2D water_tex; layout(set = 2, binding = 2) uniform sampler2D noise_tex; @@ -268,37 +292,46 @@ layout(set = 3, binding = 1) uniform shd_uniforms { float show_noise; }; -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { - vec2 noise_uv = screen_uv; + vec2 noise_uv = params.screen_uv; noise_uv.x *= 640.0 / 128.0; noise_uv.y *= 480.0 / 128.0; vec4 noise_c = vec4(texture(noise_tex, noise_uv * 0.5 + time).r - 0.5); vec2 offset = vec2(1.0 / 640.0, 1.0 / 480.0) * noise_c.r * amplitude; - vec4 c = texture(water_tex, screen_uv + offset); + vec4 c = texture(water_tex, params.screen_uv + offset); c = show_noise > 0 ? noise_c + vec4(0.5) : c; return c; } -#line 33 0 - - -vec2 pts[8]; +#line 57 0 void main() { - bool is_sprite = v_type >= (0.0 / 255.0) && v_type < (0.5 / 255.0); - bool is_text = v_type > (0.5 / 255.0) && v_type < (1.5 / 255.0); - bool is_box = v_type > (1.5 / 255.0) && v_type < (2.5 / 255.0); - bool is_seg = v_type > (2.5 / 255.0) && v_type < (3.5 / 255.0); - bool is_tri = v_type > (3.5 / 255.0) && v_type < (4.5 / 255.0); - bool is_tri_sdf = v_type > (4.5 / 255.0) && v_type < (5.5 / 255.0); - bool is_poly = v_type > (5.5 / 255.0) && v_type < (6.5 / 255.0); + + v_pos = v_pos_uv.xy; + v_uv = v_pos_uv.zw; + v_radius = v_shape.x; + v_stroke = v_shape.y; + v_aa = v_shape.z; + v_type = v_shape.w; + v_alpha = v_blend_posH.x; + v_fill = v_blend_posH.y; + v_posH = v_blend_posH.zw; + + bool is_sprite = v_type >= 0.0 && v_type < 0.5; + bool is_text = v_type > 0.5 && v_type < 1.5; + bool is_box = v_type > 1.5 && v_type < 2.5; + bool is_seg = v_type > 2.5 && v_type < 3.5; + bool is_tri = v_type > 3.5 && v_type < 4.5; + bool is_tri_sdf = v_type > 4.5 && v_type < 5.5; + bool is_poly = v_type > 5.5 && v_type < 6.5; vec4 c = vec4(0); - c = ! (is_sprite && is_text) ? de_gamma(texture(u_image, smooth_uv(v_uv, u_texture_size))) : c; - c = is_sprite ? gamma(c) : c; - c = is_text ? v_col * c.a : c; + vec2 uv = u_use_smooth_uv == 0 ? smooth_uv(v_uv, u_texture_size) : v_uv; + vec4 tex_c = de_gamma(texture(u_image, uv)); + c = is_sprite ? gamma(tex_c) : c; + c = is_text ? v_col * tex_c.a : c; c = is_tri ? v_col : c; @@ -324,1474 +357,1919 @@ void main() c = (! is_sprite && ! is_text && ! is_tri) ? sdf(c, v_col, d - v_radius) : c; c *= v_alpha; - vec2 screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1); - c = shader(c, v_pos, screen_uv, v_user); + pos = v_pos; + screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1); + ShaderParams sp; + sp.view_pos = pos; + sp.uv = v_uv; + sp.uv_min = v_uv_bounds.xy; + sp.uv_max = v_uv_bounds.zw; + sp.screen_uv = screen_uv; + sp.attributes = v_user; + c = shader(c, sp); if (u_alpha_discard != 0 && c.a == 0) discard; result = c; } */ -static const uint8_t s_waves_shd_bytecode_draw_content[22272] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0xA4, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x16, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, - 0x0E, 0x01, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0xD2, 0x02, 0x00, 0x00, 0xF2, 0x02, 0x00, 0x00, - 0x03, 0x03, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, 0x4F, 0x03, 0x00, 0x00, - 0x58, 0x03, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x75, 0x03, 0x00, 0x00, 0x81, 0x03, 0x00, 0x00, - 0x86, 0x03, 0x00, 0x00, 0x8C, 0x03, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, - 0x04, 0x00, 0x09, 0x00, 0x47, 0x4C, 0x5F, 0x41, 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x69, - 0x6E, 0x67, 0x5F, 0x6C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5F, 0x69, 0x6E, 0x63, 0x6C, - 0x75, 0x64, 0x65, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, - 0x45, 0x5F, 0x63, 0x70, 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, - 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x67, 0x61, 0x6D, 0x6D, 0x61, 0x28, 0x76, 0x66, 0x34, 0x3B, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x64, 0x65, 0x5F, 0x67, 0x61, 0x6D, 0x6D, 0x61, 0x28, 0x76, 0x66, 0x34, - 0x3B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x07, 0x00, 0x15, 0x00, 0x00, 0x00, 0x73, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x5F, 0x75, - 0x76, 0x28, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x75, 0x76, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, - 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x73, 0x61, 0x66, 0x65, 0x5F, 0x64, 0x69, 0x76, - 0x28, 0x66, 0x31, 0x3B, 0x66, 0x31, 0x3B, 0x00, 0x05, 0x00, 0x03, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x73, 0x61, 0x66, 0x65, 0x5F, 0x6C, 0x65, 0x6E, - 0x28, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x23, 0x00, 0x00, 0x00, 0x73, 0x6B, 0x65, 0x77, - 0x28, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x28, 0x00, 0x00, 0x00, 0x64, 0x65, 0x74, 0x32, - 0x28, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x73, 0x64, 0x66, 0x5F, - 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x28, 0x66, 0x31, 0x3B, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x2B, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x73, 0x64, 0x66, 0x28, 0x76, 0x66, 0x34, 0x3B, 0x76, 0x66, 0x34, 0x3B, 0x66, 0x31, 0x3B, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x36, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x74, - 0x61, 0x6E, 0x63, 0x65, 0x5F, 0x61, 0x61, 0x62, 0x62, 0x28, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, - 0x32, 0x3B, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x34, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x35, 0x00, 0x00, 0x00, 0x68, 0x65, 0x00, 0x00, 0x05, 0x00, 0x0A, 0x00, - 0x3D, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x5F, 0x62, 0x6F, 0x78, - 0x28, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, - 0x3B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x39, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x3B, 0x00, 0x00, 0x00, 0x68, 0x65, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x3C, 0x00, 0x00, 0x00, - 0x75, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x43, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x74, - 0x61, 0x6E, 0x63, 0x65, 0x5F, 0x73, 0x65, 0x67, 0x6D, 0x65, 0x6E, 0x74, 0x28, 0x76, 0x66, 0x32, - 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x41, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x42, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x0B, 0x00, 0x49, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, - 0x5F, 0x74, 0x72, 0x69, 0x61, 0x6E, 0x67, 0x6C, 0x65, 0x28, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, - 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x46, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x47, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x48, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0A, 0x00, - 0x55, 0x00, 0x00, 0x00, 0x64, 0x69, 0x73, 0x74, 0x61, 0x6E, 0x63, 0x65, 0x5F, 0x70, 0x6F, 0x6C, - 0x79, 0x67, 0x6F, 0x6E, 0x28, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x5B, 0x38, 0x5D, 0x3B, - 0x69, 0x31, 0x3B, 0x00, 0x05, 0x00, 0x03, 0x00, 0x52, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x53, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, 0x5C, 0x00, 0x00, 0x00, - 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x28, 0x76, 0x66, 0x34, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x76, - 0x66, 0x32, 0x3B, 0x76, 0x66, 0x34, 0x3B, 0x00, 0x05, 0x00, 0x04, 0x00, 0x58, 0x00, 0x00, 0x00, - 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x70, 0x6F, 0x73, 0x00, 0x05, 0x00, 0x05, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x73, 0x63, 0x72, 0x65, - 0x65, 0x6E, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x73, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x7C, 0x00, 0x00, 0x00, - 0x70, 0x69, 0x78, 0x65, 0x6C, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x73, 0x65, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xA5, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0xCE, 0x00, 0x00, 0x00, 0x76, 0x5F, 0x73, 0x74, - 0x72, 0x6F, 0x6B, 0x65, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xD3, 0x00, 0x00, 0x00, - 0x77, 0x69, 0x72, 0x65, 0x5F, 0x64, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xD4, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0xD7, 0x00, 0x00, 0x00, - 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x5F, 0x61, 0x61, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xDA, 0x00, 0x00, 0x00, 0x76, 0x5F, 0x61, 0x61, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0xE0, 0x00, 0x00, 0x00, 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x5F, 0x6E, 0x6F, 0x5F, 0x61, 0x61, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x66, 0x69, 0x6C, 0x6C, - 0x5F, 0x61, 0x61, 0x00, 0x05, 0x00, 0x05, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x66, 0x69, 0x6C, 0x6C, - 0x5F, 0x6E, 0x6F, 0x5F, 0x61, 0x61, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xFA, 0x00, 0x00, 0x00, - 0x73, 0x74, 0x72, 0x6F, 0x6B, 0x65, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x02, 0x01, 0x00, 0x00, - 0x66, 0x69, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0B, 0x01, 0x00, 0x00, - 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0E, 0x01, 0x00, 0x00, - 0x76, 0x5F, 0x66, 0x69, 0x6C, 0x6C, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x15, 0x01, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x29, 0x01, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x2B, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x3E, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x43, 0x01, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x47, 0x01, 0x00, 0x00, 0x70, 0x61, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x4B, 0x01, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x52, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x53, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x55, 0x01, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x5D, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x61, 0x01, 0x00, 0x00, 0x65, 0x30, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x65, 0x01, 0x00, 0x00, 0x65, 0x31, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x69, 0x01, 0x00, 0x00, - 0x65, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x6D, 0x01, 0x00, 0x00, 0x76, 0x30, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x71, 0x01, 0x00, 0x00, 0x76, 0x31, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x75, 0x01, 0x00, 0x00, 0x76, 0x32, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x79, 0x01, 0x00, 0x00, - 0x70, 0x71, 0x30, 0x00, 0x05, 0x00, 0x04, 0x00, 0x82, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x83, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x88, 0x01, 0x00, 0x00, 0x70, 0x71, 0x31, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x91, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x92, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x97, 0x01, 0x00, 0x00, 0x70, 0x71, 0x32, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xA0, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xA1, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0xA6, 0x01, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xA7, 0x01, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xA9, 0x01, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xAC, 0x01, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xB1, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xB3, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xBC, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xBE, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xC8, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xCA, 0x01, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xDA, 0x01, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0xE5, 0x01, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0xE6, 0x01, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xE7, 0x01, 0x00, 0x00, - 0x6A, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xF3, 0x01, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0xFB, 0x01, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x01, 0x02, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x15, 0x02, 0x00, 0x00, - 0x63, 0x6F, 0x6E, 0x64, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x44, 0x02, 0x00, 0x00, - 0x6E, 0x6F, 0x69, 0x73, 0x65, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x50, 0x02, 0x00, 0x00, 0x6E, 0x6F, 0x69, 0x73, 0x65, 0x5F, 0x63, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x54, 0x02, 0x00, 0x00, 0x6E, 0x6F, 0x69, 0x73, 0x65, 0x5F, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x06, 0x00, 0x58, 0x02, 0x00, 0x00, 0x73, 0x68, 0x64, 0x5F, 0x75, 0x6E, 0x69, 0x66, - 0x6F, 0x72, 0x6D, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x58, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x61, 0x6D, 0x70, 0x6C, 0x69, 0x74, 0x75, 0x64, 0x65, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x05, 0x00, 0x58, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x74, 0x69, 0x6D, 0x65, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x58, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x73, 0x68, 0x6F, 0x77, 0x5F, 0x6E, 0x6F, 0x69, 0x73, 0x65, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x5A, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x64, 0x02, 0x00, 0x00, - 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x6E, 0x02, 0x00, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x6F, 0x02, 0x00, 0x00, 0x77, 0x61, 0x74, 0x65, - 0x72, 0x5F, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x86, 0x02, 0x00, 0x00, - 0x69, 0x73, 0x5F, 0x73, 0x70, 0x72, 0x69, 0x74, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x87, 0x02, 0x00, 0x00, 0x76, 0x5F, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x90, 0x02, 0x00, 0x00, 0x69, 0x73, 0x5F, 0x74, 0x65, 0x78, 0x74, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x99, 0x02, 0x00, 0x00, 0x69, 0x73, 0x5F, 0x62, 0x6F, 0x78, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xA2, 0x02, 0x00, 0x00, 0x69, 0x73, 0x5F, 0x73, 0x65, 0x67, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xAB, 0x02, 0x00, 0x00, 0x69, 0x73, 0x5F, 0x74, 0x72, 0x69, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0xB4, 0x02, 0x00, 0x00, 0x69, 0x73, 0x5F, 0x74, 0x72, 0x69, 0x5F, 0x73, 0x64, 0x66, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0xBD, 0x02, 0x00, 0x00, 0x69, 0x73, 0x5F, 0x70, 0x6F, 0x6C, 0x79, 0x00, - 0x05, 0x00, 0x03, 0x00, 0xC6, 0x02, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xCF, 0x02, 0x00, 0x00, 0x75, 0x5F, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xD2, 0x02, 0x00, 0x00, 0x76, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0xD3, 0x02, 0x00, 0x00, 0x75, 0x6E, 0x69, 0x66, 0x6F, 0x72, 0x6D, 0x5F, 0x62, 0x6C, 0x6F, 0x63, - 0x6B, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0xD3, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x75, 0x5F, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, - 0x06, 0x00, 0x07, 0x00, 0xD3, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x5F, 0x61, 0x6C, - 0x70, 0x68, 0x61, 0x5F, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x00, 0x05, 0x00, 0x03, 0x00, - 0xD5, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xD6, 0x02, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xD8, 0x02, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xDE, 0x02, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xE7, 0x02, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0xF2, 0x02, 0x00, 0x00, - 0x76, 0x5F, 0x63, 0x6F, 0x6C, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0xFF, 0x02, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x03, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x70, 0x6F, - 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x61, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x05, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x63, 0x64, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x06, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x08, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0B, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x0E, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x16, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x18, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x1B, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x20, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x22, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x25, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x2E, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x30, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x33, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x36, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x3F, 0x03, 0x00, 0x00, 0x70, 0x74, 0x73, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x4F, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x65, 0x66, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x58, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x67, 0x68, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x61, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x6E, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x62, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x64, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x66, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x75, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x78, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x7A, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x7C, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x81, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x61, 0x6C, 0x70, 0x68, 0x61, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x85, 0x03, 0x00, 0x00, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6E, 0x5F, 0x75, - 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x86, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x70, 0x6F, - 0x73, 0x48, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8C, 0x03, 0x00, 0x00, 0x76, 0x5F, 0x75, 0x73, - 0x65, 0x72, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8D, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x8F, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x91, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x93, 0x03, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, - 0x6D, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xCE, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0B, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x54, 0x02, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x54, 0x02, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x58, 0x02, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x58, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x58, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5A, 0x02, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x5A, 0x02, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x6F, 0x02, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x6F, 0x02, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x87, 0x02, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0xCF, 0x02, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0xCF, 0x02, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0xD2, 0x02, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0xD3, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0xD3, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0xD3, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xD5, 0x02, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xD5, 0x02, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0xF2, 0x02, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x03, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x04, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x05, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x4F, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x04, 0x00, 0x58, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x03, 0x00, 0x61, 0x03, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x61, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x75, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x81, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x86, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x8C, 0x03, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x21, 0x00, 0x04, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x21, 0x00, 0x05, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x05, 0x00, 0x18, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x21, 0x00, 0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x04, 0x00, 0x21, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x05, 0x00, 0x25, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x04, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x21, 0x00, 0x06, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x07, 0x00, 0x38, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x21, 0x00, 0x06, 0x00, - 0x3F, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x04, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x4C, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x4D, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x21, 0x00, 0x06, 0x00, 0x51, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x21, 0x00, 0x07, 0x00, - 0x57, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x2F, 0xBA, 0xE8, 0x3E, 0x2C, 0x00, 0x06, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xCD, 0xCC, 0x0C, 0x40, - 0x2C, 0x00, 0x06, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, - 0x71, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x02, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4B, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0xE5, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBF, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, - 0x20, 0x00, 0x04, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x0A, 0x01, 0x00, 0x00, 0x0B, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x04, 0x00, 0x27, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x28, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x27, 0x01, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0xE9, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x04, 0x00, 0x13, 0x02, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x14, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x02, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x46, 0x02, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x40, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4B, 0x02, 0x00, 0x00, 0x00, 0x00, 0x70, 0x40, - 0x19, 0x00, 0x09, 0x00, 0x51, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, 0x52, 0x02, 0x00, 0x00, 0x51, 0x02, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x53, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x02, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x53, 0x02, 0x00, 0x00, 0x54, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x05, 0x00, 0x58, 0x02, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x59, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x58, 0x02, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x59, 0x02, 0x00, 0x00, 0x5A, 0x02, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x5B, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x65, 0x02, 0x00, 0x00, - 0xCD, 0xCC, 0xCC, 0x3A, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x66, 0x02, 0x00, 0x00, - 0x89, 0x88, 0x08, 0x3B, 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x67, 0x02, 0x00, 0x00, - 0x65, 0x02, 0x00, 0x00, 0x66, 0x02, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x53, 0x02, 0x00, 0x00, - 0x6F, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0x75, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x7D, 0x02, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x85, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8D, 0x02, 0x00, 0x00, - 0x81, 0x80, 0x00, 0x3B, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x96, 0x02, 0x00, 0x00, - 0xC1, 0xC0, 0xC0, 0x3B, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9F, 0x02, 0x00, 0x00, - 0xA1, 0xA0, 0x20, 0x3C, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA8, 0x02, 0x00, 0x00, - 0xE1, 0xE0, 0x60, 0x3C, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB1, 0x02, 0x00, 0x00, - 0x91, 0x90, 0x90, 0x3C, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBA, 0x02, 0x00, 0x00, - 0xB1, 0xB0, 0xB0, 0x3C, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC3, 0x02, 0x00, 0x00, - 0xD1, 0xD0, 0xD0, 0x3C, 0x2C, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0xC7, 0x02, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x53, 0x02, 0x00, 0x00, 0xCF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0xD1, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xD1, 0x02, 0x00, 0x00, 0xD2, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x04, 0x00, 0xD3, 0x02, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0xD4, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xD3, 0x02, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xD4, 0x02, 0x00, 0x00, 0xD5, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0xD9, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0xF1, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xF1, 0x02, 0x00, 0x00, 0xF2, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xD1, 0x02, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xF1, 0x02, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xF1, 0x02, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x3E, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x3E, 0x03, 0x00, 0x00, 0x3F, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x42, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x4A, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x4E, 0x03, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xF1, 0x02, 0x00, 0x00, 0x4F, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x53, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x57, 0x03, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xF1, 0x02, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x5C, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x04, 0x00, 0x60, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x60, 0x03, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x75, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xCD, 0x00, 0x00, 0x00, 0x81, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0xD1, 0x02, 0x00, 0x00, 0x86, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2C, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x88, 0x03, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, - 0xF2, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xF1, 0x02, 0x00, 0x00, 0x8C, 0x03, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x96, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x85, 0x02, 0x00, 0x00, 0x86, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x85, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x85, 0x02, 0x00, 0x00, 0x99, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x85, 0x02, 0x00, 0x00, 0xA2, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x85, 0x02, 0x00, 0x00, 0xAB, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x85, 0x02, 0x00, 0x00, 0xB4, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x85, 0x02, 0x00, 0x00, 0xBD, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xC6, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xCC, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xD6, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xD8, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xDE, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xE4, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xE7, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xEE, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xFF, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0B, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0E, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x1B, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x22, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x25, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2E, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x33, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x36, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x62, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x64, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0x66, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x71, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x78, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x7A, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x7C, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x85, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x8D, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x8F, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x91, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x93, 0x03, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x88, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, - 0xBE, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x89, 0x02, 0x00, 0x00, 0x88, 0x02, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x8B, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0x89, 0x02, 0x00, 0x00, 0x8A, 0x02, 0x00, 0x00, 0x8B, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x8A, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x8C, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x8E, 0x02, 0x00, 0x00, 0x8C, 0x02, 0x00, 0x00, 0x8D, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x8B, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x8B, 0x02, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x8F, 0x02, 0x00, 0x00, 0x89, 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, - 0x8E, 0x02, 0x00, 0x00, 0x8A, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x86, 0x02, 0x00, 0x00, - 0x8F, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x91, 0x02, 0x00, 0x00, - 0x87, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x92, 0x02, 0x00, 0x00, - 0x91, 0x02, 0x00, 0x00, 0x8D, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x94, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x92, 0x02, 0x00, 0x00, 0x93, 0x02, 0x00, 0x00, - 0x94, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x93, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x97, 0x02, 0x00, 0x00, 0x95, 0x02, 0x00, 0x00, 0x96, 0x02, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x94, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x94, 0x02, 0x00, 0x00, - 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, 0x98, 0x02, 0x00, 0x00, 0x92, 0x02, 0x00, 0x00, - 0x8B, 0x02, 0x00, 0x00, 0x97, 0x02, 0x00, 0x00, 0x93, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x90, 0x02, 0x00, 0x00, 0x98, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x9A, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x9B, 0x02, 0x00, 0x00, 0x9A, 0x02, 0x00, 0x00, 0x96, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0x9D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x9B, 0x02, 0x00, 0x00, - 0x9C, 0x02, 0x00, 0x00, 0x9D, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x9C, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9E, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, - 0xB8, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0xA0, 0x02, 0x00, 0x00, 0x9E, 0x02, 0x00, 0x00, - 0x9F, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x9D, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x9D, 0x02, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, 0xA1, 0x02, 0x00, 0x00, - 0x9B, 0x02, 0x00, 0x00, 0x94, 0x02, 0x00, 0x00, 0xA0, 0x02, 0x00, 0x00, 0x9C, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x99, 0x02, 0x00, 0x00, 0xA1, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xA3, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0xA4, 0x02, 0x00, 0x00, 0xA3, 0x02, 0x00, 0x00, 0x9F, 0x02, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0xA6, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0xA4, 0x02, 0x00, 0x00, 0xA5, 0x02, 0x00, 0x00, 0xA6, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0xA5, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA7, 0x02, 0x00, 0x00, - 0x87, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0xA9, 0x02, 0x00, 0x00, - 0xA7, 0x02, 0x00, 0x00, 0xA8, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xA6, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xA6, 0x02, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, - 0xAA, 0x02, 0x00, 0x00, 0xA4, 0x02, 0x00, 0x00, 0x9D, 0x02, 0x00, 0x00, 0xA9, 0x02, 0x00, 0x00, - 0xA5, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA2, 0x02, 0x00, 0x00, 0xAA, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAC, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, - 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0xAD, 0x02, 0x00, 0x00, 0xAC, 0x02, 0x00, 0x00, - 0xA8, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0xAF, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0xAD, 0x02, 0x00, 0x00, 0xAE, 0x02, 0x00, 0x00, 0xAF, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xAE, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xB0, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0xB2, 0x02, 0x00, 0x00, 0xB0, 0x02, 0x00, 0x00, 0xB1, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0xAF, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xAF, 0x02, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, - 0x99, 0x00, 0x00, 0x00, 0xB3, 0x02, 0x00, 0x00, 0xAD, 0x02, 0x00, 0x00, 0xA6, 0x02, 0x00, 0x00, - 0xB2, 0x02, 0x00, 0x00, 0xAE, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xAB, 0x02, 0x00, 0x00, - 0xB3, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB5, 0x02, 0x00, 0x00, - 0x87, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0xB6, 0x02, 0x00, 0x00, - 0xB5, 0x02, 0x00, 0x00, 0xB1, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0xB8, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xB6, 0x02, 0x00, 0x00, 0xB7, 0x02, 0x00, 0x00, - 0xB8, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xB7, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xB9, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0xBB, 0x02, 0x00, 0x00, 0xB9, 0x02, 0x00, 0x00, 0xBA, 0x02, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0xB8, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xB8, 0x02, 0x00, 0x00, - 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, 0xB6, 0x02, 0x00, 0x00, - 0xAF, 0x02, 0x00, 0x00, 0xBB, 0x02, 0x00, 0x00, 0xB7, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xB4, 0x02, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xBE, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0xBF, 0x02, 0x00, 0x00, 0xBE, 0x02, 0x00, 0x00, 0xBA, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0xC1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xBF, 0x02, 0x00, 0x00, - 0xC0, 0x02, 0x00, 0x00, 0xC1, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xC0, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC2, 0x02, 0x00, 0x00, 0x87, 0x02, 0x00, 0x00, - 0xB8, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0xC4, 0x02, 0x00, 0x00, 0xC2, 0x02, 0x00, 0x00, - 0xC3, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xC1, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0xC1, 0x02, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, 0xC5, 0x02, 0x00, 0x00, - 0xBF, 0x02, 0x00, 0x00, 0xB8, 0x02, 0x00, 0x00, 0xC4, 0x02, 0x00, 0x00, 0xC0, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xBD, 0x02, 0x00, 0x00, 0xC5, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xC6, 0x02, 0x00, 0x00, 0xC7, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, - 0xC8, 0x02, 0x00, 0x00, 0x86, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, - 0xC9, 0x02, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, 0xA7, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0xCA, 0x02, 0x00, 0x00, 0xC8, 0x02, 0x00, 0x00, 0xC9, 0x02, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, - 0x99, 0x00, 0x00, 0x00, 0xCB, 0x02, 0x00, 0x00, 0xCA, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0xCE, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xCB, 0x02, 0x00, 0x00, - 0xCD, 0x02, 0x00, 0x00, 0xE0, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xCD, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x52, 0x02, 0x00, 0x00, 0xD0, 0x02, 0x00, 0x00, 0xCF, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xD7, 0x02, 0x00, 0x00, 0xD2, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xD6, 0x02, 0x00, 0x00, 0xD7, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0xD9, 0x02, 0x00, 0x00, 0xDA, 0x02, 0x00, 0x00, 0xD5, 0x02, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xDB, 0x02, 0x00, 0x00, 0xDA, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xD8, 0x02, 0x00, 0x00, 0xDB, 0x02, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xDC, 0x02, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0xD6, 0x02, 0x00, 0x00, - 0xD8, 0x02, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xDD, 0x02, 0x00, 0x00, - 0xD0, 0x02, 0x00, 0x00, 0xDC, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xDE, 0x02, 0x00, 0x00, - 0xDD, 0x02, 0x00, 0x00, 0x39, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0xDF, 0x02, 0x00, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0xDE, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xCC, 0x02, 0x00, 0x00, - 0xDF, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xCE, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0xE0, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xE1, 0x02, 0x00, 0x00, - 0xC6, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xCC, 0x02, 0x00, 0x00, 0xE1, 0x02, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0xCE, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xCE, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xE2, 0x02, 0x00, 0x00, 0xCC, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xC6, 0x02, 0x00, 0x00, 0xE2, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x99, 0x00, 0x00, 0x00, 0xE3, 0x02, 0x00, 0x00, 0x86, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0xE6, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xE3, 0x02, 0x00, 0x00, - 0xE5, 0x02, 0x00, 0x00, 0xEA, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xE5, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xE8, 0x02, 0x00, 0x00, 0xC6, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xE7, 0x02, 0x00, 0x00, 0xE8, 0x02, 0x00, 0x00, 0x39, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xE9, 0x02, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0xE7, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xE4, 0x02, 0x00, 0x00, 0xE9, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0xE6, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xEA, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xEB, 0x02, 0x00, 0x00, 0xC6, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xE4, 0x02, 0x00, 0x00, 0xEB, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xE6, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xE6, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xEC, 0x02, 0x00, 0x00, 0xE4, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xC6, 0x02, 0x00, 0x00, - 0xEC, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0xED, 0x02, 0x00, 0x00, - 0x90, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0xED, 0x02, 0x00, 0x00, 0xEF, 0x02, 0x00, 0x00, 0xF7, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xEF, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xF3, 0x02, 0x00, 0x00, 0xF2, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, - 0xF4, 0x02, 0x00, 0x00, 0xC6, 0x02, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xF5, 0x02, 0x00, 0x00, 0xF4, 0x02, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xF6, 0x02, 0x00, 0x00, 0xF3, 0x02, 0x00, 0x00, 0xF5, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xEE, 0x02, 0x00, 0x00, 0xF6, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0xF0, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xF7, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xF8, 0x02, 0x00, 0x00, 0xC6, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xEE, 0x02, 0x00, 0x00, 0xF8, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xF0, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xF0, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xF9, 0x02, 0x00, 0x00, 0xEE, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xC6, 0x02, 0x00, 0x00, - 0xF9, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0xFA, 0x02, 0x00, 0x00, - 0xAB, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xFB, 0x02, 0x00, 0x00, - 0xF2, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xFC, 0x02, 0x00, 0x00, - 0xC6, 0x02, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xFD, 0x02, 0x00, 0x00, - 0xFA, 0x02, 0x00, 0x00, 0xFA, 0x02, 0x00, 0x00, 0xFA, 0x02, 0x00, 0x00, 0xFA, 0x02, 0x00, 0x00, - 0xA9, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0xFE, 0x02, 0x00, 0x00, 0xFD, 0x02, 0x00, 0x00, - 0xFB, 0x02, 0x00, 0x00, 0xFC, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xC6, 0x02, 0x00, 0x00, - 0xFE, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xFF, 0x02, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x99, 0x02, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x00, 0x03, 0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x12, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x01, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, - 0x03, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x06, 0x03, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0A, 0x03, 0x00, 0x00, 0x09, 0x03, 0x00, 0x00, - 0x09, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x08, 0x03, 0x00, 0x00, 0x0A, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x0C, 0x03, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x0D, 0x03, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x0B, 0x03, 0x00, 0x00, 0x0D, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0F, 0x03, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x0F, 0x03, 0x00, 0x00, - 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x0E, 0x03, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x39, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x11, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, - 0x0B, 0x03, 0x00, 0x00, 0x0E, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xFF, 0x02, 0x00, 0x00, - 0x11, 0x03, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x12, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x13, 0x03, 0x00, 0x00, - 0xA2, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x15, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0x13, 0x03, 0x00, 0x00, 0x14, 0x03, 0x00, 0x00, 0x2A, 0x03, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x14, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x17, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x16, 0x03, 0x00, 0x00, - 0x17, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x19, 0x03, 0x00, 0x00, - 0x04, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1A, 0x03, 0x00, 0x00, - 0x19, 0x03, 0x00, 0x00, 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x18, 0x03, 0x00, 0x00, 0x1A, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1D, 0x03, 0x00, 0x00, 0x1C, 0x03, 0x00, 0x00, 0x1C, 0x03, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1B, 0x03, 0x00, 0x00, - 0x1D, 0x03, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1E, 0x03, 0x00, 0x00, - 0x43, 0x00, 0x00, 0x00, 0x16, 0x03, 0x00, 0x00, 0x18, 0x03, 0x00, 0x00, 0x1B, 0x03, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xFF, 0x02, 0x00, 0x00, 0x1E, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1F, 0x03, 0x00, 0x00, 0xFF, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x20, 0x03, 0x00, 0x00, 0x21, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x23, 0x03, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x24, 0x03, 0x00, 0x00, 0x23, 0x03, 0x00, 0x00, 0x23, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x22, 0x03, 0x00, 0x00, 0x24, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x26, 0x03, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x27, 0x03, 0x00, 0x00, 0x26, 0x03, 0x00, 0x00, - 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x25, 0x03, 0x00, 0x00, 0x27, 0x03, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x28, 0x03, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x22, 0x03, 0x00, 0x00, - 0x25, 0x03, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x29, 0x03, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x1F, 0x03, 0x00, 0x00, 0x28, 0x03, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xFF, 0x02, 0x00, 0x00, 0x29, 0x03, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x15, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x2A, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x2B, 0x03, 0x00, 0x00, 0xB4, 0x02, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0x2D, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x2B, 0x03, 0x00, 0x00, - 0x2C, 0x03, 0x00, 0x00, 0x3A, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x2C, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2F, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x2E, 0x03, 0x00, 0x00, 0x2F, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x31, 0x03, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x32, 0x03, 0x00, 0x00, 0x31, 0x03, 0x00, 0x00, 0x31, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x30, 0x03, 0x00, 0x00, - 0x32, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x03, 0x00, 0x00, - 0x04, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x35, 0x03, 0x00, 0x00, - 0x34, 0x03, 0x00, 0x00, 0x34, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x33, 0x03, 0x00, 0x00, 0x35, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x37, 0x03, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x38, 0x03, 0x00, 0x00, 0x37, 0x03, 0x00, 0x00, 0x37, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x36, 0x03, 0x00, 0x00, - 0x38, 0x03, 0x00, 0x00, 0x39, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x2E, 0x03, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x33, 0x03, 0x00, 0x00, - 0x36, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xFF, 0x02, 0x00, 0x00, 0x39, 0x03, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x2D, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3A, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x3B, 0x03, 0x00, 0x00, 0xBD, 0x02, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x3D, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x3B, 0x03, 0x00, 0x00, 0x3C, 0x03, 0x00, 0x00, 0x3D, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x3C, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x40, 0x03, 0x00, 0x00, - 0x04, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x41, 0x03, 0x00, 0x00, - 0x40, 0x03, 0x00, 0x00, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x42, 0x03, 0x00, 0x00, 0x43, 0x03, 0x00, 0x00, 0x3F, 0x03, 0x00, 0x00, - 0xDC, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x43, 0x03, 0x00, 0x00, 0x41, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x44, 0x03, 0x00, 0x00, 0x04, 0x03, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x45, 0x03, 0x00, 0x00, 0x44, 0x03, 0x00, 0x00, - 0x44, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x42, 0x03, 0x00, 0x00, 0x46, 0x03, 0x00, 0x00, 0x3F, 0x03, 0x00, 0x00, 0xE9, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x46, 0x03, 0x00, 0x00, 0x45, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x47, 0x03, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x47, 0x03, 0x00, 0x00, 0x47, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x42, 0x03, 0x00, 0x00, - 0x49, 0x03, 0x00, 0x00, 0x3F, 0x03, 0x00, 0x00, 0x75, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x49, 0x03, 0x00, 0x00, 0x48, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x4B, 0x03, 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x4C, 0x03, 0x00, 0x00, 0x4B, 0x03, 0x00, 0x00, 0x4B, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x42, 0x03, 0x00, 0x00, 0x4D, 0x03, 0x00, 0x00, - 0x3F, 0x03, 0x00, 0x00, 0x4A, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x4D, 0x03, 0x00, 0x00, - 0x4C, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, 0x03, 0x00, 0x00, - 0x4F, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x51, 0x03, 0x00, 0x00, - 0x50, 0x03, 0x00, 0x00, 0x50, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x42, 0x03, 0x00, 0x00, 0x52, 0x03, 0x00, 0x00, 0x3F, 0x03, 0x00, 0x00, - 0x4E, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x52, 0x03, 0x00, 0x00, 0x51, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x54, 0x03, 0x00, 0x00, 0x4F, 0x03, 0x00, 0x00, - 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x55, 0x03, 0x00, 0x00, 0x54, 0x03, 0x00, 0x00, - 0x54, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x42, 0x03, 0x00, 0x00, 0x56, 0x03, 0x00, 0x00, 0x3F, 0x03, 0x00, 0x00, 0x53, 0x03, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x56, 0x03, 0x00, 0x00, 0x55, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x59, 0x03, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x5A, 0x03, 0x00, 0x00, 0x59, 0x03, 0x00, 0x00, 0x59, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x42, 0x03, 0x00, 0x00, - 0x5B, 0x03, 0x00, 0x00, 0x3F, 0x03, 0x00, 0x00, 0x57, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x5B, 0x03, 0x00, 0x00, 0x5A, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x5D, 0x03, 0x00, 0x00, 0x58, 0x03, 0x00, 0x00, 0x4F, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x5E, 0x03, 0x00, 0x00, 0x5D, 0x03, 0x00, 0x00, 0x5D, 0x03, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x42, 0x03, 0x00, 0x00, 0x5F, 0x03, 0x00, 0x00, - 0x3F, 0x03, 0x00, 0x00, 0x5C, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x5F, 0x03, 0x00, 0x00, - 0x5E, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x63, 0x03, 0x00, 0x00, - 0x03, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x62, 0x03, 0x00, 0x00, 0x63, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x65, 0x03, 0x00, 0x00, 0x3F, 0x03, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x64, 0x03, 0x00, 0x00, 0x65, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x67, 0x03, 0x00, 0x00, 0x61, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x66, 0x03, 0x00, 0x00, 0x67, 0x03, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x68, 0x03, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x62, 0x03, 0x00, 0x00, 0x64, 0x03, 0x00, 0x00, - 0x66, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xFF, 0x02, 0x00, 0x00, 0x68, 0x03, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x3D, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3D, 0x03, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x2D, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x2D, 0x03, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x15, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x15, 0x03, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x69, 0x03, 0x00, 0x00, 0x86, 0x02, 0x00, 0x00, - 0xA8, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x6A, 0x03, 0x00, 0x00, 0x69, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x6B, 0x03, 0x00, 0x00, 0x90, 0x02, 0x00, 0x00, - 0xA8, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x6C, 0x03, 0x00, 0x00, 0x6B, 0x03, 0x00, 0x00, - 0xA7, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x6D, 0x03, 0x00, 0x00, 0x6A, 0x03, 0x00, 0x00, - 0x6C, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x6E, 0x03, 0x00, 0x00, - 0xAB, 0x02, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x6F, 0x03, 0x00, 0x00, - 0x6E, 0x03, 0x00, 0x00, 0xA7, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x70, 0x03, 0x00, 0x00, - 0x6D, 0x03, 0x00, 0x00, 0x6F, 0x03, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x73, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x70, 0x03, 0x00, 0x00, 0x72, 0x03, 0x00, 0x00, - 0x7E, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x72, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x74, 0x03, 0x00, 0x00, 0xFF, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x76, 0x03, 0x00, 0x00, 0x75, 0x03, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x77, 0x03, 0x00, 0x00, 0x74, 0x03, 0x00, 0x00, 0x76, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00, 0x00, 0xC6, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x78, 0x03, 0x00, 0x00, 0x79, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x7B, 0x03, 0x00, 0x00, 0xF2, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x7A, 0x03, 0x00, 0x00, 0x7B, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x7C, 0x03, 0x00, 0x00, - 0x77, 0x03, 0x00, 0x00, 0x39, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x7D, 0x03, 0x00, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x78, 0x03, 0x00, 0x00, 0x7A, 0x03, 0x00, 0x00, 0x7C, 0x03, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x71, 0x03, 0x00, 0x00, 0x7D, 0x03, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x73, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7E, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x7F, 0x03, 0x00, 0x00, 0xC6, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x71, 0x03, 0x00, 0x00, 0x7F, 0x03, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x73, 0x03, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x73, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x80, 0x03, 0x00, 0x00, 0x71, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xC6, 0x02, 0x00, 0x00, - 0x80, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x82, 0x03, 0x00, 0x00, - 0x81, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x83, 0x03, 0x00, 0x00, - 0xC6, 0x02, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x84, 0x03, 0x00, 0x00, - 0x83, 0x03, 0x00, 0x00, 0x82, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xC6, 0x02, 0x00, 0x00, - 0x84, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x87, 0x03, 0x00, 0x00, - 0x86, 0x03, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x89, 0x03, 0x00, 0x00, - 0x87, 0x03, 0x00, 0x00, 0x88, 0x03, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x8A, 0x03, 0x00, 0x00, 0x89, 0x03, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x8B, 0x03, 0x00, 0x00, 0x8A, 0x03, 0x00, 0x00, 0x88, 0x03, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x85, 0x03, 0x00, 0x00, 0x8B, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x8E, 0x03, 0x00, 0x00, 0xC6, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x8D, 0x03, 0x00, 0x00, 0x8E, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x90, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x8F, 0x03, 0x00, 0x00, - 0x90, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x92, 0x03, 0x00, 0x00, - 0x85, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x91, 0x03, 0x00, 0x00, 0x92, 0x03, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x94, 0x03, 0x00, 0x00, 0x8C, 0x03, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x93, 0x03, 0x00, 0x00, 0x94, 0x03, 0x00, 0x00, 0x39, 0x00, 0x08, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x95, 0x03, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x8D, 0x03, 0x00, 0x00, - 0x8F, 0x03, 0x00, 0x00, 0x91, 0x03, 0x00, 0x00, 0x93, 0x03, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xC6, 0x02, 0x00, 0x00, 0x95, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x96, 0x03, 0x00, 0x00, - 0x97, 0x03, 0x00, 0x00, 0xD5, 0x02, 0x00, 0x00, 0xE9, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x98, 0x03, 0x00, 0x00, 0x97, 0x03, 0x00, 0x00, 0xAB, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x99, 0x03, 0x00, 0x00, 0x98, 0x03, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x9B, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x99, 0x03, 0x00, 0x00, 0x9A, 0x03, 0x00, 0x00, 0x9B, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x9A, 0x03, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x9C, 0x03, 0x00, 0x00, - 0xC6, 0x02, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x9D, 0x03, 0x00, 0x00, 0x9C, 0x03, 0x00, 0x00, 0xB4, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x9E, 0x03, 0x00, 0x00, 0x9D, 0x03, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x9B, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x9B, 0x03, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x9F, 0x03, 0x00, 0x00, 0x99, 0x03, 0x00, 0x00, 0x73, 0x03, 0x00, 0x00, - 0x9E, 0x03, 0x00, 0x00, 0x9A, 0x03, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0xA1, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x9F, 0x03, 0x00, 0x00, 0xA0, 0x03, 0x00, 0x00, - 0xA1, 0x03, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA0, 0x03, 0x00, 0x00, 0xFC, 0x00, 0x01, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xA1, 0x03, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xA3, 0x03, 0x00, 0x00, 0xC6, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x0B, 0x01, 0x00, 0x00, - 0xA3, 0x03, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x5F, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, - 0x5E, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, - 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x6B, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, - 0x67, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x6E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x4F, 0x00, 0x08, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x6F, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x5E, 0x00, 0x00, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x07, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x00, - 0x78, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x79, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x7C, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x7D, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x7E, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x7F, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x7C, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x81, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x87, 0x00, 0x00, 0x00, - 0x7C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x8A, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x8B, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x8C, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, - 0x8F, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, - 0x86, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x7C, 0x00, 0x00, 0x00, - 0x91, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, - 0x7C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, - 0x92, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x94, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x9B, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x97, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x9A, 0x00, 0x00, 0x00, 0x97, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0x9D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x9A, 0x00, 0x00, 0x00, - 0x9C, 0x00, 0x00, 0x00, 0x9E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x9C, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x9D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x9E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x9B, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x9D, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, - 0xA2, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xA6, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0xA6, 0x00, 0x00, 0x00, - 0xA7, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA5, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, - 0xB4, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xAB, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAD, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0xAE, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, - 0xA5, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xAB, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0xAD, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xAD, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xB1, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0xB1, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x24, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x00, 0x00, - 0x7F, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB7, 0x00, 0x00, 0x00, 0xB6, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0xB9, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0xB8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x00, 0x00, - 0xB9, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0xBB, 0x00, 0x00, 0x00, - 0xB7, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0xBB, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x29, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, - 0xBE, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC1, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, 0xBF, 0x00, 0x00, 0x00, - 0xC1, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, - 0x26, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xC4, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, - 0xC5, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, 0xC5, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xC7, 0x00, 0x00, 0x00, 0xC4, 0x00, 0x00, 0x00, 0xC6, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0xC2, 0x00, 0x00, 0x00, - 0xC7, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2A, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xCB, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xCC, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xCB, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCF, 0x00, 0x00, 0x00, 0xCE, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD0, 0x00, 0x00, 0x00, 0xCC, 0x00, 0x00, 0x00, - 0xCF, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0xD0, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x33, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xD7, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD5, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xD4, 0x00, 0x00, 0x00, 0xD5, 0x00, 0x00, 0x00, 0x39, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0xD4, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xD3, 0x00, 0x00, 0x00, 0xD6, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xD9, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0xD3, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0xDB, 0x00, 0x00, 0x00, 0xDC, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, - 0x07, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, - 0xDD, 0x00, 0x00, 0x00, 0xDD, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xDF, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0xD8, 0x00, 0x00, 0x00, - 0xD9, 0x00, 0x00, 0x00, 0xDE, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xD7, 0x00, 0x00, 0x00, - 0xDF, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xE1, 0x00, 0x00, 0x00, - 0xD3, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, - 0xE1, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xE3, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xE4, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0xE5, 0x00, 0x00, 0x00, - 0xE6, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, 0xE2, 0x00, 0x00, 0x00, - 0xE2, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, - 0xE6, 0x00, 0x00, 0x00, 0xE3, 0x00, 0x00, 0x00, 0xE4, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xE0, 0x00, 0x00, 0x00, 0xE7, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xE9, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xEA, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xEB, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xEC, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xED, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0xEB, 0x00, 0x00, 0x00, 0xEC, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, - 0xEE, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, 0xED, 0x00, 0x00, 0x00, - 0xED, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x00, 0x00, - 0xEE, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE8, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x00, 0x00, 0xF1, 0x00, 0x00, 0x00, 0xF2, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, - 0xBC, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xF6, 0x00, 0x00, 0x00, - 0x30, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, - 0x2F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0xE5, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, - 0xF5, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x00, 0x00, - 0xA9, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, - 0xF6, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xF0, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xFB, 0x00, 0x00, 0x00, - 0xD7, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, - 0xD7, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, - 0xDA, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, - 0xFD, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xFF, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, - 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, - 0xFB, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xFA, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x03, 0x01, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x04, 0x01, 0x00, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x05, 0x01, 0x00, 0x00, 0xDA, 0x00, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x06, 0x01, 0x00, 0x00, 0x05, 0x01, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x06, 0x01, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, - 0x07, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, 0x07, 0x01, 0x00, 0x00, - 0x0C, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x02, 0x01, 0x00, 0x00, 0x09, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, 0xFA, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x0D, 0x01, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, - 0x0F, 0x01, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x11, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x0C, 0x01, 0x00, 0x00, - 0x0D, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x0B, 0x01, 0x00, 0x00, - 0x11, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x12, 0x01, 0x00, 0x00, - 0x0B, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x12, 0x01, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x37, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, 0x10, 0x00, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x16, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, 0x35, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0x17, 0x01, 0x00, 0x00, 0x18, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x15, 0x01, 0x00, 0x00, 0x19, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1A, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1C, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x1A, 0x01, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1D, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, - 0x1C, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1E, 0x01, 0x00, 0x00, - 0x15, 0x01, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1F, 0x01, 0x00, 0x00, 0x1E, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x20, 0x01, 0x00, 0x00, 0x15, 0x01, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0x1F, 0x01, 0x00, 0x00, 0x21, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x23, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x22, 0x01, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x24, 0x01, 0x00, 0x00, - 0x1D, 0x01, 0x00, 0x00, 0x23, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x24, 0x01, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x3E, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x28, 0x01, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x3E, 0x01, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x2B, 0x01, 0x00, 0x00, 0x2C, 0x01, 0x00, 0x00, 0x39, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x2D, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x2B, 0x01, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2E, 0x01, 0x00, 0x00, 0x2A, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2F, 0x01, 0x00, 0x00, - 0x2A, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x30, 0x01, 0x00, 0x00, 0x2D, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, 0x2D, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x32, 0x01, 0x00, 0x00, 0x2E, 0x01, 0x00, 0x00, - 0x2F, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, - 0x30, 0x01, 0x00, 0x00, 0x31, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x27, 0x01, 0x00, 0x00, - 0x34, 0x01, 0x00, 0x00, 0x32, 0x01, 0x00, 0x00, 0x33, 0x01, 0x00, 0x00, 0x54, 0x00, 0x04, 0x00, - 0x27, 0x01, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, 0x34, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x29, 0x01, 0x00, 0x00, 0x35, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x36, 0x01, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x37, 0x01, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x38, 0x01, 0x00, 0x00, 0x36, 0x01, 0x00, 0x00, 0x37, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x39, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x27, 0x01, 0x00, 0x00, - 0x39, 0x01, 0x00, 0x00, 0x29, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x3A, 0x01, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x91, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x3B, 0x01, 0x00, 0x00, 0x39, 0x01, 0x00, 0x00, 0x3A, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x39, 0x00, 0x00, 0x00, 0x3B, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x3D, 0x01, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x3C, 0x01, 0x00, 0x00, - 0x3D, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x3F, 0x01, 0x00, 0x00, - 0x3B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x3E, 0x01, 0x00, 0x00, 0x3F, 0x01, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x3C, 0x01, 0x00, 0x00, 0x3E, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x40, 0x01, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x47, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x4B, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x53, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x55, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x5D, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x44, 0x01, 0x00, 0x00, - 0x42, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x45, 0x01, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x46, 0x01, 0x00, 0x00, - 0x44, 0x01, 0x00, 0x00, 0x45, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x43, 0x01, 0x00, 0x00, - 0x46, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, - 0x40, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4A, 0x01, 0x00, 0x00, - 0x48, 0x01, 0x00, 0x00, 0x49, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x47, 0x01, 0x00, 0x00, - 0x4A, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4C, 0x01, 0x00, 0x00, - 0x47, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x4D, 0x01, 0x00, 0x00, - 0x43, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x4E, 0x01, 0x00, 0x00, - 0x4C, 0x01, 0x00, 0x00, 0x4D, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x4F, 0x01, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x50, 0x01, 0x00, 0x00, 0x43, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x51, 0x01, 0x00, 0x00, 0x4F, 0x01, 0x00, 0x00, 0x50, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x52, 0x01, 0x00, 0x00, 0x4E, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x53, 0x01, 0x00, 0x00, - 0x51, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x52, 0x01, 0x00, 0x00, 0x53, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x4B, 0x01, 0x00, 0x00, 0x54, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x56, 0x01, 0x00, 0x00, 0x4B, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x57, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x56, 0x01, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x55, 0x01, 0x00, 0x00, - 0x57, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, - 0x47, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x59, 0x01, 0x00, 0x00, - 0x55, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5A, 0x01, 0x00, 0x00, - 0x43, 0x01, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5B, 0x01, 0x00, 0x00, - 0x5A, 0x01, 0x00, 0x00, 0x59, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x5C, 0x01, 0x00, 0x00, 0x58, 0x01, 0x00, 0x00, 0x5B, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x5D, 0x01, 0x00, 0x00, 0x5C, 0x01, 0x00, 0x00, 0x39, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x5E, 0x01, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x5D, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, - 0x5E, 0x01, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x4A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x6D, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x71, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x75, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x82, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x83, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x91, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0x92, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xA0, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xA1, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xA6, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xA7, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xA9, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xAC, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xB1, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xB3, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xBE, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xCA, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x62, 0x01, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x63, 0x01, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x64, 0x01, 0x00, 0x00, - 0x62, 0x01, 0x00, 0x00, 0x63, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x61, 0x01, 0x00, 0x00, - 0x64, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x66, 0x01, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x67, 0x01, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x68, 0x01, 0x00, 0x00, - 0x66, 0x01, 0x00, 0x00, 0x67, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x65, 0x01, 0x00, 0x00, - 0x68, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x6A, 0x01, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x6B, 0x01, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x6C, 0x01, 0x00, 0x00, - 0x6A, 0x01, 0x00, 0x00, 0x6B, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x69, 0x01, 0x00, 0x00, - 0x6C, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x6E, 0x01, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x6F, 0x01, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x70, 0x01, 0x00, 0x00, - 0x6E, 0x01, 0x00, 0x00, 0x6F, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x6D, 0x01, 0x00, 0x00, - 0x70, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x72, 0x01, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x73, 0x01, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, - 0x72, 0x01, 0x00, 0x00, 0x73, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x71, 0x01, 0x00, 0x00, - 0x74, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x76, 0x01, 0x00, 0x00, - 0x45, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x77, 0x01, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x78, 0x01, 0x00, 0x00, - 0x76, 0x01, 0x00, 0x00, 0x77, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x75, 0x01, 0x00, 0x00, - 0x78, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x7A, 0x01, 0x00, 0x00, - 0x6D, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x7B, 0x01, 0x00, 0x00, - 0x61, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x7C, 0x01, 0x00, 0x00, - 0x6D, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x7D, 0x01, 0x00, 0x00, - 0x61, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x7E, 0x01, 0x00, 0x00, - 0x7C, 0x01, 0x00, 0x00, 0x7D, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x7F, 0x01, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x80, 0x01, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x81, 0x01, 0x00, 0x00, 0x7F, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x82, 0x01, 0x00, 0x00, 0x7E, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x83, 0x01, 0x00, 0x00, - 0x81, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x84, 0x01, 0x00, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x82, 0x01, 0x00, 0x00, 0x83, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x85, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, - 0x84, 0x01, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x86, 0x01, 0x00, 0x00, 0x7B, 0x01, 0x00, 0x00, 0x85, 0x01, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x87, 0x01, 0x00, 0x00, 0x7A, 0x01, 0x00, 0x00, - 0x86, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x79, 0x01, 0x00, 0x00, 0x87, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x89, 0x01, 0x00, 0x00, 0x71, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8A, 0x01, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, 0x71, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8C, 0x01, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, - 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8D, 0x01, 0x00, 0x00, 0x8B, 0x01, 0x00, 0x00, - 0x8C, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8E, 0x01, 0x00, 0x00, - 0x65, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x8F, 0x01, 0x00, 0x00, - 0x65, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, - 0x8E, 0x01, 0x00, 0x00, 0x8F, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x91, 0x01, 0x00, 0x00, - 0x8D, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x92, 0x01, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x93, 0x01, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x91, 0x01, 0x00, 0x00, 0x92, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x94, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x93, 0x01, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x95, 0x01, 0x00, 0x00, 0x8A, 0x01, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x96, 0x01, 0x00, 0x00, 0x89, 0x01, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x88, 0x01, 0x00, 0x00, 0x96, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0x75, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x99, 0x01, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x9A, 0x01, 0x00, 0x00, 0x75, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x9B, 0x01, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x9C, 0x01, 0x00, 0x00, 0x9A, 0x01, 0x00, 0x00, 0x9B, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9D, 0x01, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x9E, 0x01, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, - 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x9F, 0x01, 0x00, 0x00, 0x9D, 0x01, 0x00, 0x00, - 0x9E, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA0, 0x01, 0x00, 0x00, 0x9C, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xA1, 0x01, 0x00, 0x00, 0x9F, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xA2, 0x01, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0xA0, 0x01, 0x00, 0x00, - 0xA1, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0xA3, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0xA2, 0x01, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, - 0xF3, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0xA4, 0x01, 0x00, 0x00, - 0x99, 0x01, 0x00, 0x00, 0xA3, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xA5, 0x01, 0x00, 0x00, 0x98, 0x01, 0x00, 0x00, 0xA4, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x97, 0x01, 0x00, 0x00, 0xA5, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xA8, 0x01, 0x00, 0x00, 0x61, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA7, 0x01, 0x00, 0x00, - 0xA8, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xAA, 0x01, 0x00, 0x00, - 0x69, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA9, 0x01, 0x00, 0x00, 0xAA, 0x01, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAB, 0x01, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0xA7, 0x01, 0x00, 0x00, 0xA9, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xA6, 0x01, 0x00, 0x00, - 0xAB, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xAD, 0x01, 0x00, 0x00, - 0x79, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, - 0x79, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xAF, 0x01, 0x00, 0x00, - 0xAD, 0x01, 0x00, 0x00, 0xAE, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xB0, 0x01, 0x00, 0x00, 0xA6, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xB2, 0x01, 0x00, 0x00, 0x6D, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xB1, 0x01, 0x00, 0x00, - 0xB2, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xB4, 0x01, 0x00, 0x00, - 0x61, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xB3, 0x01, 0x00, 0x00, 0xB4, 0x01, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xB5, 0x01, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, - 0xB1, 0x01, 0x00, 0x00, 0xB3, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xB6, 0x01, 0x00, 0x00, 0xB0, 0x01, 0x00, 0x00, 0xB5, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xB7, 0x01, 0x00, 0x00, 0xAF, 0x01, 0x00, 0x00, 0xB6, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xB8, 0x01, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xB9, 0x01, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, - 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBA, 0x01, 0x00, 0x00, 0xB8, 0x01, 0x00, 0x00, - 0xB9, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xBB, 0x01, 0x00, 0x00, - 0xA6, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xBD, 0x01, 0x00, 0x00, - 0x71, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xBC, 0x01, 0x00, 0x00, 0xBD, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xBF, 0x01, 0x00, 0x00, 0x65, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xBE, 0x01, 0x00, 0x00, 0xBF, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0xBC, 0x01, 0x00, 0x00, - 0xBE, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xC1, 0x01, 0x00, 0x00, - 0xBB, 0x01, 0x00, 0x00, 0xC0, 0x01, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xC2, 0x01, 0x00, 0x00, 0xBA, 0x01, 0x00, 0x00, 0xC1, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xC3, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0xB7, 0x01, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xC4, 0x01, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xC5, 0x01, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xC6, 0x01, 0x00, 0x00, 0xC4, 0x01, 0x00, 0x00, 0xC5, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0xA6, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xC9, 0x01, 0x00, 0x00, 0x75, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xC8, 0x01, 0x00, 0x00, 0xC9, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xCB, 0x01, 0x00, 0x00, 0x69, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xCA, 0x01, 0x00, 0x00, - 0xCB, 0x01, 0x00, 0x00, 0x39, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xCC, 0x01, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0xC8, 0x01, 0x00, 0x00, 0xCA, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xCD, 0x01, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x00, 0xCC, 0x01, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0xCE, 0x01, 0x00, 0x00, 0xC6, 0x01, 0x00, 0x00, - 0xCD, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x10, 0x00, 0x00, 0x00, 0xCF, 0x01, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0xC3, 0x01, 0x00, 0x00, 0xCE, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xAC, 0x01, 0x00, 0x00, 0xCF, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xD0, 0x01, 0x00, 0x00, 0xAC, 0x01, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD1, 0x01, 0x00, 0x00, 0xD0, 0x01, 0x00, 0x00, - 0x0C, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD2, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0xD1, 0x01, 0x00, 0x00, 0x7F, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xD3, 0x01, 0x00, 0x00, 0xD2, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, - 0xD4, 0x01, 0x00, 0x00, 0xAC, 0x01, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xD5, 0x01, 0x00, 0x00, 0xD4, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xD6, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, - 0xD5, 0x01, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xD7, 0x01, 0x00, 0x00, - 0xD3, 0x01, 0x00, 0x00, 0xD6, 0x01, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0xD7, 0x01, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x52, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x50, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xDA, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, 0xE5, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0xE6, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x50, 0x00, 0x00, 0x00, 0xE7, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0xFB, 0x01, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x14, 0x02, 0x00, 0x00, 0x15, 0x02, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xDB, 0x01, 0x00, 0x00, - 0x52, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0xDD, 0x01, 0x00, 0x00, - 0x53, 0x00, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xDE, 0x01, 0x00, 0x00, 0xDD, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0xDF, 0x01, 0x00, 0x00, 0xDB, 0x01, 0x00, 0x00, 0xDE, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x11, 0x00, 0x00, 0x00, 0xE1, 0x01, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xE2, 0x01, 0x00, 0x00, 0xE1, 0x01, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0xE3, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, - 0xE2, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0xE4, 0x01, 0x00, 0x00, - 0xDF, 0x01, 0x00, 0x00, 0xE3, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xDA, 0x01, 0x00, 0x00, - 0xE4, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE5, 0x01, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xE6, 0x01, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0xE8, 0x01, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x82, 0x00, 0x05, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0xEA, 0x01, 0x00, 0x00, 0xE8, 0x01, 0x00, 0x00, 0xE9, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xE7, 0x01, 0x00, 0x00, 0xEA, 0x01, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0xEB, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xEB, 0x01, 0x00, 0x00, 0xF6, 0x00, 0x04, 0x00, - 0xED, 0x01, 0x00, 0x00, 0xEE, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0xEF, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xEF, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xE6, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0xF1, 0x01, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0xF2, 0x01, 0x00, 0x00, 0xF0, 0x01, 0x00, 0x00, 0xF1, 0x01, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0xF2, 0x01, 0x00, 0x00, 0xEC, 0x01, 0x00, 0x00, 0xED, 0x01, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xEC, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, - 0xF4, 0x01, 0x00, 0x00, 0xE7, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, - 0xF5, 0x01, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0xF4, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0xF6, 0x01, 0x00, 0x00, 0xF5, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0xF7, 0x01, 0x00, 0x00, 0xE6, 0x01, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x11, 0x00, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0xF7, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xF9, 0x01, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0xFA, 0x01, 0x00, 0x00, 0xF6, 0x01, 0x00, 0x00, - 0xF9, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xFA, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xFC, 0x01, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0xFD, 0x01, 0x00, 0x00, 0xE6, 0x01, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0xFD, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0xFF, 0x01, 0x00, 0x00, - 0xFE, 0x01, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0xFC, 0x01, 0x00, 0x00, 0xFF, 0x01, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xFB, 0x01, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, - 0xFB, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, - 0xF3, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, - 0xFB, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, - 0xF3, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, - 0x04, 0x02, 0x00, 0x00, 0x05, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x07, 0x02, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x08, 0x02, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x09, 0x02, 0x00, 0x00, 0x07, 0x02, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x0A, 0x02, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0x09, 0x02, 0x00, 0x00, - 0x0C, 0x00, 0x08, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0B, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x00, 0x00, 0x0A, 0x02, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, - 0x8E, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, - 0x0B, 0x02, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0D, 0x02, 0x00, 0x00, - 0x02, 0x02, 0x00, 0x00, 0x0C, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x01, 0x02, 0x00, 0x00, - 0x0D, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0E, 0x02, 0x00, 0x00, - 0xDA, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0F, 0x02, 0x00, 0x00, - 0x01, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, - 0x01, 0x02, 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x11, 0x02, 0x00, 0x00, - 0x0F, 0x02, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x0C, 0x00, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x12, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x0E, 0x02, 0x00, 0x00, - 0x11, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xDA, 0x01, 0x00, 0x00, 0x12, 0x02, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x16, 0x02, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, - 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x17, 0x02, 0x00, 0x00, - 0x16, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, - 0xE6, 0x01, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x17, 0x00, 0x00, 0x00, 0x19, 0x02, 0x00, 0x00, - 0x53, 0x00, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x1A, 0x02, 0x00, 0x00, 0x19, 0x02, 0x00, 0x00, 0xBE, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x1B, 0x02, 0x00, 0x00, 0x17, 0x02, 0x00, 0x00, 0x1A, 0x02, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1C, 0x02, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, - 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x1D, 0x02, 0x00, 0x00, - 0x1C, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x1E, 0x02, 0x00, 0x00, - 0xE7, 0x01, 0x00, 0x00, 0x41, 0x00, 0x06, 0x00, 0x17, 0x00, 0x00, 0x00, 0x1F, 0x02, 0x00, 0x00, - 0x53, 0x00, 0x00, 0x00, 0x1E, 0x02, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, 0x1F, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x05, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, 0x1D, 0x02, 0x00, 0x00, 0x20, 0x02, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x22, 0x02, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, - 0xB8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x23, 0x02, 0x00, 0x00, - 0x22, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, - 0xFB, 0x01, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x25, 0x02, 0x00, 0x00, 0x24, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x26, 0x02, 0x00, 0x00, 0x23, 0x02, 0x00, 0x00, 0x25, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x27, 0x02, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x28, 0x02, 0x00, 0x00, 0x27, 0x02, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x29, 0x02, 0x00, 0x00, 0xFB, 0x01, 0x00, 0x00, - 0xB8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2A, 0x02, 0x00, 0x00, - 0x29, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x02, 0x00, 0x00, - 0x28, 0x02, 0x00, 0x00, 0x2A, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x2C, 0x02, 0x00, 0x00, 0x26, 0x02, 0x00, 0x00, 0x2B, 0x02, 0x00, 0x00, 0x50, 0x00, 0x06, 0x00, - 0x13, 0x02, 0x00, 0x00, 0x2D, 0x02, 0x00, 0x00, 0x1B, 0x02, 0x00, 0x00, 0x21, 0x02, 0x00, 0x00, - 0x2C, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x15, 0x02, 0x00, 0x00, 0x2D, 0x02, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x13, 0x02, 0x00, 0x00, 0x2E, 0x02, 0x00, 0x00, 0x15, 0x02, 0x00, 0x00, - 0x9B, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x2F, 0x02, 0x00, 0x00, 0x2E, 0x02, 0x00, 0x00, - 0xA8, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x30, 0x02, 0x00, 0x00, 0x2F, 0x02, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x32, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x30, 0x02, 0x00, 0x00, 0x31, 0x02, 0x00, 0x00, 0x32, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x31, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x13, 0x02, 0x00, 0x00, 0x33, 0x02, 0x00, 0x00, - 0x15, 0x02, 0x00, 0x00, 0xA8, 0x00, 0x04, 0x00, 0x13, 0x02, 0x00, 0x00, 0x34, 0x02, 0x00, 0x00, - 0x33, 0x02, 0x00, 0x00, 0x9B, 0x00, 0x04, 0x00, 0x99, 0x00, 0x00, 0x00, 0x35, 0x02, 0x00, 0x00, - 0x34, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x32, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x32, 0x02, 0x00, 0x00, 0xF5, 0x00, 0x07, 0x00, 0x99, 0x00, 0x00, 0x00, 0x36, 0x02, 0x00, 0x00, - 0x2F, 0x02, 0x00, 0x00, 0xEC, 0x01, 0x00, 0x00, 0x35, 0x02, 0x00, 0x00, 0x31, 0x02, 0x00, 0x00, - 0xF7, 0x00, 0x03, 0x00, 0x38, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, - 0x36, 0x02, 0x00, 0x00, 0x37, 0x02, 0x00, 0x00, 0x38, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x37, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x39, 0x02, 0x00, 0x00, - 0xE5, 0x01, 0x00, 0x00, 0x7F, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3A, 0x02, 0x00, 0x00, - 0x39, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0xE5, 0x01, 0x00, 0x00, 0x3A, 0x02, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0x38, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x38, 0x02, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0xEE, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xEE, 0x01, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x3B, 0x02, 0x00, 0x00, 0xE6, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xE7, 0x01, 0x00, 0x00, 0x3B, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x3C, 0x02, 0x00, 0x00, 0xE6, 0x01, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x3D, 0x02, 0x00, 0x00, 0x3C, 0x02, 0x00, 0x00, 0xE9, 0x01, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0xE6, 0x01, 0x00, 0x00, 0x3D, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0xEB, 0x01, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xED, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3E, 0x02, 0x00, 0x00, 0xE5, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3F, 0x02, 0x00, 0x00, 0xDA, 0x01, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, - 0x3F, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x41, 0x02, 0x00, 0x00, - 0x3E, 0x02, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x41, 0x02, 0x00, 0x00, - 0x38, 0x00, 0x01, 0x00, 0x36, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x5D, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x44, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, 0x64, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x6E, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x79, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, 0x45, 0x02, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x44, 0x02, 0x00, 0x00, 0x45, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x47, 0x02, 0x00, 0x00, 0x44, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x48, 0x02, 0x00, 0x00, 0x47, 0x02, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x49, 0x02, 0x00, 0x00, 0x48, 0x02, 0x00, 0x00, - 0x46, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x4A, 0x02, 0x00, 0x00, - 0x44, 0x02, 0x00, 0x00, 0xB8, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x4A, 0x02, 0x00, 0x00, - 0x49, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x4C, 0x02, 0x00, 0x00, - 0x44, 0x02, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4D, 0x02, 0x00, 0x00, 0x4C, 0x02, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x4E, 0x02, 0x00, 0x00, 0x4D, 0x02, 0x00, 0x00, 0x4B, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x4F, 0x02, 0x00, 0x00, 0x44, 0x02, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x4F, 0x02, 0x00, 0x00, 0x4E, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x52, 0x02, 0x00, 0x00, 0x55, 0x02, 0x00, 0x00, 0x54, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, 0x44, 0x02, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x57, 0x02, 0x00, 0x00, 0x56, 0x02, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x5B, 0x02, 0x00, 0x00, 0x5C, 0x02, 0x00, 0x00, 0x5A, 0x02, 0x00, 0x00, - 0xE9, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5D, 0x02, 0x00, 0x00, - 0x5C, 0x02, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x00, - 0x5D, 0x02, 0x00, 0x00, 0x5D, 0x02, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x5F, 0x02, 0x00, 0x00, 0x57, 0x02, 0x00, 0x00, 0x5E, 0x02, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, 0x55, 0x02, 0x00, 0x00, 0x5F, 0x02, 0x00, 0x00, - 0x51, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x61, 0x02, 0x00, 0x00, 0x60, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x62, 0x02, 0x00, 0x00, - 0x61, 0x02, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x63, 0x02, 0x00, 0x00, 0x62, 0x02, 0x00, 0x00, 0x62, 0x02, 0x00, 0x00, 0x62, 0x02, 0x00, 0x00, - 0x62, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x50, 0x02, 0x00, 0x00, 0x63, 0x02, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x17, 0x00, 0x00, 0x00, 0x68, 0x02, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, - 0xB8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x69, 0x02, 0x00, 0x00, - 0x68, 0x02, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x10, 0x00, 0x00, 0x00, 0x6A, 0x02, 0x00, 0x00, - 0x67, 0x02, 0x00, 0x00, 0x69, 0x02, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x5B, 0x02, 0x00, 0x00, - 0x6B, 0x02, 0x00, 0x00, 0x5A, 0x02, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x6C, 0x02, 0x00, 0x00, 0x6B, 0x02, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x6D, 0x02, 0x00, 0x00, 0x6A, 0x02, 0x00, 0x00, 0x6C, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x64, 0x02, 0x00, 0x00, 0x6D, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x52, 0x02, 0x00, 0x00, 0x70, 0x02, 0x00, 0x00, 0x6F, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x72, 0x02, 0x00, 0x00, 0x64, 0x02, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x73, 0x02, 0x00, 0x00, 0x71, 0x02, 0x00, 0x00, 0x72, 0x02, 0x00, 0x00, - 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x74, 0x02, 0x00, 0x00, 0x70, 0x02, 0x00, 0x00, - 0x73, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x6E, 0x02, 0x00, 0x00, 0x74, 0x02, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x5B, 0x02, 0x00, 0x00, 0x76, 0x02, 0x00, 0x00, 0x5A, 0x02, 0x00, 0x00, - 0x75, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x77, 0x02, 0x00, 0x00, - 0x76, 0x02, 0x00, 0x00, 0xBA, 0x00, 0x05, 0x00, 0x99, 0x00, 0x00, 0x00, 0x78, 0x02, 0x00, 0x00, - 0x77, 0x02, 0x00, 0x00, 0x98, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x7B, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0x78, 0x02, 0x00, 0x00, 0x7A, 0x02, 0x00, 0x00, - 0x7F, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7A, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x7C, 0x02, 0x00, 0x00, 0x50, 0x02, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x7E, 0x02, 0x00, 0x00, 0x7C, 0x02, 0x00, 0x00, 0x7D, 0x02, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x79, 0x02, 0x00, 0x00, 0x7E, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, - 0x7B, 0x02, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x7F, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0x6E, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x79, 0x02, 0x00, 0x00, 0x80, 0x02, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x7B, 0x02, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x7B, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x81, 0x02, 0x00, 0x00, 0x79, 0x02, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x6E, 0x02, 0x00, 0x00, - 0x81, 0x02, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x82, 0x02, 0x00, 0x00, - 0x6E, 0x02, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x82, 0x02, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, +#ifndef __EMSCRIPTEN__ +static const uint8_t s_waves_shd_bytecode_draw_content[22992] = { + 0x03,0x02,0x23,0x07,0x00,0x03,0x01,0x00,0x0B,0x00,0x08,0x00,0xBD,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0B,0x00,0x06,0x00, + 0x01,0x00,0x00,0x00,0x47,0x4C,0x53,0x4C,0x2E,0x73,0x74,0x64,0x2E,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0F,0x00,0x11,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, + 0x00,0x00,0x00,0x00,0x0B,0x01,0x00,0x00,0x8B,0x02,0x00,0x00,0x92,0x02,0x00,0x00, + 0x9F,0x02,0x00,0x00,0x04,0x03,0x00,0x00,0x15,0x03,0x00,0x00,0x16,0x03,0x00,0x00, + 0x5E,0x03,0x00,0x00,0x67,0x03,0x00,0x00,0x70,0x03,0x00,0x00,0x9F,0x03,0x00,0x00, + 0xA8,0x03,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xC2,0x01,0x00,0x00,0x04,0x00,0x09,0x00, + 0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x68,0x61,0x64,0x69,0x6E,0x67,0x5F,0x6C, + 0x61,0x6E,0x67,0x75,0x61,0x67,0x65,0x5F,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x00, + 0x04,0x00,0x0A,0x00,0x47,0x4C,0x5F,0x47,0x4F,0x4F,0x47,0x4C,0x45,0x5F,0x63,0x70, + 0x70,0x5F,0x73,0x74,0x79,0x6C,0x65,0x5F,0x6C,0x69,0x6E,0x65,0x5F,0x64,0x69,0x72, + 0x65,0x63,0x74,0x69,0x76,0x65,0x00,0x00,0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00, + 0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0B,0x00,0x00,0x00, + 0x67,0x61,0x6D,0x6D,0x61,0x28,0x76,0x66,0x34,0x3B,0x00,0x00,0x05,0x00,0x03,0x00, + 0x0A,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x0E,0x00,0x00,0x00, + 0x64,0x65,0x5F,0x67,0x61,0x6D,0x6D,0x61,0x28,0x76,0x66,0x34,0x3B,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x0D,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x07,0x00, + 0x15,0x00,0x00,0x00,0x73,0x6D,0x6F,0x6F,0x74,0x68,0x5F,0x75,0x76,0x28,0x76,0x66, + 0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x13,0x00,0x00,0x00, + 0x75,0x76,0x00,0x00,0x05,0x00,0x06,0x00,0x14,0x00,0x00,0x00,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x1B,0x00,0x00,0x00,0x73,0x61,0x66,0x65,0x5F,0x64,0x69,0x76,0x28,0x66,0x31,0x3B, + 0x66,0x31,0x3B,0x00,0x05,0x00,0x03,0x00,0x19,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x1A,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x1F,0x00,0x00,0x00,0x73,0x61,0x66,0x65,0x5F,0x6C,0x65,0x6E,0x28,0x76,0x66,0x32, + 0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x1E,0x00,0x00,0x00,0x76,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x23,0x00,0x00,0x00,0x73,0x6B,0x65,0x77,0x28,0x76,0x66,0x32, + 0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x22,0x00,0x00,0x00,0x76,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x28,0x00,0x00,0x00,0x64,0x65,0x74,0x32,0x28,0x76,0x66,0x32, + 0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x26,0x00,0x00,0x00, + 0x61,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x27,0x00,0x00,0x00,0x62,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x2C,0x00,0x00,0x00,0x73,0x64,0x66,0x5F,0x73,0x74,0x72,0x6F, + 0x6B,0x65,0x28,0x66,0x31,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x2B,0x00,0x00,0x00, + 0x64,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x32,0x00,0x00,0x00,0x73,0x64,0x66,0x28, + 0x76,0x66,0x34,0x3B,0x76,0x66,0x34,0x3B,0x66,0x31,0x3B,0x00,0x05,0x00,0x03,0x00, + 0x2F,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x30,0x00,0x00,0x00, + 0x62,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x31,0x00,0x00,0x00,0x64,0x00,0x00,0x00, + 0x05,0x00,0x08,0x00,0x36,0x00,0x00,0x00,0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65, + 0x5F,0x61,0x61,0x62,0x62,0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00, + 0x05,0x00,0x03,0x00,0x34,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x35,0x00,0x00,0x00,0x68,0x65,0x00,0x00,0x05,0x00,0x0A,0x00,0x3D,0x00,0x00,0x00, + 0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x5F,0x62,0x6F,0x78,0x28,0x76,0x66,0x32, + 0x3B,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x39,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x3A,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x3B,0x00,0x00,0x00, + 0x68,0x65,0x00,0x00,0x05,0x00,0x03,0x00,0x3C,0x00,0x00,0x00,0x75,0x00,0x00,0x00, + 0x05,0x00,0x0A,0x00,0x43,0x00,0x00,0x00,0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65, + 0x5F,0x73,0x65,0x67,0x6D,0x65,0x6E,0x74,0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32, + 0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x40,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x41,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x42,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x05,0x00,0x0B,0x00, + 0x49,0x00,0x00,0x00,0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x5F,0x74,0x72,0x69, + 0x61,0x6E,0x67,0x6C,0x65,0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x76,0x66, + 0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x45,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x46,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x47,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x48,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x0A,0x00,0x55,0x00,0x00,0x00, + 0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x5F,0x70,0x6F,0x6C,0x79,0x67,0x6F,0x6E, + 0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x5B,0x38,0x5D,0x3B,0x69,0x31,0x3B,0x00, + 0x05,0x00,0x03,0x00,0x52,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x53,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x54,0x00,0x00,0x00, + 0x4E,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x57,0x00,0x00,0x00,0x53,0x68,0x61,0x64, + 0x65,0x72,0x50,0x61,0x72,0x61,0x6D,0x73,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00, + 0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x76,0x69,0x65,0x77,0x5F,0x70,0x6F,0x73, + 0x00,0x00,0x00,0x00,0x06,0x00,0x04,0x00,0x57,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x75,0x76,0x00,0x00,0x06,0x00,0x05,0x00,0x57,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x75,0x76,0x5F,0x6D,0x69,0x6E,0x00,0x00,0x06,0x00,0x05,0x00,0x57,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x75,0x76,0x5F,0x6D,0x61,0x78,0x00,0x00,0x06,0x00,0x06,0x00, + 0x57,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75, + 0x76,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x57,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x73,0x00,0x00,0x05,0x00,0x11,0x00, + 0x5C,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x65,0x72,0x28,0x76,0x66,0x34,0x3B,0x73, + 0x74,0x72,0x75,0x63,0x74,0x2D,0x53,0x68,0x61,0x64,0x65,0x72,0x50,0x61,0x72,0x61, + 0x6D,0x73,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76, + 0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x34,0x31,0x3B,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x5A,0x00,0x00,0x00,0x63,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x5B,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x73,0x00,0x00, + 0x05,0x00,0x04,0x00,0x7C,0x00,0x00,0x00,0x70,0x69,0x78,0x65,0x6C,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x80,0x00,0x00,0x00,0x73,0x65,0x61,0x6D,0x00,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0xA5,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0xCE,0x00,0x00,0x00,0x76,0x5F,0x73,0x74,0x72,0x6F,0x6B,0x65,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xD3,0x00,0x00,0x00,0x77,0x69,0x72,0x65,0x5F,0x64,0x00,0x00, + 0x05,0x00,0x04,0x00,0xD4,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0xD7,0x00,0x00,0x00,0x73,0x74,0x72,0x6F,0x6B,0x65,0x5F,0x61, + 0x61,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xDA,0x00,0x00,0x00,0x76,0x5F,0x61,0x61, + 0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0xE0,0x00,0x00,0x00,0x73,0x74,0x72,0x6F, + 0x6B,0x65,0x5F,0x6E,0x6F,0x5F,0x61,0x61,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xE8,0x00,0x00,0x00,0x66,0x69,0x6C,0x6C,0x5F,0x61,0x61,0x00,0x05,0x00,0x05,0x00, + 0xF0,0x00,0x00,0x00,0x66,0x69,0x6C,0x6C,0x5F,0x6E,0x6F,0x5F,0x61,0x61,0x00,0x00, + 0x05,0x00,0x04,0x00,0xFA,0x00,0x00,0x00,0x73,0x74,0x72,0x6F,0x6B,0x65,0x00,0x00, + 0x05,0x00,0x04,0x00,0x02,0x01,0x00,0x00,0x66,0x69,0x6C,0x6C,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x0B,0x01,0x00,0x00,0x72,0x65,0x73,0x75,0x6C,0x74,0x00,0x00, + 0x05,0x00,0x04,0x00,0x0E,0x01,0x00,0x00,0x76,0x5F,0x66,0x69,0x6C,0x6C,0x00,0x00, + 0x05,0x00,0x03,0x00,0x15,0x01,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x29,0x01,0x00,0x00,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x2B,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x3C,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x3E,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x43,0x01,0x00,0x00, + 0x6E,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x47,0x01,0x00,0x00,0x70,0x61,0x00,0x00, + 0x05,0x00,0x03,0x00,0x4B,0x01,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x52,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x53,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x55,0x01,0x00,0x00,0x68,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x5D,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x61,0x01,0x00,0x00, + 0x65,0x30,0x00,0x00,0x05,0x00,0x03,0x00,0x65,0x01,0x00,0x00,0x65,0x31,0x00,0x00, + 0x05,0x00,0x03,0x00,0x69,0x01,0x00,0x00,0x65,0x32,0x00,0x00,0x05,0x00,0x03,0x00, + 0x6D,0x01,0x00,0x00,0x76,0x30,0x00,0x00,0x05,0x00,0x03,0x00,0x71,0x01,0x00,0x00, + 0x76,0x31,0x00,0x00,0x05,0x00,0x03,0x00,0x75,0x01,0x00,0x00,0x76,0x32,0x00,0x00, + 0x05,0x00,0x03,0x00,0x79,0x01,0x00,0x00,0x70,0x71,0x30,0x00,0x05,0x00,0x04,0x00, + 0x82,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x83,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x88,0x01,0x00,0x00,0x70,0x71,0x31,0x00,0x05,0x00,0x04,0x00,0x91,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x92,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x97,0x01,0x00,0x00, + 0x70,0x71,0x32,0x00,0x05,0x00,0x04,0x00,0xA0,0x01,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xA1,0x01,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xA6,0x01,0x00,0x00,0x73,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xA7,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xA9,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0xAC,0x01,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xB1,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xB3,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xBC,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xBE,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xC8,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xCA,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0xDA,0x01,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xE5,0x01,0x00,0x00, + 0x73,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xE6,0x01,0x00,0x00,0x69,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0xE7,0x01,0x00,0x00,0x6A,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0xF3,0x01,0x00,0x00,0x65,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xFB,0x01,0x00,0x00, + 0x77,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x01,0x02,0x00,0x00,0x62,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x15,0x02,0x00,0x00,0x63,0x6F,0x6E,0x64,0x00,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x44,0x02,0x00,0x00,0x6E,0x6F,0x69,0x73,0x65,0x5F,0x75,0x76, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x52,0x02,0x00,0x00,0x6E,0x6F,0x69,0x73, + 0x65,0x5F,0x63,0x00,0x05,0x00,0x05,0x00,0x56,0x02,0x00,0x00,0x6E,0x6F,0x69,0x73, + 0x65,0x5F,0x74,0x65,0x78,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x5A,0x02,0x00,0x00, + 0x73,0x68,0x64,0x5F,0x75,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x73,0x00,0x00,0x00,0x00, + 0x06,0x00,0x06,0x00,0x5A,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x61,0x6D,0x70,0x6C, + 0x69,0x74,0x75,0x64,0x65,0x00,0x00,0x00,0x06,0x00,0x05,0x00,0x5A,0x02,0x00,0x00, + 0x01,0x00,0x00,0x00,0x74,0x69,0x6D,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00, + 0x5A,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x73,0x68,0x6F,0x77,0x5F,0x6E,0x6F,0x69, + 0x73,0x65,0x00,0x00,0x05,0x00,0x03,0x00,0x5C,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x66,0x02,0x00,0x00,0x6F,0x66,0x66,0x73,0x65,0x74,0x00,0x00, + 0x05,0x00,0x03,0x00,0x70,0x02,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0x71,0x02,0x00,0x00,0x77,0x61,0x74,0x65,0x72,0x5F,0x74,0x65,0x78,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x89,0x02,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x8B,0x02,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x5F,0x75,0x76, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x8E,0x02,0x00,0x00,0x76,0x5F,0x75,0x76, + 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x91,0x02,0x00,0x00,0x76,0x5F,0x72,0x61, + 0x64,0x69,0x75,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x92,0x02,0x00,0x00, + 0x76,0x5F,0x73,0x68,0x61,0x70,0x65,0x00,0x05,0x00,0x04,0x00,0x9B,0x02,0x00,0x00, + 0x76,0x5F,0x74,0x79,0x70,0x65,0x00,0x00,0x05,0x00,0x04,0x00,0x9E,0x02,0x00,0x00, + 0x76,0x5F,0x61,0x6C,0x70,0x68,0x61,0x00,0x05,0x00,0x06,0x00,0x9F,0x02,0x00,0x00, + 0x76,0x5F,0x62,0x6C,0x65,0x6E,0x64,0x5F,0x70,0x6F,0x73,0x48,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xA4,0x02,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x48,0x00,0x00, + 0x05,0x00,0x05,0x00,0xA8,0x02,0x00,0x00,0x69,0x73,0x5F,0x73,0x70,0x72,0x69,0x74, + 0x65,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xAE,0x02,0x00,0x00,0x69,0x73,0x5F,0x74, + 0x65,0x78,0x74,0x00,0x05,0x00,0x04,0x00,0xB5,0x02,0x00,0x00,0x69,0x73,0x5F,0x62, + 0x6F,0x78,0x00,0x00,0x05,0x00,0x04,0x00,0xBC,0x02,0x00,0x00,0x69,0x73,0x5F,0x73, + 0x65,0x67,0x00,0x00,0x05,0x00,0x04,0x00,0xC3,0x02,0x00,0x00,0x69,0x73,0x5F,0x74, + 0x72,0x69,0x00,0x00,0x05,0x00,0x05,0x00,0xCA,0x02,0x00,0x00,0x69,0x73,0x5F,0x74, + 0x72,0x69,0x5F,0x73,0x64,0x66,0x00,0x00,0x05,0x00,0x04,0x00,0xD1,0x02,0x00,0x00, + 0x69,0x73,0x5F,0x70,0x6F,0x6C,0x79,0x00,0x05,0x00,0x03,0x00,0xD8,0x02,0x00,0x00, + 0x63,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xDA,0x02,0x00,0x00,0x75,0x76,0x00,0x00, + 0x05,0x00,0x06,0x00,0xDB,0x02,0x00,0x00,0x75,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x5F, + 0x62,0x6C,0x6F,0x63,0x6B,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0xDB,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x75,0x5F,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5F,0x73,0x69, + 0x7A,0x65,0x00,0x00,0x06,0x00,0x07,0x00,0xDB,0x02,0x00,0x00,0x01,0x00,0x00,0x00, + 0x75,0x5F,0x61,0x6C,0x70,0x68,0x61,0x5F,0x64,0x69,0x73,0x63,0x61,0x72,0x64,0x00, + 0x06,0x00,0x07,0x00,0xDB,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x75,0x5F,0x75,0x73, + 0x65,0x5F,0x73,0x6D,0x6F,0x6F,0x74,0x68,0x5F,0x75,0x76,0x00,0x05,0x00,0x03,0x00, + 0xDD,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xE5,0x02,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xE7,0x02,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xEF,0x02,0x00,0x00, + 0x74,0x65,0x78,0x5F,0x63,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xF0,0x02,0x00,0x00, + 0x75,0x5F,0x69,0x6D,0x61,0x67,0x65,0x00,0x05,0x00,0x04,0x00,0xF4,0x02,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xFA,0x02,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x04,0x03,0x00,0x00, + 0x76,0x5F,0x63,0x6F,0x6C,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x11,0x03,0x00,0x00, + 0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x15,0x03,0x00,0x00,0x76,0x5F,0x61,0x62, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x16,0x03,0x00,0x00,0x76,0x5F,0x63,0x64, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x17,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x19,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x1C,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x1F,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x27,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x29,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x2C,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x31,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x33,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x36,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x3F,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x41,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x44,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x47,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x50,0x03,0x00,0x00,0x70,0x74,0x73,0x00, + 0x05,0x00,0x04,0x00,0x5E,0x03,0x00,0x00,0x76,0x5F,0x65,0x66,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x67,0x03,0x00,0x00,0x76,0x5F,0x67,0x68,0x00,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x70,0x03,0x00,0x00,0x76,0x5F,0x6E,0x00,0x05,0x00,0x04,0x00, + 0x71,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x73,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x75,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x86,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x88,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x8A,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x92,0x03,0x00,0x00,0x70,0x6F,0x73,0x00,0x05,0x00,0x05,0x00,0x94,0x03,0x00,0x00, + 0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75,0x76,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x9A,0x03,0x00,0x00,0x73,0x70,0x00,0x00,0x05,0x00,0x05,0x00,0x9F,0x03,0x00,0x00, + 0x76,0x5F,0x75,0x76,0x5F,0x62,0x6F,0x75,0x6E,0x64,0x73,0x00,0x05,0x00,0x04,0x00, + 0xA8,0x03,0x00,0x00,0x76,0x5F,0x75,0x73,0x65,0x72,0x00,0x00,0x05,0x00,0x04,0x00, + 0xAB,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xAD,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x0B,0x01,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x56,0x02,0x00,0x00,0x21,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x56,0x02,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x5A,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x5A,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x5A,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x5A,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x5C,0x02,0x00,0x00,0x21,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x5C,0x02,0x00,0x00,0x22,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x71,0x02,0x00,0x00,0x21,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x71,0x02,0x00,0x00,0x22,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x8B,0x02,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x92,0x02,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x9F,0x02,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0xDB,0x02,0x00,0x00,0x02,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0xDB,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0xDB,0x02,0x00,0x00,0x01,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0xDB,0x02,0x00,0x00, + 0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0xDD,0x02,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0xDD,0x02,0x00,0x00,0x22,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0xF0,0x02,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0xF0,0x02,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x04,0x03,0x00,0x00,0x1E,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x15,0x03,0x00,0x00,0x1E,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x16,0x03,0x00,0x00,0x1E,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x5E,0x03,0x00,0x00,0x1E,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x67,0x03,0x00,0x00,0x1E,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x70,0x03,0x00,0x00,0x0E,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x70,0x03,0x00,0x00, + 0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x9F,0x03,0x00,0x00, + 0x1E,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xA8,0x03,0x00,0x00, + 0x1E,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00, + 0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x21,0x00,0x04,0x00,0x09,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x21,0x00,0x05,0x00,0x12,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x17,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x21,0x00,0x05,0x00, + 0x18,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x21,0x00,0x04,0x00,0x1D,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x21,0x00,0x04,0x00,0x21,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x21,0x00,0x05,0x00,0x25,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x21,0x00,0x04,0x00,0x2A,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x21,0x00,0x06,0x00,0x2E,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x21,0x00,0x07,0x00, + 0x38,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x06,0x00,0x3F,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x15,0x00,0x04,0x00,0x4B,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x4B,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x1C,0x00,0x04,0x00,0x4D,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x4C,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x4E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x4D,0x00,0x00,0x00, + 0x15,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x50,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x4F,0x00,0x00,0x00, + 0x21,0x00,0x06,0x00,0x51,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x4E,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x1E,0x00,0x08,0x00,0x57,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x58,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x21,0x00,0x05,0x00,0x59,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x5E,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x2F,0xBA,0xE8,0x3E,0x2C,0x00,0x06,0x00, + 0x5E,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x62,0x00,0x00,0x00, + 0x62,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4B,0x00,0x00,0x00,0x65,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x71,0x00,0x00,0x00, + 0xCD,0xCC,0x0C,0x40,0x2C,0x00,0x06,0x00,0x5E,0x00,0x00,0x00,0x72,0x00,0x00,0x00, + 0x71,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x82,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x8D,0x00,0x00,0x00,0x00,0x00,0x00,0xBF,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x02,0x00, + 0x99,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4B,0x00,0x00,0x00,0xB4,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4B,0x00,0x00,0x00,0xB8,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0xCD,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xCD,0x00,0x00,0x00,0xCE,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xCD,0x00,0x00,0x00,0xDA,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0xE5,0x00,0x00,0x00,0x99,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xF2,0x00,0x00,0x00, + 0x00,0x00,0x80,0xBF,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xF3,0x00,0x00,0x00, + 0x00,0x00,0x80,0x3F,0x20,0x00,0x04,0x00,0x0A,0x01,0x00,0x00,0x03,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x01,0x00,0x00,0x0B,0x01,0x00,0x00, + 0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xCD,0x00,0x00,0x00,0x0E,0x01,0x00,0x00, + 0x06,0x00,0x00,0x00,0x18,0x00,0x04,0x00,0x27,0x01,0x00,0x00,0x10,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x28,0x01,0x00,0x00,0x07,0x00,0x00,0x00, + 0x27,0x01,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0xDC,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0xE9,0x01,0x00,0x00, + 0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x13,0x02,0x00,0x00,0x99,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x14,0x02,0x00,0x00,0x07,0x00,0x00,0x00, + 0x13,0x02,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x45,0x02,0x00,0x00, + 0x04,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x48,0x02,0x00,0x00, + 0x00,0x00,0xA0,0x40,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x4D,0x02,0x00,0x00, + 0x00,0x00,0x70,0x40,0x19,0x00,0x09,0x00,0x53,0x02,0x00,0x00,0x06,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0x00,0x03,0x00,0x54,0x02,0x00,0x00, + 0x53,0x02,0x00,0x00,0x20,0x00,0x04,0x00,0x55,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0x54,0x02,0x00,0x00,0x3B,0x00,0x04,0x00,0x55,0x02,0x00,0x00,0x56,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x1E,0x00,0x05,0x00,0x5A,0x02,0x00,0x00,0x06,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x5B,0x02,0x00,0x00, + 0x02,0x00,0x00,0x00,0x5A,0x02,0x00,0x00,0x3B,0x00,0x04,0x00,0x5B,0x02,0x00,0x00, + 0x5C,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x5D,0x02,0x00,0x00, + 0x02,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x67,0x02,0x00,0x00,0xCD,0xCC,0xCC,0x3A,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x68,0x02,0x00,0x00,0x89,0x88,0x08,0x3B,0x2C,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x69,0x02,0x00,0x00,0x67,0x02,0x00,0x00,0x68,0x02,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x55,0x02,0x00,0x00,0x71,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x78,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x2C,0x00,0x07,0x00, + 0x07,0x00,0x00,0x00,0x80,0x02,0x00,0x00,0x82,0x00,0x00,0x00,0x82,0x00,0x00,0x00, + 0x82,0x00,0x00,0x00,0x82,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x88,0x02,0x00,0x00, + 0x06,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x88,0x02,0x00,0x00, + 0x89,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x8A,0x02,0x00,0x00, + 0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x8A,0x02,0x00,0x00, + 0x8B,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x88,0x02,0x00,0x00, + 0x8E,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xCD,0x00,0x00,0x00, + 0x91,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x8A,0x02,0x00,0x00, + 0x92,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x93,0x02,0x00,0x00, + 0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4B,0x00,0x00,0x00, + 0x98,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xCD,0x00,0x00,0x00, + 0x9B,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xCD,0x00,0x00,0x00, + 0x9E,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x8A,0x02,0x00,0x00, + 0x9F,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x88,0x02,0x00,0x00, + 0xA4,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0xA7,0x02,0x00,0x00, + 0x07,0x00,0x00,0x00,0x99,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xB2,0x02,0x00,0x00,0x00,0x00,0xC0,0x3F,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xB9,0x02,0x00,0x00,0x00,0x00,0x20,0x40,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xC0,0x02,0x00,0x00,0x00,0x00,0x60,0x40,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xC7,0x02,0x00,0x00,0x00,0x00,0x90,0x40,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xCE,0x02,0x00,0x00,0x00,0x00,0xB0,0x40,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xD5,0x02,0x00,0x00,0x00,0x00,0xD0,0x40,0x2C,0x00,0x07,0x00,0x07,0x00,0x00,0x00, + 0xD9,0x02,0x00,0x00,0x98,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x98,0x00,0x00,0x00,0x1E,0x00,0x05,0x00,0xDB,0x02,0x00,0x00,0x10,0x00,0x00,0x00, + 0x4F,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0xDC,0x02,0x00,0x00, + 0x02,0x00,0x00,0x00,0xDB,0x02,0x00,0x00,0x3B,0x00,0x04,0x00,0xDC,0x02,0x00,0x00, + 0xDD,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0xDE,0x02,0x00,0x00, + 0x02,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0xE8,0x02,0x00,0x00, + 0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x55,0x02,0x00,0x00, + 0xF0,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x8A,0x02,0x00,0x00, + 0x04,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x8A,0x02,0x00,0x00, + 0x15,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x8A,0x02,0x00,0x00, + 0x16,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x4F,0x03,0x00,0x00, + 0x06,0x00,0x00,0x00,0x4D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x4F,0x03,0x00,0x00, + 0x50,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0x5A,0x03,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x8A,0x02,0x00,0x00, + 0x5E,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0x62,0x03,0x00,0x00,0x05,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0x66,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x8A,0x02,0x00,0x00, + 0x67,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0x6B,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x6F,0x03,0x00,0x00, + 0x01,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x6F,0x03,0x00,0x00, + 0x70,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x88,0x02,0x00,0x00, + 0x92,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x88,0x02,0x00,0x00, + 0x94,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x2C,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x96,0x03,0x00,0x00,0xF3,0x00,0x00,0x00,0xF2,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x8A,0x02,0x00,0x00,0x9F,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x8A,0x02,0x00,0x00,0xA8,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xA7,0x02,0x00,0x00, + 0xA8,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xA7,0x02,0x00,0x00, + 0xAE,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xA7,0x02,0x00,0x00, + 0xB5,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xA7,0x02,0x00,0x00, + 0xBC,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xA7,0x02,0x00,0x00, + 0xC3,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xA7,0x02,0x00,0x00, + 0xCA,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xA7,0x02,0x00,0x00, + 0xD1,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0xD8,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xDA,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xE2,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xE5,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0xE7,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0xEF,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0xF4,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0xF7,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0xFA,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x01,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x11,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x17,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x19,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x1C,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x1F,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x27,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x29,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x2C,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x31,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x33,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x36,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x3F,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x41,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x44,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x47,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x71,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x4E,0x00,0x00,0x00, + 0x73,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x50,0x00,0x00,0x00, + 0x75,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x80,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x86,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x88,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x8A,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x58,0x00,0x00,0x00, + 0x9A,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0xAB,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x58,0x00,0x00,0x00, + 0xAD,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x8C,0x02,0x00,0x00,0x8B,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x8D,0x02,0x00,0x00,0x8C,0x02,0x00,0x00,0x8C,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x89,0x02,0x00,0x00,0x8D,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x8F,0x02,0x00,0x00,0x8B,0x02,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x90,0x02,0x00,0x00,0x8F,0x02,0x00,0x00, + 0x8F,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x8E,0x02,0x00,0x00,0x90,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x93,0x02,0x00,0x00, + 0x94,0x02,0x00,0x00,0x92,0x02,0x00,0x00,0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x95,0x02,0x00,0x00,0x94,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x91,0x02,0x00,0x00,0x95,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x93,0x02,0x00,0x00, + 0x96,0x02,0x00,0x00,0x92,0x02,0x00,0x00,0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x97,0x02,0x00,0x00,0x96,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xCE,0x00,0x00,0x00,0x97,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x93,0x02,0x00,0x00, + 0x99,0x02,0x00,0x00,0x92,0x02,0x00,0x00,0x98,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x9A,0x02,0x00,0x00,0x99,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xDA,0x00,0x00,0x00,0x9A,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x93,0x02,0x00,0x00, + 0x9C,0x02,0x00,0x00,0x92,0x02,0x00,0x00,0x65,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x9D,0x02,0x00,0x00,0x9C,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x9B,0x02,0x00,0x00,0x9D,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x93,0x02,0x00,0x00, + 0xA0,0x02,0x00,0x00,0x9F,0x02,0x00,0x00,0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xA1,0x02,0x00,0x00,0xA0,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x9E,0x02,0x00,0x00,0xA1,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x93,0x02,0x00,0x00, + 0xA2,0x02,0x00,0x00,0x9F,0x02,0x00,0x00,0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xA3,0x02,0x00,0x00,0xA2,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x0E,0x01,0x00,0x00,0xA3,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xA5,0x02,0x00,0x00,0x9F,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0xA6,0x02,0x00,0x00,0xA5,0x02,0x00,0x00,0xA5,0x02,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xA4,0x02,0x00,0x00,0xA6,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xA9,0x02,0x00,0x00,0x9B,0x02,0x00,0x00, + 0xBE,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xAA,0x02,0x00,0x00,0xA9,0x02,0x00,0x00, + 0x98,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xAB,0x02,0x00,0x00, + 0x9B,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xAC,0x02,0x00,0x00, + 0xAB,0x02,0x00,0x00,0x82,0x00,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0xAD,0x02,0x00,0x00,0xAA,0x02,0x00,0x00,0xAC,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xA8,0x02,0x00,0x00,0xAD,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xAF,0x02,0x00,0x00,0x9B,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0xB0,0x02,0x00,0x00,0xAF,0x02,0x00,0x00,0x82,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xB1,0x02,0x00,0x00,0x9B,0x02,0x00,0x00,0xB8,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0xB3,0x02,0x00,0x00,0xB1,0x02,0x00,0x00,0xB2,0x02,0x00,0x00, + 0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xB4,0x02,0x00,0x00,0xB0,0x02,0x00,0x00, + 0xB3,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xAE,0x02,0x00,0x00,0xB4,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xB6,0x02,0x00,0x00,0x9B,0x02,0x00,0x00, + 0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xB7,0x02,0x00,0x00,0xB6,0x02,0x00,0x00, + 0xB2,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xB8,0x02,0x00,0x00, + 0x9B,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xBA,0x02,0x00,0x00, + 0xB8,0x02,0x00,0x00,0xB9,0x02,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0xBB,0x02,0x00,0x00,0xB7,0x02,0x00,0x00,0xBA,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xB5,0x02,0x00,0x00,0xBB,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xBD,0x02,0x00,0x00,0x9B,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0xBE,0x02,0x00,0x00,0xBD,0x02,0x00,0x00,0xB9,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xBF,0x02,0x00,0x00,0x9B,0x02,0x00,0x00,0xB8,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0xC1,0x02,0x00,0x00,0xBF,0x02,0x00,0x00,0xC0,0x02,0x00,0x00, + 0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xC2,0x02,0x00,0x00,0xBE,0x02,0x00,0x00, + 0xC1,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xBC,0x02,0x00,0x00,0xC2,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xC4,0x02,0x00,0x00,0x9B,0x02,0x00,0x00, + 0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xC5,0x02,0x00,0x00,0xC4,0x02,0x00,0x00, + 0xC0,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xC6,0x02,0x00,0x00, + 0x9B,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xC8,0x02,0x00,0x00, + 0xC6,0x02,0x00,0x00,0xC7,0x02,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0xC9,0x02,0x00,0x00,0xC5,0x02,0x00,0x00,0xC8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xC3,0x02,0x00,0x00,0xC9,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xCB,0x02,0x00,0x00,0x9B,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0xCC,0x02,0x00,0x00,0xCB,0x02,0x00,0x00,0xC7,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xCD,0x02,0x00,0x00,0x9B,0x02,0x00,0x00,0xB8,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0xCF,0x02,0x00,0x00,0xCD,0x02,0x00,0x00,0xCE,0x02,0x00,0x00, + 0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xD0,0x02,0x00,0x00,0xCC,0x02,0x00,0x00, + 0xCF,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xCA,0x02,0x00,0x00,0xD0,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xD2,0x02,0x00,0x00,0x9B,0x02,0x00,0x00, + 0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xD3,0x02,0x00,0x00,0xD2,0x02,0x00,0x00, + 0xCE,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xD4,0x02,0x00,0x00, + 0x9B,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xD6,0x02,0x00,0x00, + 0xD4,0x02,0x00,0x00,0xD5,0x02,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0xD7,0x02,0x00,0x00,0xD3,0x02,0x00,0x00,0xD6,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xD1,0x02,0x00,0x00,0xD7,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xD8,0x02,0x00,0x00, + 0xD9,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0xDE,0x02,0x00,0x00,0xDF,0x02,0x00,0x00, + 0xDD,0x02,0x00,0x00,0x78,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0xE0,0x02,0x00,0x00,0xDF,0x02,0x00,0x00,0xAA,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0xE1,0x02,0x00,0x00,0xE0,0x02,0x00,0x00,0xDC,0x01,0x00,0x00,0xF7,0x00,0x03,0x00, + 0xE4,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0xE1,0x02,0x00,0x00, + 0xE3,0x02,0x00,0x00,0xEC,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xE3,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xE6,0x02,0x00,0x00,0x8E,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xE5,0x02,0x00,0x00,0xE6,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0xE8,0x02,0x00,0x00,0xE9,0x02,0x00,0x00,0xDD,0x02,0x00,0x00,0xDC,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xEA,0x02,0x00,0x00,0xE9,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xE7,0x02,0x00,0x00,0xEA,0x02,0x00,0x00,0x39,0x00,0x06,0x00, + 0x10,0x00,0x00,0x00,0xEB,0x02,0x00,0x00,0x15,0x00,0x00,0x00,0xE5,0x02,0x00,0x00, + 0xE7,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xE2,0x02,0x00,0x00,0xEB,0x02,0x00,0x00, + 0xF9,0x00,0x02,0x00,0xE4,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xEC,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xED,0x02,0x00,0x00,0x8E,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xE2,0x02,0x00,0x00,0xED,0x02,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xE4,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xE4,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xEE,0x02,0x00,0x00,0xE2,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xDA,0x02,0x00,0x00,0xEE,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x54,0x02,0x00,0x00, + 0xF1,0x02,0x00,0x00,0xF0,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xF2,0x02,0x00,0x00,0xDA,0x02,0x00,0x00,0x57,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0xF3,0x02,0x00,0x00,0xF1,0x02,0x00,0x00,0xF2,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xF4,0x02,0x00,0x00,0xF3,0x02,0x00,0x00,0x39,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0xF5,0x02,0x00,0x00,0x0E,0x00,0x00,0x00,0xF4,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xEF,0x02,0x00,0x00,0xF5,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00, + 0xF6,0x02,0x00,0x00,0xA8,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0xF9,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0xF6,0x02,0x00,0x00,0xF8,0x02,0x00,0x00, + 0xFD,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xF8,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xFB,0x02,0x00,0x00,0xEF,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xFA,0x02,0x00,0x00,0xFB,0x02,0x00,0x00,0x39,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0xFC,0x02,0x00,0x00,0x0B,0x00,0x00,0x00,0xFA,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xF7,0x02,0x00,0x00,0xFC,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xF9,0x02,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xFD,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xFE,0x02,0x00,0x00,0xD8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xF7,0x02,0x00,0x00, + 0xFE,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xF9,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xF9,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xFF,0x02,0x00,0x00, + 0xF7,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xD8,0x02,0x00,0x00,0xFF,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0xAE,0x02,0x00,0x00, + 0xF7,0x00,0x03,0x00,0x03,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0x00,0x03,0x00,0x00,0x02,0x03,0x00,0x00,0x09,0x03,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x02,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x05,0x03,0x00,0x00, + 0x04,0x03,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x06,0x03,0x00,0x00, + 0xEF,0x02,0x00,0x00,0x65,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x07,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x8E,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x08,0x03,0x00,0x00,0x05,0x03,0x00,0x00,0x07,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x01,0x03,0x00,0x00,0x08,0x03,0x00,0x00,0xF9,0x00,0x02,0x00,0x03,0x03,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x09,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x0A,0x03,0x00,0x00,0xD8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x01,0x03,0x00,0x00, + 0x0A,0x03,0x00,0x00,0xF9,0x00,0x02,0x00,0x03,0x03,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x03,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0B,0x03,0x00,0x00, + 0x01,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0xD8,0x02,0x00,0x00,0x0B,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x0C,0x03,0x00,0x00,0xC3,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0D,0x03,0x00,0x00,0x04,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0E,0x03,0x00,0x00,0xD8,0x02,0x00,0x00, + 0x50,0x00,0x07,0x00,0xE5,0x00,0x00,0x00,0x0F,0x03,0x00,0x00,0x0C,0x03,0x00,0x00, + 0x0C,0x03,0x00,0x00,0x0C,0x03,0x00,0x00,0x0C,0x03,0x00,0x00,0xA9,0x00,0x06,0x00, + 0x07,0x00,0x00,0x00,0x10,0x03,0x00,0x00,0x0F,0x03,0x00,0x00,0x0D,0x03,0x00,0x00, + 0x0E,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0xD8,0x02,0x00,0x00,0x10,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x11,0x03,0x00,0x00,0x98,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x99,0x00,0x00,0x00,0x12,0x03,0x00,0x00,0xB5,0x02,0x00,0x00,0xF7,0x00,0x03,0x00, + 0x14,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x12,0x03,0x00,0x00, + 0x13,0x03,0x00,0x00,0x23,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x13,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x18,0x03,0x00,0x00,0x89,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x17,0x03,0x00,0x00,0x18,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x1A,0x03,0x00,0x00,0x15,0x03,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x1B,0x03,0x00,0x00,0x1A,0x03,0x00,0x00,0x1A,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x19,0x03,0x00,0x00, + 0x1B,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1D,0x03,0x00,0x00, + 0x15,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x1E,0x03,0x00,0x00, + 0x1D,0x03,0x00,0x00,0x1D,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x1C,0x03,0x00,0x00,0x1E,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x20,0x03,0x00,0x00,0x16,0x03,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x21,0x03,0x00,0x00,0x20,0x03,0x00,0x00,0x20,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x1F,0x03,0x00,0x00, + 0x21,0x03,0x00,0x00,0x39,0x00,0x08,0x00,0x06,0x00,0x00,0x00,0x22,0x03,0x00,0x00, + 0x3D,0x00,0x00,0x00,0x17,0x03,0x00,0x00,0x19,0x03,0x00,0x00,0x1C,0x03,0x00,0x00, + 0x1F,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x11,0x03,0x00,0x00,0x22,0x03,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x14,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x23,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x24,0x03,0x00,0x00,0xBC,0x02,0x00,0x00, + 0xF7,0x00,0x03,0x00,0x26,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0x24,0x03,0x00,0x00,0x25,0x03,0x00,0x00,0x3B,0x03,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x25,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x28,0x03,0x00,0x00, + 0x89,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x27,0x03,0x00,0x00,0x28,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x2A,0x03,0x00,0x00,0x15,0x03,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x2B,0x03,0x00,0x00,0x2A,0x03,0x00,0x00, + 0x2A,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x29,0x03,0x00,0x00,0x2B,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x2D,0x03,0x00,0x00,0x15,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x2E,0x03,0x00,0x00,0x2D,0x03,0x00,0x00,0x2D,0x03,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x2C,0x03,0x00,0x00,0x2E,0x03,0x00,0x00, + 0x39,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0x2F,0x03,0x00,0x00,0x43,0x00,0x00,0x00, + 0x27,0x03,0x00,0x00,0x29,0x03,0x00,0x00,0x2C,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x11,0x03,0x00,0x00,0x2F,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x30,0x03,0x00,0x00,0x11,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x32,0x03,0x00,0x00,0x89,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x31,0x03,0x00,0x00, + 0x32,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x34,0x03,0x00,0x00, + 0x15,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x35,0x03,0x00,0x00, + 0x34,0x03,0x00,0x00,0x34,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x33,0x03,0x00,0x00,0x35,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x37,0x03,0x00,0x00,0x16,0x03,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x38,0x03,0x00,0x00,0x37,0x03,0x00,0x00,0x37,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x36,0x03,0x00,0x00, + 0x38,0x03,0x00,0x00,0x39,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0x39,0x03,0x00,0x00, + 0x43,0x00,0x00,0x00,0x31,0x03,0x00,0x00,0x33,0x03,0x00,0x00,0x36,0x03,0x00,0x00, + 0x0C,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0x3A,0x03,0x00,0x00,0x01,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x30,0x03,0x00,0x00,0x39,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x11,0x03,0x00,0x00,0x3A,0x03,0x00,0x00,0xF9,0x00,0x02,0x00,0x26,0x03,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x3B,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00, + 0x3C,0x03,0x00,0x00,0xCA,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0x3E,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x3C,0x03,0x00,0x00,0x3D,0x03,0x00,0x00, + 0x4B,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x3D,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x40,0x03,0x00,0x00,0x89,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x3F,0x03,0x00,0x00,0x40,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x42,0x03,0x00,0x00,0x15,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x43,0x03,0x00,0x00,0x42,0x03,0x00,0x00,0x42,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x41,0x03,0x00,0x00,0x43,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x45,0x03,0x00,0x00,0x15,0x03,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x46,0x03,0x00,0x00,0x45,0x03,0x00,0x00, + 0x45,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x44,0x03,0x00,0x00,0x46,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x48,0x03,0x00,0x00,0x16,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x49,0x03,0x00,0x00,0x48,0x03,0x00,0x00,0x48,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x47,0x03,0x00,0x00,0x49,0x03,0x00,0x00, + 0x39,0x00,0x08,0x00,0x06,0x00,0x00,0x00,0x4A,0x03,0x00,0x00,0x49,0x00,0x00,0x00, + 0x3F,0x03,0x00,0x00,0x41,0x03,0x00,0x00,0x44,0x03,0x00,0x00,0x47,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x11,0x03,0x00,0x00,0x4A,0x03,0x00,0x00,0xF9,0x00,0x02,0x00, + 0x3E,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x4B,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x99,0x00,0x00,0x00,0x4C,0x03,0x00,0x00,0xD1,0x02,0x00,0x00,0xF7,0x00,0x03,0x00, + 0x4E,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x4C,0x03,0x00,0x00, + 0x4D,0x03,0x00,0x00,0x4E,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x4D,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x51,0x03,0x00,0x00,0x15,0x03,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x52,0x03,0x00,0x00,0x51,0x03,0x00,0x00, + 0x51,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x88,0x02,0x00,0x00,0x53,0x03,0x00,0x00,0x50,0x03,0x00,0x00,0xDC,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x53,0x03,0x00,0x00,0x52,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x54,0x03,0x00,0x00,0x15,0x03,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x55,0x03,0x00,0x00,0x54,0x03,0x00,0x00,0x54,0x03,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x88,0x02,0x00,0x00, + 0x56,0x03,0x00,0x00,0x50,0x03,0x00,0x00,0xE9,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x56,0x03,0x00,0x00,0x55,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x57,0x03,0x00,0x00,0x16,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x58,0x03,0x00,0x00,0x57,0x03,0x00,0x00,0x57,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x88,0x02,0x00,0x00,0x59,0x03,0x00,0x00, + 0x50,0x03,0x00,0x00,0x78,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x59,0x03,0x00,0x00, + 0x58,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x5B,0x03,0x00,0x00, + 0x16,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x5C,0x03,0x00,0x00, + 0x5B,0x03,0x00,0x00,0x5B,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x88,0x02,0x00,0x00,0x5D,0x03,0x00,0x00,0x50,0x03,0x00,0x00, + 0x5A,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x5D,0x03,0x00,0x00,0x5C,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x5F,0x03,0x00,0x00,0x5E,0x03,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x60,0x03,0x00,0x00,0x5F,0x03,0x00,0x00, + 0x5F,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x88,0x02,0x00,0x00,0x61,0x03,0x00,0x00,0x50,0x03,0x00,0x00,0x45,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x61,0x03,0x00,0x00,0x60,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x63,0x03,0x00,0x00,0x5E,0x03,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x64,0x03,0x00,0x00,0x63,0x03,0x00,0x00,0x63,0x03,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x88,0x02,0x00,0x00, + 0x65,0x03,0x00,0x00,0x50,0x03,0x00,0x00,0x62,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x65,0x03,0x00,0x00,0x64,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x68,0x03,0x00,0x00,0x67,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x69,0x03,0x00,0x00,0x68,0x03,0x00,0x00,0x68,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x88,0x02,0x00,0x00,0x6A,0x03,0x00,0x00, + 0x50,0x03,0x00,0x00,0x66,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x6A,0x03,0x00,0x00, + 0x69,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x6C,0x03,0x00,0x00, + 0x67,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x6D,0x03,0x00,0x00, + 0x6C,0x03,0x00,0x00,0x6C,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x88,0x02,0x00,0x00,0x6E,0x03,0x00,0x00,0x50,0x03,0x00,0x00, + 0x6B,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x6E,0x03,0x00,0x00,0x6D,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x72,0x03,0x00,0x00,0x89,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x71,0x03,0x00,0x00,0x72,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x4D,0x00,0x00,0x00,0x74,0x03,0x00,0x00,0x50,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x73,0x03,0x00,0x00,0x74,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0x76,0x03,0x00,0x00,0x70,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x75,0x03,0x00,0x00, + 0x76,0x03,0x00,0x00,0x39,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0x77,0x03,0x00,0x00, + 0x55,0x00,0x00,0x00,0x71,0x03,0x00,0x00,0x73,0x03,0x00,0x00,0x75,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x11,0x03,0x00,0x00,0x77,0x03,0x00,0x00,0xF9,0x00,0x02,0x00, + 0x4E,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x4E,0x03,0x00,0x00,0xF9,0x00,0x02,0x00, + 0x3E,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x3E,0x03,0x00,0x00,0xF9,0x00,0x02,0x00, + 0x26,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x26,0x03,0x00,0x00,0xF9,0x00,0x02,0x00, + 0x14,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x14,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x99,0x00,0x00,0x00,0x78,0x03,0x00,0x00,0xA8,0x02,0x00,0x00,0xA8,0x00,0x04,0x00, + 0x99,0x00,0x00,0x00,0x79,0x03,0x00,0x00,0x78,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x99,0x00,0x00,0x00,0x7A,0x03,0x00,0x00,0xAE,0x02,0x00,0x00,0xA8,0x00,0x04,0x00, + 0x99,0x00,0x00,0x00,0x7B,0x03,0x00,0x00,0x7A,0x03,0x00,0x00,0xA7,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x7C,0x03,0x00,0x00,0x79,0x03,0x00,0x00,0x7B,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x7D,0x03,0x00,0x00,0xC3,0x02,0x00,0x00, + 0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x7E,0x03,0x00,0x00,0x7D,0x03,0x00,0x00, + 0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x7F,0x03,0x00,0x00,0x7C,0x03,0x00,0x00, + 0x7E,0x03,0x00,0x00,0xF7,0x00,0x03,0x00,0x82,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFA,0x00,0x04,0x00,0x7F,0x03,0x00,0x00,0x81,0x03,0x00,0x00,0x8C,0x03,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x81,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x83,0x03,0x00,0x00,0x11,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x84,0x03,0x00,0x00,0x91,0x02,0x00,0x00,0x83,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x85,0x03,0x00,0x00,0x83,0x03,0x00,0x00,0x84,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x87,0x03,0x00,0x00,0xD8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x86,0x03,0x00,0x00,0x87,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x89,0x03,0x00,0x00,0x04,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x88,0x03,0x00,0x00, + 0x89,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x8A,0x03,0x00,0x00,0x85,0x03,0x00,0x00, + 0x39,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x8B,0x03,0x00,0x00,0x32,0x00,0x00,0x00, + 0x86,0x03,0x00,0x00,0x88,0x03,0x00,0x00,0x8A,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x80,0x03,0x00,0x00,0x8B,0x03,0x00,0x00,0xF9,0x00,0x02,0x00,0x82,0x03,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x8C,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x8D,0x03,0x00,0x00,0xD8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x80,0x03,0x00,0x00, + 0x8D,0x03,0x00,0x00,0xF9,0x00,0x02,0x00,0x82,0x03,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x82,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x8E,0x03,0x00,0x00, + 0x80,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0xD8,0x02,0x00,0x00,0x8E,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x8F,0x03,0x00,0x00,0x9E,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x90,0x03,0x00,0x00,0xD8,0x02,0x00,0x00, + 0x8E,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x91,0x03,0x00,0x00,0x90,0x03,0x00,0x00, + 0x8F,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0xD8,0x02,0x00,0x00,0x91,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x93,0x03,0x00,0x00,0x89,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x92,0x03,0x00,0x00,0x93,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x95,0x03,0x00,0x00,0xA4,0x02,0x00,0x00,0x81,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x97,0x03,0x00,0x00,0x95,0x03,0x00,0x00,0x96,0x03,0x00,0x00, + 0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x98,0x03,0x00,0x00,0x97,0x03,0x00,0x00, + 0x82,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x99,0x03,0x00,0x00, + 0x98,0x03,0x00,0x00,0x96,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x94,0x03,0x00,0x00, + 0x99,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x9B,0x03,0x00,0x00, + 0x92,0x03,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x9C,0x03,0x00,0x00, + 0x9A,0x03,0x00,0x00,0xDC,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x9C,0x03,0x00,0x00, + 0x9B,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x9D,0x03,0x00,0x00, + 0x8E,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x9E,0x03,0x00,0x00, + 0x9A,0x03,0x00,0x00,0xE9,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x9E,0x03,0x00,0x00, + 0x9D,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xA0,0x03,0x00,0x00, + 0x9F,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xA1,0x03,0x00,0x00, + 0xA0,0x03,0x00,0x00,0xA0,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0xA2,0x03,0x00,0x00,0x9A,0x03,0x00,0x00, + 0x78,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xA2,0x03,0x00,0x00,0xA1,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xA3,0x03,0x00,0x00,0x9F,0x03,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xA4,0x03,0x00,0x00,0xA3,0x03,0x00,0x00, + 0xA3,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x11,0x00,0x00,0x00,0xA5,0x03,0x00,0x00,0x9A,0x03,0x00,0x00,0x5A,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xA5,0x03,0x00,0x00,0xA4,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xA6,0x03,0x00,0x00,0x94,0x03,0x00,0x00,0x41,0x00,0x05,0x00, + 0x11,0x00,0x00,0x00,0xA7,0x03,0x00,0x00,0x9A,0x03,0x00,0x00,0x45,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xA7,0x03,0x00,0x00,0xA6,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xA9,0x03,0x00,0x00,0xA8,0x03,0x00,0x00,0x41,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0xAA,0x03,0x00,0x00,0x9A,0x03,0x00,0x00,0x62,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xAA,0x03,0x00,0x00,0xA9,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xAC,0x03,0x00,0x00,0xD8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xAB,0x03,0x00,0x00,0xAC,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x57,0x00,0x00,0x00, + 0xAE,0x03,0x00,0x00,0x9A,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0xAD,0x03,0x00,0x00, + 0xAE,0x03,0x00,0x00,0x39,0x00,0x06,0x00,0x07,0x00,0x00,0x00,0xAF,0x03,0x00,0x00, + 0x5C,0x00,0x00,0x00,0xAB,0x03,0x00,0x00,0xAD,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xD8,0x02,0x00,0x00,0xAF,0x03,0x00,0x00,0x41,0x00,0x05,0x00,0xDE,0x02,0x00,0x00, + 0xB0,0x03,0x00,0x00,0xDD,0x02,0x00,0x00,0xE9,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xB1,0x03,0x00,0x00,0xB0,0x03,0x00,0x00,0xAB,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0xB2,0x03,0x00,0x00,0xB1,0x03,0x00,0x00,0xDC,0x01,0x00,0x00, + 0xF7,0x00,0x03,0x00,0xB4,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0xB2,0x03,0x00,0x00,0xB3,0x03,0x00,0x00,0xB4,0x03,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xB3,0x03,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0xB5,0x03,0x00,0x00, + 0xD8,0x02,0x00,0x00,0x65,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xB6,0x03,0x00,0x00,0xB5,0x03,0x00,0x00,0xB4,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0xB7,0x03,0x00,0x00,0xB6,0x03,0x00,0x00,0x98,0x00,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xB4,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0xB4,0x03,0x00,0x00,0xF5,0x00,0x07,0x00, + 0x99,0x00,0x00,0x00,0xB8,0x03,0x00,0x00,0xB2,0x03,0x00,0x00,0x82,0x03,0x00,0x00, + 0xB7,0x03,0x00,0x00,0xB3,0x03,0x00,0x00,0xF7,0x00,0x03,0x00,0xBA,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0xB8,0x03,0x00,0x00,0xB9,0x03,0x00,0x00, + 0xBA,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0xB9,0x03,0x00,0x00,0xFC,0x00,0x01,0x00, + 0xF8,0x00,0x02,0x00,0xBA,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xBC,0x03,0x00,0x00,0xD8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x0B,0x01,0x00,0x00, + 0xBC,0x03,0x00,0x00,0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x0C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x5F,0x00,0x00,0x00, + 0x0A,0x00,0x00,0x00,0x4F,0x00,0x08,0x00,0x5E,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x5F,0x00,0x00,0x00,0x5F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x0C,0x00,0x06,0x00,0x5E,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x0C,0x00,0x07,0x00, + 0x5E,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1A,0x00,0x00,0x00, + 0x61,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0x66,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x64,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x6A,0x00,0x00,0x00, + 0x64,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00, + 0x6B,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x6A,0x00,0x00,0x00, + 0x67,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0x6B,0x00,0x00,0x00,0x38,0x00,0x01,0x00, + 0x36,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x0D,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x0F,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x6E,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x4F,0x00,0x08,0x00,0x5E,0x00,0x00,0x00, + 0x6F,0x00,0x00,0x00,0x6E,0x00,0x00,0x00,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0C,0x00,0x06,0x00,0x5E,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6F,0x00,0x00,0x00, + 0x0C,0x00,0x07,0x00,0x5E,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x1A,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x72,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0x74,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x65,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x75,0x00,0x00,0x00,0x74,0x00,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x73,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x77,0x00,0x00,0x00, + 0x73,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x78,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0x07,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x77,0x00,0x00,0x00, + 0x78,0x00,0x00,0x00,0x75,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0x79,0x00,0x00,0x00, + 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x15,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x16,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x7C,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x7D,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x7E,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x7F,0x00,0x00,0x00,0x7D,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x7C,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x81,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x83,0x00,0x00,0x00,0x82,0x00,0x00,0x00,0x82,0x00,0x00,0x00,0x81,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x81,0x00,0x00,0x00,0x83,0x00,0x00,0x00, + 0x0C,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0x85,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x80,0x00,0x00,0x00, + 0x85,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x86,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x87,0x00,0x00,0x00, + 0x7C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x89,0x00,0x00,0x00, + 0x87,0x00,0x00,0x00,0x88,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x8A,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xD1,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x8B,0x00,0x00,0x00,0x8A,0x00,0x00,0x00,0x88,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x8C,0x00,0x00,0x00,0x89,0x00,0x00,0x00,0x8B,0x00,0x00,0x00,0x50,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x8E,0x00,0x00,0x00,0x8D,0x00,0x00,0x00,0x8D,0x00,0x00,0x00, + 0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x8F,0x00,0x00,0x00,0x82,0x00,0x00,0x00, + 0x82,0x00,0x00,0x00,0x0C,0x00,0x08,0x00,0x10,0x00,0x00,0x00,0x90,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x8C,0x00,0x00,0x00,0x8E,0x00,0x00,0x00, + 0x8F,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x91,0x00,0x00,0x00, + 0x86,0x00,0x00,0x00,0x90,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x7C,0x00,0x00,0x00, + 0x91,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x92,0x00,0x00,0x00, + 0x7C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x93,0x00,0x00,0x00, + 0x14,0x00,0x00,0x00,0x88,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x94,0x00,0x00,0x00, + 0x92,0x00,0x00,0x00,0x93,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0x94,0x00,0x00,0x00, + 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x1B,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x17,0x00,0x00,0x00, + 0x19,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x17,0x00,0x00,0x00,0x1A,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x1C,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x9B,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x97,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0xB4,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0x9A,0x00,0x00,0x00,0x97,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0xF7,0x00,0x03,0x00, + 0x9D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x9A,0x00,0x00,0x00, + 0x9C,0x00,0x00,0x00,0x9E,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x9C,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x9B,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0xF9,0x00,0x02,0x00, + 0x9D,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x9E,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x9F,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xA0,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x88,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0xA1,0x00,0x00,0x00,0x9F,0x00,0x00,0x00,0xA0,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x9B,0x00,0x00,0x00,0xA1,0x00,0x00,0x00,0xF9,0x00,0x02,0x00, + 0x9D,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x9D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xA2,0x00,0x00,0x00,0x9B,0x00,0x00,0x00,0xFE,0x00,0x02,0x00, + 0xA2,0x00,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x11,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x20,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xA5,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xAB,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xA6,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xA7,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xA8,0x00,0x00,0x00,0xA6,0x00,0x00,0x00, + 0xA7,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xA5,0x00,0x00,0x00,0xA8,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xA9,0x00,0x00,0x00,0xA5,0x00,0x00,0x00, + 0xB4,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xAA,0x00,0x00,0x00,0xA9,0x00,0x00,0x00, + 0x98,0x00,0x00,0x00,0xF7,0x00,0x03,0x00,0xAD,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFA,0x00,0x04,0x00,0xAA,0x00,0x00,0x00,0xAC,0x00,0x00,0x00,0xAE,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xAC,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xAB,0x00,0x00,0x00, + 0x98,0x00,0x00,0x00,0xF9,0x00,0x02,0x00,0xAD,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xAE,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xAF,0x00,0x00,0x00, + 0xA5,0x00,0x00,0x00,0x0C,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0xB0,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0xAF,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xAB,0x00,0x00,0x00,0xB0,0x00,0x00,0x00,0xF9,0x00,0x02,0x00,0xAD,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xAD,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xB1,0x00,0x00,0x00,0xAB,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0xB1,0x00,0x00,0x00, + 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x24,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0xB5,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0xB4,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xB6,0x00,0x00,0x00,0xB5,0x00,0x00,0x00, + 0x7F,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xB7,0x00,0x00,0x00,0xB6,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0xB9,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xBA,0x00,0x00,0x00, + 0xB9,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0xBB,0x00,0x00,0x00, + 0xB7,0x00,0x00,0x00,0xBA,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0xBB,0x00,0x00,0x00, + 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x26,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x27,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x29,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0xBE,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xBF,0x00,0x00,0x00,0xBE,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0xB4,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xC1,0x00,0x00,0x00,0xC0,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xC2,0x00,0x00,0x00,0xBF,0x00,0x00,0x00, + 0xC1,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0xC3,0x00,0x00,0x00, + 0x26,0x00,0x00,0x00,0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xC4,0x00,0x00,0x00,0xC3,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0xC5,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xC6,0x00,0x00,0x00,0xC5,0x00,0x00,0x00,0x85,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0xC7,0x00,0x00,0x00,0xC4,0x00,0x00,0x00,0xC6,0x00,0x00,0x00, + 0x83,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xC8,0x00,0x00,0x00,0xC2,0x00,0x00,0x00, + 0xC7,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0xC8,0x00,0x00,0x00,0x38,0x00,0x01,0x00, + 0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x2C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2A,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x17,0x00,0x00,0x00,0x2B,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x2D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xCB,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x0C,0x00,0x06,0x00,0x06,0x00,0x00,0x00, + 0xCC,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xCB,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xCF,0x00,0x00,0x00,0xCE,0x00,0x00,0x00, + 0x83,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xD0,0x00,0x00,0x00,0xCC,0x00,0x00,0x00, + 0xCF,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0xD0,0x00,0x00,0x00,0x38,0x00,0x01,0x00, + 0x36,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2E,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x2F,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x17,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x33,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xD3,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xD4,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xD7,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xE8,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xFA,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x02,0x01,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xD5,0x00,0x00,0x00,0x31,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xD4,0x00,0x00,0x00,0xD5,0x00,0x00,0x00,0x39,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0xD6,0x00,0x00,0x00,0x2C,0x00,0x00,0x00,0xD4,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xD3,0x00,0x00,0x00,0xD6,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xD9,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xDB,0x00,0x00,0x00,0xDA,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xDC,0x00,0x00,0x00,0xD3,0x00,0x00,0x00,0x0C,0x00,0x08,0x00, + 0x06,0x00,0x00,0x00,0xDD,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x31,0x00,0x00,0x00, + 0x98,0x00,0x00,0x00,0xDB,0x00,0x00,0x00,0xDC,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0x07,0x00,0x00,0x00,0xDE,0x00,0x00,0x00,0xDD,0x00,0x00,0x00,0xDD,0x00,0x00,0x00, + 0xDD,0x00,0x00,0x00,0xDD,0x00,0x00,0x00,0x0C,0x00,0x08,0x00,0x07,0x00,0x00,0x00, + 0xDF,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0xD8,0x00,0x00,0x00, + 0xD9,0x00,0x00,0x00,0xDE,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xD7,0x00,0x00,0x00, + 0xDF,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xE1,0x00,0x00,0x00, + 0xD3,0x00,0x00,0x00,0xBC,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xE2,0x00,0x00,0x00, + 0xE1,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xE3,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xE4,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0xE5,0x00,0x00,0x00, + 0xE6,0x00,0x00,0x00,0xE2,0x00,0x00,0x00,0xE2,0x00,0x00,0x00,0xE2,0x00,0x00,0x00, + 0xE2,0x00,0x00,0x00,0xA9,0x00,0x06,0x00,0x07,0x00,0x00,0x00,0xE7,0x00,0x00,0x00, + 0xE6,0x00,0x00,0x00,0xE3,0x00,0x00,0x00,0xE4,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xE0,0x00,0x00,0x00,0xE7,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xE9,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xEA,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xEB,0x00,0x00,0x00,0xDA,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xEC,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x0C,0x00,0x08,0x00,0x06,0x00,0x00,0x00, + 0xED,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0xEB,0x00,0x00,0x00,0xEC,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00, + 0xEE,0x00,0x00,0x00,0xED,0x00,0x00,0x00,0xED,0x00,0x00,0x00,0xED,0x00,0x00,0x00, + 0xED,0x00,0x00,0x00,0x0C,0x00,0x08,0x00,0x07,0x00,0x00,0x00,0xEF,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0xE9,0x00,0x00,0x00,0xEA,0x00,0x00,0x00, + 0xEE,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xE8,0x00,0x00,0x00,0xEF,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xF1,0x00,0x00,0x00,0x31,0x00,0x00,0x00, + 0x0C,0x00,0x08,0x00,0x06,0x00,0x00,0x00,0xF4,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2B,0x00,0x00,0x00,0xF1,0x00,0x00,0x00,0xF2,0x00,0x00,0x00,0xF3,0x00,0x00,0x00, + 0xBC,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xF5,0x00,0x00,0x00,0xF4,0x00,0x00,0x00, + 0x98,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xF6,0x00,0x00,0x00, + 0x30,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xF7,0x00,0x00,0x00, + 0x2F,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0xE5,0x00,0x00,0x00,0xF8,0x00,0x00,0x00, + 0xF5,0x00,0x00,0x00,0xF5,0x00,0x00,0x00,0xF5,0x00,0x00,0x00,0xF5,0x00,0x00,0x00, + 0xA9,0x00,0x06,0x00,0x07,0x00,0x00,0x00,0xF9,0x00,0x00,0x00,0xF8,0x00,0x00,0x00, + 0xF6,0x00,0x00,0x00,0xF7,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xF0,0x00,0x00,0x00, + 0xF9,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xFB,0x00,0x00,0x00, + 0xD7,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xFC,0x00,0x00,0x00, + 0xD7,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xFD,0x00,0x00,0x00, + 0xDA,0x00,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0xFE,0x00,0x00,0x00, + 0xFD,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0xA9,0x00,0x06,0x00,0x06,0x00,0x00,0x00, + 0xFF,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xF3,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0xFF,0x00,0x00,0x00, + 0xFF,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x0C,0x00,0x08,0x00, + 0x07,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2E,0x00,0x00,0x00, + 0xFB,0x00,0x00,0x00,0xFC,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xFA,0x00,0x00,0x00,0x01,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x03,0x01,0x00,0x00,0xF0,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x04,0x01,0x00,0x00,0xE8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x05,0x01,0x00,0x00,0xDA,0x00,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0x06,0x01,0x00,0x00,0x05,0x01,0x00,0x00,0x98,0x00,0x00,0x00,0xA9,0x00,0x06,0x00, + 0x06,0x00,0x00,0x00,0x07,0x01,0x00,0x00,0x06,0x01,0x00,0x00,0xF3,0x00,0x00,0x00, + 0x98,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x08,0x01,0x00,0x00, + 0x07,0x01,0x00,0x00,0x07,0x01,0x00,0x00,0x07,0x01,0x00,0x00,0x07,0x01,0x00,0x00, + 0x0C,0x00,0x08,0x00,0x07,0x00,0x00,0x00,0x09,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2E,0x00,0x00,0x00,0x03,0x01,0x00,0x00,0x04,0x01,0x00,0x00,0x08,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x02,0x01,0x00,0x00,0x09,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x0C,0x01,0x00,0x00,0xFA,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x0D,0x01,0x00,0x00,0x02,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x0F,0x01,0x00,0x00,0x0E,0x01,0x00,0x00,0x50,0x00,0x07,0x00, + 0x07,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x0F,0x01,0x00,0x00,0x0F,0x01,0x00,0x00, + 0x0F,0x01,0x00,0x00,0x0F,0x01,0x00,0x00,0x0C,0x00,0x08,0x00,0x07,0x00,0x00,0x00, + 0x11,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0x0C,0x01,0x00,0x00, + 0x0D,0x01,0x00,0x00,0x10,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x0B,0x01,0x00,0x00, + 0x11,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x12,0x01,0x00,0x00, + 0x0B,0x01,0x00,0x00,0xFE,0x00,0x02,0x00,0x12,0x01,0x00,0x00,0x38,0x00,0x01,0x00, + 0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x34,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x37,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x15,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x16,0x01,0x00,0x00, + 0x34,0x00,0x00,0x00,0x0C,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0x17,0x01,0x00,0x00, + 0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x16,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x18,0x01,0x00,0x00,0x35,0x00,0x00,0x00,0x83,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x19,0x01,0x00,0x00,0x17,0x01,0x00,0x00,0x18,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x15,0x01,0x00,0x00,0x19,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x1A,0x01,0x00,0x00,0x15,0x01,0x00,0x00,0x50,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x1B,0x01,0x00,0x00,0x98,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x0C,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x1C,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x28,0x00,0x00,0x00,0x1A,0x01,0x00,0x00,0x1B,0x01,0x00,0x00,0x0C,0x00,0x06,0x00, + 0x06,0x00,0x00,0x00,0x1D,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x42,0x00,0x00,0x00, + 0x1C,0x01,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x1E,0x01,0x00,0x00, + 0x15,0x01,0x00,0x00,0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x1F,0x01,0x00,0x00,0x1E,0x01,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0x20,0x01,0x00,0x00,0x15,0x01,0x00,0x00,0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x21,0x01,0x00,0x00,0x20,0x01,0x00,0x00,0x0C,0x00,0x07,0x00, + 0x06,0x00,0x00,0x00,0x22,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x1F,0x01,0x00,0x00,0x21,0x01,0x00,0x00,0x0C,0x00,0x07,0x00,0x06,0x00,0x00,0x00, + 0x23,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x22,0x01,0x00,0x00, + 0x98,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x24,0x01,0x00,0x00, + 0x1D,0x01,0x00,0x00,0x23,0x01,0x00,0x00,0xFE,0x00,0x02,0x00,0x24,0x01,0x00,0x00, + 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x3D,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x39,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x3A,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x3B,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x11,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x3E,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x28,0x01,0x00,0x00,0x29,0x01,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x2B,0x01,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x3C,0x01,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x3E,0x01,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x2A,0x01,0x00,0x00,0x3C,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x2C,0x01,0x00,0x00,0x3C,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x2B,0x01,0x00,0x00,0x2C,0x01,0x00,0x00,0x39,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x2D,0x01,0x00,0x00,0x23,0x00,0x00,0x00,0x2B,0x01,0x00,0x00, + 0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x2E,0x01,0x00,0x00,0x2A,0x01,0x00,0x00, + 0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x2F,0x01,0x00,0x00, + 0x2A,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x30,0x01,0x00,0x00,0x2D,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x31,0x01,0x00,0x00,0x2D,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x32,0x01,0x00,0x00,0x2E,0x01,0x00,0x00, + 0x2F,0x01,0x00,0x00,0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x33,0x01,0x00,0x00, + 0x30,0x01,0x00,0x00,0x31,0x01,0x00,0x00,0x50,0x00,0x05,0x00,0x27,0x01,0x00,0x00, + 0x34,0x01,0x00,0x00,0x32,0x01,0x00,0x00,0x33,0x01,0x00,0x00,0x54,0x00,0x04,0x00, + 0x27,0x01,0x00,0x00,0x35,0x01,0x00,0x00,0x34,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x29,0x01,0x00,0x00,0x35,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x36,0x01,0x00,0x00,0x39,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x37,0x01,0x00,0x00,0x3A,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x38,0x01,0x00,0x00,0x36,0x01,0x00,0x00,0x37,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x39,0x00,0x00,0x00,0x38,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x27,0x01,0x00,0x00, + 0x39,0x01,0x00,0x00,0x29,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x3A,0x01,0x00,0x00,0x39,0x00,0x00,0x00,0x91,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x3B,0x01,0x00,0x00,0x39,0x01,0x00,0x00,0x3A,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x39,0x00,0x00,0x00,0x3B,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x3D,0x01,0x00,0x00,0x39,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x3C,0x01,0x00,0x00, + 0x3D,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x3F,0x01,0x00,0x00, + 0x3B,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x3E,0x01,0x00,0x00,0x3F,0x01,0x00,0x00, + 0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x40,0x01,0x00,0x00,0x36,0x00,0x00,0x00, + 0x3C,0x01,0x00,0x00,0x3E,0x01,0x00,0x00,0xFE,0x00,0x02,0x00,0x40,0x01,0x00,0x00, + 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x43,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x41,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x44,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x43,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x47,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x4B,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x52,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x53,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x55,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x5D,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x44,0x01,0x00,0x00, + 0x42,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x45,0x01,0x00,0x00, + 0x41,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x46,0x01,0x00,0x00, + 0x44,0x01,0x00,0x00,0x45,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x43,0x01,0x00,0x00, + 0x46,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x48,0x01,0x00,0x00, + 0x40,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x49,0x01,0x00,0x00, + 0x41,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x4A,0x01,0x00,0x00, + 0x48,0x01,0x00,0x00,0x49,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x47,0x01,0x00,0x00, + 0x4A,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x4C,0x01,0x00,0x00, + 0x47,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x4D,0x01,0x00,0x00, + 0x43,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x4E,0x01,0x00,0x00, + 0x4C,0x01,0x00,0x00,0x4D,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x4F,0x01,0x00,0x00,0x43,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x50,0x01,0x00,0x00,0x43,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x51,0x01,0x00,0x00,0x4F,0x01,0x00,0x00,0x50,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x52,0x01,0x00,0x00,0x4E,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x53,0x01,0x00,0x00, + 0x51,0x01,0x00,0x00,0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x54,0x01,0x00,0x00, + 0x1B,0x00,0x00,0x00,0x52,0x01,0x00,0x00,0x53,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x4B,0x01,0x00,0x00,0x54,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x56,0x01,0x00,0x00,0x4B,0x01,0x00,0x00,0x0C,0x00,0x08,0x00,0x06,0x00,0x00,0x00, + 0x57,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x56,0x01,0x00,0x00, + 0x98,0x00,0x00,0x00,0xF3,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x55,0x01,0x00,0x00, + 0x57,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x58,0x01,0x00,0x00, + 0x47,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x59,0x01,0x00,0x00, + 0x55,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x5A,0x01,0x00,0x00, + 0x43,0x01,0x00,0x00,0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x5B,0x01,0x00,0x00, + 0x5A,0x01,0x00,0x00,0x59,0x01,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x5C,0x01,0x00,0x00,0x58,0x01,0x00,0x00,0x5B,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x5D,0x01,0x00,0x00,0x5C,0x01,0x00,0x00,0x39,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x5E,0x01,0x00,0x00,0x1F,0x00,0x00,0x00,0x5D,0x01,0x00,0x00,0xFE,0x00,0x02,0x00, + 0x5E,0x01,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x49,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x11,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x46,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x47,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x4A,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x61,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x65,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x69,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x6D,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x71,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x75,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x79,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x82,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x83,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x88,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x91,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x92,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x97,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xA0,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xA1,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xA6,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xA7,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xA9,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xAC,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xB1,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xB3,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xBC,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xBE,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xC8,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xCA,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x62,0x01,0x00,0x00, + 0x47,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x63,0x01,0x00,0x00, + 0x46,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x64,0x01,0x00,0x00, + 0x62,0x01,0x00,0x00,0x63,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x61,0x01,0x00,0x00, + 0x64,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x66,0x01,0x00,0x00, + 0x48,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x67,0x01,0x00,0x00, + 0x47,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x68,0x01,0x00,0x00, + 0x66,0x01,0x00,0x00,0x67,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x65,0x01,0x00,0x00, + 0x68,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x6A,0x01,0x00,0x00, + 0x46,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x6B,0x01,0x00,0x00, + 0x48,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x6C,0x01,0x00,0x00, + 0x6A,0x01,0x00,0x00,0x6B,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x69,0x01,0x00,0x00, + 0x6C,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x6E,0x01,0x00,0x00, + 0x45,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x6F,0x01,0x00,0x00, + 0x46,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x70,0x01,0x00,0x00, + 0x6E,0x01,0x00,0x00,0x6F,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x6D,0x01,0x00,0x00, + 0x70,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x72,0x01,0x00,0x00, + 0x45,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x73,0x01,0x00,0x00, + 0x47,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x74,0x01,0x00,0x00, + 0x72,0x01,0x00,0x00,0x73,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x71,0x01,0x00,0x00, + 0x74,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x76,0x01,0x00,0x00, + 0x45,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x77,0x01,0x00,0x00, + 0x48,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x78,0x01,0x00,0x00, + 0x76,0x01,0x00,0x00,0x77,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x75,0x01,0x00,0x00, + 0x78,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x7A,0x01,0x00,0x00, + 0x6D,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x7B,0x01,0x00,0x00, + 0x61,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x7C,0x01,0x00,0x00, + 0x6D,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x7D,0x01,0x00,0x00, + 0x61,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x7E,0x01,0x00,0x00, + 0x7C,0x01,0x00,0x00,0x7D,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x7F,0x01,0x00,0x00,0x61,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x80,0x01,0x00,0x00,0x61,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x81,0x01,0x00,0x00,0x7F,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x82,0x01,0x00,0x00,0x7E,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x83,0x01,0x00,0x00, + 0x81,0x01,0x00,0x00,0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x84,0x01,0x00,0x00, + 0x1B,0x00,0x00,0x00,0x82,0x01,0x00,0x00,0x83,0x01,0x00,0x00,0x0C,0x00,0x08,0x00, + 0x06,0x00,0x00,0x00,0x85,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00, + 0x84,0x01,0x00,0x00,0x98,0x00,0x00,0x00,0xF3,0x00,0x00,0x00,0x8E,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x86,0x01,0x00,0x00,0x7B,0x01,0x00,0x00,0x85,0x01,0x00,0x00, + 0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x87,0x01,0x00,0x00,0x7A,0x01,0x00,0x00, + 0x86,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x79,0x01,0x00,0x00,0x87,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x89,0x01,0x00,0x00,0x71,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x8A,0x01,0x00,0x00,0x65,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x8B,0x01,0x00,0x00,0x71,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x65,0x01,0x00,0x00, + 0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x8D,0x01,0x00,0x00,0x8B,0x01,0x00,0x00, + 0x8C,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x8E,0x01,0x00,0x00, + 0x65,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x8F,0x01,0x00,0x00, + 0x65,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x90,0x01,0x00,0x00, + 0x8E,0x01,0x00,0x00,0x8F,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x91,0x01,0x00,0x00, + 0x8D,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x92,0x01,0x00,0x00,0x90,0x01,0x00,0x00, + 0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0x93,0x01,0x00,0x00,0x1B,0x00,0x00,0x00, + 0x91,0x01,0x00,0x00,0x92,0x01,0x00,0x00,0x0C,0x00,0x08,0x00,0x06,0x00,0x00,0x00, + 0x94,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x93,0x01,0x00,0x00, + 0x98,0x00,0x00,0x00,0xF3,0x00,0x00,0x00,0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x95,0x01,0x00,0x00,0x8A,0x01,0x00,0x00,0x94,0x01,0x00,0x00,0x83,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x96,0x01,0x00,0x00,0x89,0x01,0x00,0x00,0x95,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x88,0x01,0x00,0x00,0x96,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x98,0x01,0x00,0x00,0x75,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x99,0x01,0x00,0x00,0x69,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x9A,0x01,0x00,0x00,0x75,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x9B,0x01,0x00,0x00,0x69,0x01,0x00,0x00,0x94,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x9C,0x01,0x00,0x00,0x9A,0x01,0x00,0x00,0x9B,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x9D,0x01,0x00,0x00,0x69,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x9E,0x01,0x00,0x00,0x69,0x01,0x00,0x00, + 0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x9F,0x01,0x00,0x00,0x9D,0x01,0x00,0x00, + 0x9E,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xA0,0x01,0x00,0x00,0x9C,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xA1,0x01,0x00,0x00,0x9F,0x01,0x00,0x00,0x39,0x00,0x06,0x00, + 0x06,0x00,0x00,0x00,0xA2,0x01,0x00,0x00,0x1B,0x00,0x00,0x00,0xA0,0x01,0x00,0x00, + 0xA1,0x01,0x00,0x00,0x0C,0x00,0x08,0x00,0x06,0x00,0x00,0x00,0xA3,0x01,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0xA2,0x01,0x00,0x00,0x98,0x00,0x00,0x00, + 0xF3,0x00,0x00,0x00,0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0xA4,0x01,0x00,0x00, + 0x99,0x01,0x00,0x00,0xA3,0x01,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0xA5,0x01,0x00,0x00,0x98,0x01,0x00,0x00,0xA4,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x97,0x01,0x00,0x00,0xA5,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xA8,0x01,0x00,0x00,0x61,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xA7,0x01,0x00,0x00, + 0xA8,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xAA,0x01,0x00,0x00, + 0x69,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xA9,0x01,0x00,0x00,0xAA,0x01,0x00,0x00, + 0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0xAB,0x01,0x00,0x00,0x28,0x00,0x00,0x00, + 0xA7,0x01,0x00,0x00,0xA9,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xA6,0x01,0x00,0x00, + 0xAB,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xAD,0x01,0x00,0x00, + 0x79,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xAE,0x01,0x00,0x00, + 0x79,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xAF,0x01,0x00,0x00, + 0xAD,0x01,0x00,0x00,0xAE,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xB0,0x01,0x00,0x00,0xA6,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xB2,0x01,0x00,0x00,0x6D,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xB1,0x01,0x00,0x00, + 0xB2,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xB4,0x01,0x00,0x00, + 0x61,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xB3,0x01,0x00,0x00,0xB4,0x01,0x00,0x00, + 0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0xB5,0x01,0x00,0x00,0x28,0x00,0x00,0x00, + 0xB1,0x01,0x00,0x00,0xB3,0x01,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0xB6,0x01,0x00,0x00,0xB0,0x01,0x00,0x00,0xB5,0x01,0x00,0x00,0x50,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0xB7,0x01,0x00,0x00,0xAF,0x01,0x00,0x00,0xB6,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xB8,0x01,0x00,0x00,0x88,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xB9,0x01,0x00,0x00,0x88,0x01,0x00,0x00, + 0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xBA,0x01,0x00,0x00,0xB8,0x01,0x00,0x00, + 0xB9,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xBB,0x01,0x00,0x00, + 0xA6,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xBD,0x01,0x00,0x00, + 0x71,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xBC,0x01,0x00,0x00,0xBD,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xBF,0x01,0x00,0x00,0x65,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xBE,0x01,0x00,0x00,0xBF,0x01,0x00,0x00,0x39,0x00,0x06,0x00, + 0x06,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x28,0x00,0x00,0x00,0xBC,0x01,0x00,0x00, + 0xBE,0x01,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xC1,0x01,0x00,0x00, + 0xBB,0x01,0x00,0x00,0xC0,0x01,0x00,0x00,0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0xC2,0x01,0x00,0x00,0xBA,0x01,0x00,0x00,0xC1,0x01,0x00,0x00,0x0C,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0xC3,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0xB7,0x01,0x00,0x00,0xC2,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xC4,0x01,0x00,0x00,0x97,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xC5,0x01,0x00,0x00,0x97,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0xC6,0x01,0x00,0x00,0xC4,0x01,0x00,0x00,0xC5,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xC7,0x01,0x00,0x00,0xA6,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xC9,0x01,0x00,0x00,0x75,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xC8,0x01,0x00,0x00,0xC9,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xCB,0x01,0x00,0x00,0x69,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xCA,0x01,0x00,0x00, + 0xCB,0x01,0x00,0x00,0x39,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0xCC,0x01,0x00,0x00, + 0x28,0x00,0x00,0x00,0xC8,0x01,0x00,0x00,0xCA,0x01,0x00,0x00,0x85,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0xCD,0x01,0x00,0x00,0xC7,0x01,0x00,0x00,0xCC,0x01,0x00,0x00, + 0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0xCE,0x01,0x00,0x00,0xC6,0x01,0x00,0x00, + 0xCD,0x01,0x00,0x00,0x0C,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xCF,0x01,0x00,0x00, + 0x01,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0xC3,0x01,0x00,0x00,0xCE,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xAC,0x01,0x00,0x00,0xCF,0x01,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0xD0,0x01,0x00,0x00,0xAC,0x01,0x00,0x00,0xB8,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xD1,0x01,0x00,0x00,0xD0,0x01,0x00,0x00, + 0x0C,0x00,0x06,0x00,0x06,0x00,0x00,0x00,0xD2,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x1F,0x00,0x00,0x00,0xD1,0x01,0x00,0x00,0x7F,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xD3,0x01,0x00,0x00,0xD2,0x01,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0xD4,0x01,0x00,0x00,0xAC,0x01,0x00,0x00,0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xD5,0x01,0x00,0x00,0xD4,0x01,0x00,0x00,0x0C,0x00,0x06,0x00, + 0x06,0x00,0x00,0x00,0xD6,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0xD5,0x01,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xD7,0x01,0x00,0x00, + 0xD3,0x01,0x00,0x00,0xD6,0x01,0x00,0x00,0xFE,0x00,0x02,0x00,0xD7,0x01,0x00,0x00, + 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x55,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x51,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, + 0x52,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x4E,0x00,0x00,0x00,0x53,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x50,0x00,0x00,0x00,0x54,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x56,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xDA,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xE5,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x50,0x00,0x00,0x00,0xE6,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x50,0x00,0x00,0x00,0xE7,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xF3,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xFB,0x01,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x01,0x02,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x14,0x02,0x00,0x00,0x15,0x02,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xDB,0x01,0x00,0x00, + 0x52,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0xDD,0x01,0x00,0x00, + 0x53,0x00,0x00,0x00,0xDC,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xDE,0x01,0x00,0x00,0xDD,0x01,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0xDF,0x01,0x00,0x00,0xDB,0x01,0x00,0x00,0xDE,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0x52,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x11,0x00,0x00,0x00,0xE1,0x01,0x00,0x00,0x53,0x00,0x00,0x00,0xDC,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xE2,0x01,0x00,0x00,0xE1,0x01,0x00,0x00, + 0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0xE3,0x01,0x00,0x00,0xE0,0x01,0x00,0x00, + 0xE2,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0xE4,0x01,0x00,0x00, + 0xDF,0x01,0x00,0x00,0xE3,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xDA,0x01,0x00,0x00, + 0xE4,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xE5,0x01,0x00,0x00,0xF3,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xE6,0x01,0x00,0x00,0xDC,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xE8,0x01,0x00,0x00,0x54,0x00,0x00,0x00,0x82,0x00,0x05,0x00, + 0x4F,0x00,0x00,0x00,0xEA,0x01,0x00,0x00,0xE8,0x01,0x00,0x00,0xE9,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xE7,0x01,0x00,0x00,0xEA,0x01,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xEB,0x01,0x00,0x00,0xF8,0x00,0x02,0x00,0xEB,0x01,0x00,0x00,0xF6,0x00,0x04,0x00, + 0xED,0x01,0x00,0x00,0xEE,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xEF,0x01,0x00,0x00,0xF8,0x00,0x02,0x00,0xEF,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xF0,0x01,0x00,0x00,0xE6,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xF1,0x01,0x00,0x00,0x54,0x00,0x00,0x00,0xB1,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0xF2,0x01,0x00,0x00,0xF0,0x01,0x00,0x00,0xF1,0x01,0x00,0x00, + 0xFA,0x00,0x04,0x00,0xF2,0x01,0x00,0x00,0xEC,0x01,0x00,0x00,0xED,0x01,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xEC,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0xF4,0x01,0x00,0x00,0xE7,0x01,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00, + 0xF5,0x01,0x00,0x00,0x53,0x00,0x00,0x00,0xF4,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xF6,0x01,0x00,0x00,0xF5,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xF7,0x01,0x00,0x00,0xE6,0x01,0x00,0x00,0x41,0x00,0x05,0x00, + 0x11,0x00,0x00,0x00,0xF8,0x01,0x00,0x00,0x53,0x00,0x00,0x00,0xF7,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xF9,0x01,0x00,0x00,0xF8,0x01,0x00,0x00, + 0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0xFA,0x01,0x00,0x00,0xF6,0x01,0x00,0x00, + 0xF9,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xF3,0x01,0x00,0x00,0xFA,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0x52,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0xFD,0x01,0x00,0x00,0xE6,0x01,0x00,0x00, + 0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0xFE,0x01,0x00,0x00,0x53,0x00,0x00,0x00, + 0xFD,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xFF,0x01,0x00,0x00, + 0xFE,0x01,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x00,0x02,0x00,0x00, + 0xFC,0x01,0x00,0x00,0xFF,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0xFB,0x01,0x00,0x00, + 0x00,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x02,0x02,0x00,0x00, + 0xFB,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x03,0x02,0x00,0x00, + 0xF3,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x04,0x02,0x00,0x00, + 0xFB,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x05,0x02,0x00,0x00, + 0xF3,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x06,0x02,0x00,0x00, + 0x04,0x02,0x00,0x00,0x05,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x07,0x02,0x00,0x00,0xF3,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x08,0x02,0x00,0x00,0xF3,0x01,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x09,0x02,0x00,0x00,0x07,0x02,0x00,0x00,0x08,0x02,0x00,0x00,0x88,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x0A,0x02,0x00,0x00,0x06,0x02,0x00,0x00,0x09,0x02,0x00,0x00, + 0x0C,0x00,0x08,0x00,0x06,0x00,0x00,0x00,0x0B,0x02,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2B,0x00,0x00,0x00,0x0A,0x02,0x00,0x00,0x98,0x00,0x00,0x00,0xF3,0x00,0x00,0x00, + 0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x0C,0x02,0x00,0x00,0x03,0x02,0x00,0x00, + 0x0B,0x02,0x00,0x00,0x83,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x0D,0x02,0x00,0x00, + 0x02,0x02,0x00,0x00,0x0C,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x01,0x02,0x00,0x00, + 0x0D,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x0E,0x02,0x00,0x00, + 0xDA,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x0F,0x02,0x00,0x00, + 0x01,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x10,0x02,0x00,0x00, + 0x01,0x02,0x00,0x00,0x94,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x11,0x02,0x00,0x00, + 0x0F,0x02,0x00,0x00,0x10,0x02,0x00,0x00,0x0C,0x00,0x07,0x00,0x06,0x00,0x00,0x00, + 0x12,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x0E,0x02,0x00,0x00, + 0x11,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xDA,0x01,0x00,0x00,0x12,0x02,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x16,0x02,0x00,0x00,0x52,0x00,0x00,0x00, + 0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x17,0x02,0x00,0x00, + 0x16,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x18,0x02,0x00,0x00, + 0xE6,0x01,0x00,0x00,0x41,0x00,0x06,0x00,0x17,0x00,0x00,0x00,0x19,0x02,0x00,0x00, + 0x53,0x00,0x00,0x00,0x18,0x02,0x00,0x00,0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x1A,0x02,0x00,0x00,0x19,0x02,0x00,0x00,0xBE,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x1B,0x02,0x00,0x00,0x17,0x02,0x00,0x00,0x1A,0x02,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x1C,0x02,0x00,0x00,0x52,0x00,0x00,0x00, + 0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x1D,0x02,0x00,0x00, + 0x1C,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x1E,0x02,0x00,0x00, + 0xE7,0x01,0x00,0x00,0x41,0x00,0x06,0x00,0x17,0x00,0x00,0x00,0x1F,0x02,0x00,0x00, + 0x53,0x00,0x00,0x00,0x1E,0x02,0x00,0x00,0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x20,0x02,0x00,0x00,0x1F,0x02,0x00,0x00,0xB8,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x21,0x02,0x00,0x00,0x1D,0x02,0x00,0x00,0x20,0x02,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x22,0x02,0x00,0x00,0xF3,0x01,0x00,0x00, + 0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x23,0x02,0x00,0x00, + 0x22,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x24,0x02,0x00,0x00, + 0xFB,0x01,0x00,0x00,0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x25,0x02,0x00,0x00,0x24,0x02,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x26,0x02,0x00,0x00,0x23,0x02,0x00,0x00,0x25,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0x27,0x02,0x00,0x00,0xF3,0x01,0x00,0x00,0xB4,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x28,0x02,0x00,0x00,0x27,0x02,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x29,0x02,0x00,0x00,0xFB,0x01,0x00,0x00, + 0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x2A,0x02,0x00,0x00, + 0x29,0x02,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x2B,0x02,0x00,0x00, + 0x28,0x02,0x00,0x00,0x2A,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0x2C,0x02,0x00,0x00,0x26,0x02,0x00,0x00,0x2B,0x02,0x00,0x00,0x50,0x00,0x06,0x00, + 0x13,0x02,0x00,0x00,0x2D,0x02,0x00,0x00,0x1B,0x02,0x00,0x00,0x21,0x02,0x00,0x00, + 0x2C,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x15,0x02,0x00,0x00,0x2D,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x13,0x02,0x00,0x00,0x2E,0x02,0x00,0x00,0x15,0x02,0x00,0x00, + 0x9B,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x2F,0x02,0x00,0x00,0x2E,0x02,0x00,0x00, + 0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x30,0x02,0x00,0x00,0x2F,0x02,0x00,0x00, + 0xF7,0x00,0x03,0x00,0x32,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0x30,0x02,0x00,0x00,0x31,0x02,0x00,0x00,0x32,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x31,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x13,0x02,0x00,0x00,0x33,0x02,0x00,0x00, + 0x15,0x02,0x00,0x00,0xA8,0x00,0x04,0x00,0x13,0x02,0x00,0x00,0x34,0x02,0x00,0x00, + 0x33,0x02,0x00,0x00,0x9B,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x35,0x02,0x00,0x00, + 0x34,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0x32,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x32,0x02,0x00,0x00,0xF5,0x00,0x07,0x00,0x99,0x00,0x00,0x00,0x36,0x02,0x00,0x00, + 0x2F,0x02,0x00,0x00,0xEC,0x01,0x00,0x00,0x35,0x02,0x00,0x00,0x31,0x02,0x00,0x00, + 0xF7,0x00,0x03,0x00,0x38,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0x36,0x02,0x00,0x00,0x37,0x02,0x00,0x00,0x38,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x37,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x39,0x02,0x00,0x00, + 0xE5,0x01,0x00,0x00,0x7F,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x3A,0x02,0x00,0x00, + 0x39,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xE5,0x01,0x00,0x00,0x3A,0x02,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x38,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0x38,0x02,0x00,0x00, + 0xF9,0x00,0x02,0x00,0xEE,0x01,0x00,0x00,0xF8,0x00,0x02,0x00,0xEE,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x3B,0x02,0x00,0x00,0xE6,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xE7,0x01,0x00,0x00,0x3B,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x3C,0x02,0x00,0x00,0xE6,0x01,0x00,0x00,0x80,0x00,0x05,0x00, + 0x4F,0x00,0x00,0x00,0x3D,0x02,0x00,0x00,0x3C,0x02,0x00,0x00,0xE9,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xE6,0x01,0x00,0x00,0x3D,0x02,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xEB,0x01,0x00,0x00,0xF8,0x00,0x02,0x00,0xED,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x3E,0x02,0x00,0x00,0xE5,0x01,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x3F,0x02,0x00,0x00,0xDA,0x01,0x00,0x00,0x0C,0x00,0x06,0x00, + 0x06,0x00,0x00,0x00,0x40,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x1F,0x00,0x00,0x00, + 0x3F,0x02,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x41,0x02,0x00,0x00, + 0x3E,0x02,0x00,0x00,0x40,0x02,0x00,0x00,0xFE,0x00,0x02,0x00,0x41,0x02,0x00,0x00, + 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x5C,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00, + 0x5A,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x58,0x00,0x00,0x00,0x5B,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x5D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x44,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x52,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x66,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x70,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x7C,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00, + 0x46,0x02,0x00,0x00,0x5B,0x00,0x00,0x00,0x45,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x47,0x02,0x00,0x00,0x46,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x44,0x02,0x00,0x00,0x47,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0x49,0x02,0x00,0x00,0x44,0x02,0x00,0x00,0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x4A,0x02,0x00,0x00,0x49,0x02,0x00,0x00,0x85,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x4B,0x02,0x00,0x00,0x4A,0x02,0x00,0x00,0x48,0x02,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x4C,0x02,0x00,0x00,0x44,0x02,0x00,0x00, + 0xB8,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x4C,0x02,0x00,0x00,0x4B,0x02,0x00,0x00, + 0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00,0x4E,0x02,0x00,0x00,0x44,0x02,0x00,0x00, + 0xB4,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x4F,0x02,0x00,0x00, + 0x4E,0x02,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x50,0x02,0x00,0x00, + 0x4F,0x02,0x00,0x00,0x4D,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0x51,0x02,0x00,0x00,0x44,0x02,0x00,0x00,0xB4,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x51,0x02,0x00,0x00,0x50,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x54,0x02,0x00,0x00, + 0x57,0x02,0x00,0x00,0x56,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x58,0x02,0x00,0x00,0x44,0x02,0x00,0x00,0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x59,0x02,0x00,0x00,0x58,0x02,0x00,0x00,0x82,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x5D,0x02,0x00,0x00,0x5E,0x02,0x00,0x00,0x5C,0x02,0x00,0x00,0xE9,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x5F,0x02,0x00,0x00,0x5E,0x02,0x00,0x00, + 0x50,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x60,0x02,0x00,0x00,0x5F,0x02,0x00,0x00, + 0x5F,0x02,0x00,0x00,0x81,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x61,0x02,0x00,0x00, + 0x59,0x02,0x00,0x00,0x60,0x02,0x00,0x00,0x57,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x62,0x02,0x00,0x00,0x57,0x02,0x00,0x00,0x61,0x02,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x63,0x02,0x00,0x00,0x62,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0x83,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x64,0x02,0x00,0x00,0x63,0x02,0x00,0x00, + 0x82,0x00,0x00,0x00,0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x65,0x02,0x00,0x00, + 0x64,0x02,0x00,0x00,0x64,0x02,0x00,0x00,0x64,0x02,0x00,0x00,0x64,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x52,0x02,0x00,0x00,0x65,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x17,0x00,0x00,0x00,0x6A,0x02,0x00,0x00,0x52,0x02,0x00,0x00,0xB8,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x6B,0x02,0x00,0x00,0x6A,0x02,0x00,0x00, + 0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x6C,0x02,0x00,0x00,0x69,0x02,0x00,0x00, + 0x6B,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x5D,0x02,0x00,0x00,0x6D,0x02,0x00,0x00, + 0x5C,0x02,0x00,0x00,0xDC,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x6E,0x02,0x00,0x00,0x6D,0x02,0x00,0x00,0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x6F,0x02,0x00,0x00,0x6C,0x02,0x00,0x00,0x6E,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x66,0x02,0x00,0x00,0x6F,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x54,0x02,0x00,0x00, + 0x72,0x02,0x00,0x00,0x71,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00, + 0x73,0x02,0x00,0x00,0x5B,0x00,0x00,0x00,0x45,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x74,0x02,0x00,0x00,0x73,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x75,0x02,0x00,0x00,0x66,0x02,0x00,0x00,0x81,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x76,0x02,0x00,0x00,0x74,0x02,0x00,0x00,0x75,0x02,0x00,0x00, + 0x57,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x77,0x02,0x00,0x00,0x72,0x02,0x00,0x00, + 0x76,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x70,0x02,0x00,0x00,0x77,0x02,0x00,0x00, + 0x41,0x00,0x05,0x00,0x5D,0x02,0x00,0x00,0x79,0x02,0x00,0x00,0x5C,0x02,0x00,0x00, + 0x78,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x7A,0x02,0x00,0x00, + 0x79,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x7B,0x02,0x00,0x00, + 0x7A,0x02,0x00,0x00,0x98,0x00,0x00,0x00,0xF7,0x00,0x03,0x00,0x7E,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x7B,0x02,0x00,0x00,0x7D,0x02,0x00,0x00, + 0x82,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0x7D,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x7F,0x02,0x00,0x00,0x52,0x02,0x00,0x00,0x81,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x81,0x02,0x00,0x00,0x7F,0x02,0x00,0x00,0x80,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x7C,0x02,0x00,0x00,0x81,0x02,0x00,0x00,0xF9,0x00,0x02,0x00, + 0x7E,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0x82,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x83,0x02,0x00,0x00,0x70,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x7C,0x02,0x00,0x00,0x83,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0x7E,0x02,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x7E,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x84,0x02,0x00,0x00,0x7C,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x70,0x02,0x00,0x00, + 0x84,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x85,0x02,0x00,0x00, + 0x70,0x02,0x00,0x00,0xFE,0x00,0x02,0x00,0x85,0x02,0x00,0x00,0x38,0x00,0x01,0x00, }; +#endif +static const char s_waves_shd_bytecode_draw_glsl300_src[10541] = +"#version 300 es\n" +"precision mediump float;\n" +"precision highp int;\n" +"\n" +"struct ShaderParams\n" +"{\n" +" highp vec2 view_pos;\n" +" highp vec2 uv;\n" +" highp vec2 uv_min;\n" +" highp vec2 uv_max;\n" +" highp vec2 screen_uv;\n" +" highp vec4 attributes;\n" +"};\n" +"\n" +"layout(std140) uniform shd_uniforms\n" +"{\n" +" highp float amplitude;\n" +" highp float time;\n" +" highp float show_noise;\n" +"} _604;\n" +"\n" +"layout(std140) uniform uniform_block\n" +"{\n" +" highp vec2 u_texture_size;\n" +" int u_alpha_discard;\n" +" int u_use_smooth_uv;\n" +"} _733;\n" +"\n" +"uniform highp sampler2D noise_tex;\n" +"uniform highp sampler2D water_tex;\n" +"uniform highp sampler2D u_image;\n" +"\n" +"layout(location = 0) out highp vec4 result;\n" +"in highp vec4 v_pos_uv;\n" +"in highp vec4 v_shape;\n" +"in highp vec4 v_blend_posH;\n" +"in highp vec4 v_col;\n" +"in highp vec4 v_ab;\n" +"in highp vec4 v_cd;\n" +"in highp vec4 v_ef;\n" +"in highp vec4 v_gh;\n" +"flat in int v_n;\n" +"in highp vec4 v_uv_bounds;\n" +"in highp vec4 v_user;\n" +"highp float v_stroke;\n" +"highp float v_aa;\n" +"highp float v_fill;\n" +"highp vec2 v_pos;\n" +"highp vec2 v_uv;\n" +"highp float v_radius;\n" +"highp float v_type;\n" +"highp float v_alpha;\n" +"highp vec2 v_posH;\n" +"highp vec2 pts[8];\n" +"highp vec2 pos;\n" +"highp vec2 screen_uv;\n" +"\n" +"highp vec2 smooth_uv(highp vec2 uv, highp vec2 texture_size)\n" +"{\n" +" highp vec2 pixel = uv * texture_size;\n" +" highp vec2 seam = floor(pixel + vec2(0.5));\n" +" pixel = seam + clamp((pixel - seam) / fwidth(pixel), vec2(-0.5), vec2(0.5));\n" +" return pixel / texture_size;\n" +"}\n" +"\n" +"highp vec4 de_gamma(highp vec4 c)\n" +"{\n" +" return vec4(pow(abs(c.xyz), vec3(2.2000000476837158203125)), c.w);\n" +"}\n" +"\n" +"highp vec4 gamma(highp vec4 c)\n" +"{\n" +" return vec4(pow(abs(c.xyz), vec3(0.4545454680919647216796875)), c.w);\n" +"}\n" +"\n" +"highp vec2 skew(highp vec2 v)\n" +"{\n" +" return vec2(-v.y, v.x);\n" +"}\n" +"\n" +"highp float distance_aabb(highp vec2 p, highp vec2 he)\n" +"{\n" +" highp vec2 d = abs(p) - he;\n" +" return length(max(d, vec2(0.0))) + min(max(d.x, d.y), 0.0);\n" +"}\n" +"\n" +"highp float distance_box(inout highp vec2 p, highp vec2 c, highp vec2 he, highp vec2 u)\n" +"{\n" +" highp vec2 param = u;\n" +" highp mat2 m = transpose(mat2(vec2(u), vec2(skew(param))));\n" +" p -= c;\n" +" p = m * p;\n" +" highp vec2 param_1 = p;\n" +" highp vec2 param_2 = he;\n" +" return distance_aabb(param_1, param_2);\n" +"}\n" +"\n" +"highp float safe_div(highp float a, highp float b)\n" +"{\n" +" highp float _155;\n" +" if (b == 0.0)\n" +" {\n" +" _155 = 0.0;\n" +" }\n" +" else\n" +" {\n" +" _155 = a / b;\n" +" }\n" +" return _155;\n" +"}\n" +"\n" +"highp float safe_len(highp vec2 v)\n" +"{\n" +" highp float d = dot(v, v);\n" +" highp float _171;\n" +" if (d == 0.0)\n" +" {\n" +" _171 = 0.0;\n" +" }\n" +" else\n" +" {\n" +" _171 = sqrt(d);\n" +" }\n" +" return _171;\n" +"}\n" +"\n" +"highp float distance_segment(highp vec2 p, highp vec2 a, highp vec2 b)\n" +"{\n" +" highp vec2 n = b - a;\n" +" highp vec2 pa = p - a;\n" +" highp float param = dot(pa, n);\n" +" highp float param_1 = dot(n, n);\n" +" highp float d = safe_div(param, param_1);\n" +" highp float h = clamp(d, 0.0, 1.0);\n" +" highp vec2 param_2 = pa - (n * h);\n" +" return safe_len(param_2);\n" +"}\n" +"\n" +"highp float det2(highp vec2 a, highp vec2 b)\n" +"{\n" +" return (a.x * b.y) - (a.y * b.x);\n" +"}\n" +"\n" +"highp float distance_triangle(highp vec2 p, highp vec2 a, highp vec2 b, highp vec2 c)\n" +"{\n" +" highp vec2 e0 = b - a;\n" +" highp vec2 e1 = c - b;\n" +" highp vec2 e2 = a - c;\n" +" highp vec2 v0 = p - a;\n" +" highp vec2 v1 = p - b;\n" +" highp vec2 v2 = p - c;\n" +" highp float param = dot(v0, e0);\n" +" highp float param_1 = dot(e0, e0);\n" +" highp vec2 pq0 = v0 - (e0 * clamp(safe_div(param, param_1), 0.0, 1.0));\n" +" highp float param_2 = dot(v1, e1);\n" +" highp float param_3 = dot(e1, e1);\n" +" highp vec2 pq1 = v1 - (e1 * clamp(safe_div(param_2, param_3), 0.0, 1.0));\n" +" highp float param_4 = dot(v2, e2);\n" +" highp float param_5 = dot(e2, e2);\n" +" highp vec2 pq2 = v2 - (e2 * clamp(safe_div(param_4, param_5), 0.0, 1.0));\n" +" highp vec2 param_6 = e0;\n" +" highp vec2 param_7 = e2;\n" +" highp float s = det2(param_6, param_7);\n" +" highp vec2 param_8 = v0;\n" +" highp vec2 param_9 = e0;\n" +" highp vec2 param_10 = v1;\n" +" highp vec2 param_11 = e1;\n" +" highp vec2 param_12 = v2;\n" +" highp vec2 param_13 = e2;\n" +" highp vec2 d = min(min(vec2(dot(pq0, pq0), s * det2(param_8, param_9)), vec2(dot(pq1, pq1), s * det2(param_10, param_11))), vec2(dot(pq2, pq2), s * det2(param_12, param_13)));\n" +" return (-sqrt(d.x)) * sign(d.y);\n" +"}\n" +"\n" +"highp float distance_polygon(highp vec2 p, highp vec2 v[8], int N)\n" +"{\n" +" highp float d = dot(p - v[0], p - v[0]);\n" +" highp float s = 1.0;\n" +" int _490 = N - 1;\n" +" for (int i = 0, j = _490; i < N; j = i, i++)\n" +" {\n" +" highp vec2 e = v[j] - v[i];\n" +" highp vec2 w = p - v[i];\n" +" highp vec2 b = w - (e * clamp(dot(w, e) / dot(e, e), 0.0, 1.0));\n" +" d = min(d, dot(b, b));\n" +" bvec3 cond = bvec3(p.y >= v[i].y, p.y < v[j].y, (e.x * w.y) > (e.y * w.x));\n" +" bool _559 = all(cond);\n" +" bool _566;\n" +" if (!_559)\n" +" {\n" +" _566 = all(not(cond));\n" +" }\n" +" else\n" +" {\n" +" _566 = _559;\n" +" }\n" +" if (_566)\n" +" {\n" +" s = -s;\n" +" }\n" +" }\n" +" return s * sqrt(d);\n" +"}\n" +"\n" +"highp float sdf_stroke(highp float d)\n" +"{\n" +" return abs(d) - v_stroke;\n" +"}\n" +"\n" +"highp vec4 sdf(highp vec4 a, highp vec4 b, highp float d)\n" +"{\n" +" highp float param = d;\n" +" highp float wire_d = sdf_stroke(param);\n" +" highp vec4 stroke_aa = mix(b, a, vec4(smoothstep(0.0, v_aa, wire_d)));\n" +" bvec4 _230 = bvec4(wire_d <= 0.0);\n" +" highp vec4 stroke_no_aa = vec4(_230.x ? b.x : a.x, _230.y ? b.y : a.y, _230.z ? b.z : a.z, _230.w ? b.w : a.w);\n" +" highp vec4 fill_aa = mix(b, a, vec4(smoothstep(0.0, v_aa, d)));\n" +" bvec4 _248 = bvec4(clamp(d, -1.0, 1.0) <= 0.0);\n" +" highp vec4 fill_no_aa = vec4(_248.x ? b.x : a.x, _248.y ? b.y : a.y, _248.z ? b.z : a.z, _248.w ? b.w : a.w);\n" +" highp vec4 stroke = mix(stroke_aa, stroke_aa, vec4(float(v_aa > 0.0)));\n" +" highp vec4 fill = mix(fill_no_aa, fill_aa, vec4(float(v_aa > 0.0)));\n" +" result = mix(stroke, fill, vec4(v_fill));\n" +" return result;\n" +"}\n" +"\n" +"highp vec4 shader(highp vec4 color, ShaderParams params)\n" +"{\n" +" highp vec2 noise_uv = params.screen_uv;\n" +" noise_uv.x *= 5.0;\n" +" noise_uv.y *= 3.75;\n" +" highp vec4 noise_c = vec4(texture(noise_tex, (noise_uv * 0.5) + vec2(_604.time)).x - 0.5);\n" +" highp vec2 offset = (vec2(0.001562500023283064365386962890625, 0.00208333344198763370513916015625) * noise_c.x) * _604.amplitude;\n" +" highp vec4 c = texture(water_tex, params.screen_uv + offset);\n" +" highp vec4 _636;\n" +" if (_604.show_noise > 0.0)\n" +" {\n" +" _636 = noise_c + vec4(0.5);\n" +" }\n" +" else\n" +" {\n" +" _636 = c;\n" +" }\n" +" c = _636;\n" +" return c;\n" +"}\n" +"\n" +"void main()\n" +"{\n" +" v_pos = v_pos_uv.xy;\n" +" v_uv = v_pos_uv.zw;\n" +" v_radius = v_shape.x;\n" +" v_stroke = v_shape.y;\n" +" v_aa = v_shape.z;\n" +" v_type = v_shape.w;\n" +" v_alpha = v_blend_posH.x;\n" +" v_fill = v_blend_posH.y;\n" +" v_posH = v_blend_posH.zw;\n" +" bool is_sprite = (v_type >= 0.0) && (v_type < 0.5);\n" +" bool is_text = (v_type > 0.5) && (v_type < 1.5);\n" +" bool is_box = (v_type > 1.5) && (v_type < 2.5);\n" +" bool is_seg = (v_type > 2.5) && (v_type < 3.5);\n" +" bool is_tri = (v_type > 3.5) && (v_type < 4.5);\n" +" bool is_tri_sdf = (v_type > 4.5) && (v_type < 5.5);\n" +" bool is_poly = (v_type > 5.5) && (v_type < 6.5);\n" +" highp vec4 c = vec4(0.0);\n" +" highp vec2 _738;\n" +" if (_733.u_use_smooth_uv == 0)\n" +" {\n" +" highp vec2 param = v_uv;\n" +" highp vec2 param_1 = _733.u_texture_size;\n" +" _738 = smooth_uv(param, param_1);\n" +" }\n" +" else\n" +" {\n" +" _738 = v_uv;\n" +" }\n" +" highp vec2 uv = _738;\n" +" highp vec4 param_2 = texture(u_image, uv);\n" +" highp vec4 tex_c = de_gamma(param_2);\n" +" highp vec4 _759;\n" +" if (is_sprite)\n" +" {\n" +" highp vec4 param_3 = tex_c;\n" +" _759 = gamma(param_3);\n" +" }\n" +" else\n" +" {\n" +" _759 = c;\n" +" }\n" +" c = _759;\n" +" highp vec4 _769;\n" +" if (is_text)\n" +" {\n" +" _769 = v_col * tex_c.w;\n" +" }\n" +" else\n" +" {\n" +" _769 = c;\n" +" }\n" +" c = _769;\n" +" bvec4 _783 = bvec4(is_tri);\n" +" c = vec4(_783.x ? v_col.x : c.x, _783.y ? v_col.y : c.y, _783.z ? v_col.z : c.z, _783.w ? v_col.w : c.w);\n" +" highp float d = 0.0;\n" +" if (is_box)\n" +" {\n" +" highp vec2 param_4 = v_pos;\n" +" highp vec2 param_5 = v_ab.xy;\n" +" highp vec2 param_6 = v_ab.zw;\n" +" highp vec2 param_7 = v_cd.xy;\n" +" highp float _802 = distance_box(param_4, param_5, param_6, param_7);\n" +" d = _802;\n" +" }\n" +" else\n" +" {\n" +" if (is_seg)\n" +" {\n" +" highp vec2 param_8 = v_pos;\n" +" highp vec2 param_9 = v_ab.xy;\n" +" highp vec2 param_10 = v_ab.zw;\n" +" d = distance_segment(param_8, param_9, param_10);\n" +" highp vec2 param_11 = v_pos;\n" +" highp vec2 param_12 = v_ab.zw;\n" +" highp vec2 param_13 = v_cd.xy;\n" +" d = min(d, distance_segment(param_11, param_12, param_13));\n" +" }\n" +" else\n" +" {\n" +" if (is_tri_sdf)\n" +" {\n" +" highp vec2 param_14 = v_pos;\n" +" highp vec2 param_15 = v_ab.xy;\n" +" highp vec2 param_16 = v_ab.zw;\n" +" highp vec2 param_17 = v_cd.xy;\n" +" d = distance_triangle(param_14, param_15, param_16, param_17);\n" +" }\n" +" else\n" +" {\n" +" if (is_poly)\n" +" {\n" +" pts[0] = v_ab.xy;\n" +" pts[1] = v_ab.zw;\n" +" pts[2] = v_cd.xy;\n" +" pts[3] = v_cd.zw;\n" +" pts[4] = v_ef.xy;\n" +" pts[5] = v_ef.zw;\n" +" pts[6] = v_gh.xy;\n" +" pts[7] = v_gh.zw;\n" +" highp vec2 param_18 = v_pos;\n" +" highp vec2 param_19[8] = pts;\n" +" int param_20 = v_n;\n" +" d = distance_polygon(param_18, param_19, param_20);\n" +" }\n" +" }\n" +" }\n" +" }\n" +" highp vec4 _896;\n" +" if (((!is_sprite) && (!is_text)) && (!is_tri))\n" +" {\n" +" highp vec4 param_21 = c;\n" +" highp vec4 param_22 = v_col;\n" +" highp float param_23 = d - v_radius;\n" +" highp vec4 _907 = sdf(param_21, param_22, param_23);\n" +" _896 = _907;\n" +" }\n" +" else\n" +" {\n" +" _896 = c;\n" +" }\n" +" c = _896;\n" +" c *= v_alpha;\n" +" pos = v_pos;\n" +" screen_uv = ((v_posH + vec2(1.0, -1.0)) * 0.5) * vec2(1.0, -1.0);\n" +" ShaderParams sp;\n" +" sp.view_pos = pos;\n" +" sp.uv = v_uv;\n" +" sp.uv_min = v_uv_bounds.xy;\n" +" sp.uv_max = v_uv_bounds.zw;\n" +" sp.screen_uv = screen_uv;\n" +" sp.attributes = v_user;\n" +" highp vec4 param_24 = c;\n" +" ShaderParams param_25 = sp;\n" +" c = shader(param_24, param_25);\n" +" bool _946 = _733.u_alpha_discard != 0;\n" +" bool _952;\n" +" if (_946)\n" +" {\n" +" _952 = c.w == 0.0;\n" +" }\n" +" else\n" +" {\n" +" _952 = _946;\n" +" }\n" +" if (_952)\n" +" {\n" +" discard;\n" +" }\n" +" result = c;\n" +"}\n" +"\n" +""; static const char* s_waves_shd_bytecode_draw_image_names[3] = { "u_image", "water_tex", "noise_tex", }; +static int s_waves_shd_bytecode_draw_image_binding_slots[3] = { 0, 1, 2, }; static CF_ShaderUniformInfo s_waves_shd_bytecode_draw_uniforms[2] = { - { - .block_name = "uniform_block", - .block_index = 0, - .block_size = 16, - .num_members = 2, - }, - { - .block_name = "shd_uniforms", - .block_index = 1, - .block_size = 16, - .num_members = 3, - }, + { + .block_name = "uniform_block", + .block_index = 0, + .block_size = 16, + .num_members = 3, + }, + { + .block_name = "shd_uniforms", + .block_index = 1, + .block_size = 16, + .num_members = 3, + }, }; -static CF_ShaderUniformMemberInfo s_waves_shd_bytecode_draw_uniform_members[5] = { - { - .name = "u_texture_size", - .type = CF_SHADER_INFO_TYPE_FLOAT2, - .offset = 0, - .array_length = 1, - }, - { - .name = "u_alpha_discard", - .type = CF_SHADER_INFO_TYPE_SINT, - .offset = 8, - .array_length = 1, - }, - { - .name = "amplitude", - .type = CF_SHADER_INFO_TYPE_FLOAT, - .offset = 0, - .array_length = 1, - }, - { - .name = "time", - .type = CF_SHADER_INFO_TYPE_FLOAT, - .offset = 4, - .array_length = 1, - }, - { - .name = "show_noise", - .type = CF_SHADER_INFO_TYPE_FLOAT, - .offset = 8, - .array_length = 1, - }, -}; -static CF_ShaderInputInfo* s_waves_shd_bytecode_draw_inputs = NULL; -static const CF_ShaderBytecode s_waves_shd_bytecode_draw = { - .content = s_waves_shd_bytecode_draw_content, - .size = 22272, - .shader_info = { - .num_samplers = 3, - .num_storage_textures = 0, - .num_storage_buffers = 0, - .num_images = 3, - .image_names = s_waves_shd_bytecode_draw_image_names, - .num_uniforms = 2, - .uniforms = s_waves_shd_bytecode_draw_uniforms, - .num_uniform_members = 5, - .uniform_members = s_waves_shd_bytecode_draw_uniform_members, - .num_inputs = 0, - .inputs = s_waves_shd_bytecode_draw_inputs, - }, +static CF_ShaderUniformMemberInfo s_waves_shd_bytecode_draw_uniform_members[6] = { + { + .name = "u_texture_size", + .type = CF_SHADER_INFO_TYPE_FLOAT2, + .offset = 0, + .array_length = 1, + }, + { + .name = "u_alpha_discard", + .type = CF_SHADER_INFO_TYPE_SINT, + .offset = 8, + .array_length = 1, + }, + { + .name = "u_use_smooth_uv", + .type = CF_SHADER_INFO_TYPE_SINT, + .offset = 12, + .array_length = 1, + }, + { + .name = "amplitude", + .type = CF_SHADER_INFO_TYPE_FLOAT, + .offset = 0, + .array_length = 1, + }, + { + .name = "time", + .type = CF_SHADER_INFO_TYPE_FLOAT, + .offset = 4, + .array_length = 1, + }, + { + .name = "show_noise", + .type = CF_SHADER_INFO_TYPE_FLOAT, + .offset = 8, + .array_length = 1, + }, }; +#define s_waves_shd_bytecode_draw_inputs NULL /* #extension GL_ARB_shading_language_include : require @@ -1806,6 +2284,11 @@ layout(location = 0) out vec4 result; layout(set = 2, binding = 0) uniform sampler2D u_image; +layout(set = 3, binding = 0) uniform uniform_block { + vec2 u_texture_size; + int u_alpha_discard; +}; + #line 1 "smooth_uv.shd" vec2 smooth_uv(vec2 uv, vec2 texture_size) @@ -1815,7 +2298,21 @@ vec2 smooth_uv(vec2 uv, vec2 texture_size) pixel = seam + clamp( (pixel - seam) / fwidth(pixel), - 0.5, 0.5); return pixel / texture_size; } -#line 12 0 +#line 17 0 + +vec2 pos; +vec2 screen_uv; + +struct ShaderParams +{ + vec2 view_pos; + vec2 uv; + vec2 uv_min; + vec2 uv_max; + vec2 screen_uv; + vec4 attributes; +}; + #line 1 "shader_stub.shd" layout(set = 2, binding = 1) uniform sampler2D water_tex; layout(set = 2, binding = 2) uniform sampler2D noise_tex; @@ -1826,394 +2323,565 @@ layout(set = 3, binding = 1) uniform shd_uniforms { float show_noise; }; -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { - vec2 noise_uv = screen_uv; + vec2 noise_uv = params.screen_uv; noise_uv.x *= 640.0 / 128.0; noise_uv.y *= 480.0 / 128.0; vec4 noise_c = vec4(texture(noise_tex, noise_uv * 0.5 + time).r - 0.5); vec2 offset = vec2(1.0 / 640.0, 1.0 / 480.0) * noise_c.r * amplitude; - vec4 c = texture(water_tex, screen_uv + offset); + vec4 c = texture(water_tex, params.screen_uv + offset); c = show_noise > 0 ? noise_c + vec4(0.5) : c; return c; } -#line 13 0 - -layout(set = 3, binding = 0) uniform uniform_block { - vec2 u_texture_size; - int u_alpha_discard; -}; +#line 32 0 void main() { vec4 color = texture(u_image, smooth_uv(v_uv, u_texture_size)); - vec2 screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1); - vec4 c = shader(color, v_pos, screen_uv, v_params); + pos = v_pos; + screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1); + ShaderParams sp; + sp.view_pos = pos; + sp.uv = v_uv; + sp.uv_min = vec2(0); + sp.uv_max = vec2(0); + sp.screen_uv = screen_uv; + sp.attributes = v_params; + vec4 c = shader(color, sp); if (u_alpha_discard != 0 && c.a == 0) discard; result = c; } */ -static const uint8_t s_waves_shd_bytecode_blit_content[4652] = { - 0x03, 0x02, 0x23, 0x07, 0x00, 0x03, 0x01, 0x00, 0x0B, 0x00, 0x08, 0x00, 0xB3, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x0F, 0x00, 0x0A, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, - 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0xB1, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0xC2, 0x01, 0x00, 0x00, - 0x04, 0x00, 0x09, 0x00, 0x47, 0x4C, 0x5F, 0x41, 0x52, 0x42, 0x5F, 0x73, 0x68, 0x61, 0x64, 0x69, - 0x6E, 0x67, 0x5F, 0x6C, 0x61, 0x6E, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5F, 0x69, 0x6E, 0x63, 0x6C, - 0x75, 0x64, 0x65, 0x00, 0x04, 0x00, 0x0A, 0x00, 0x47, 0x4C, 0x5F, 0x47, 0x4F, 0x4F, 0x47, 0x4C, - 0x45, 0x5F, 0x63, 0x70, 0x70, 0x5F, 0x73, 0x74, 0x79, 0x6C, 0x65, 0x5F, 0x6C, 0x69, 0x6E, 0x65, - 0x5F, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x07, 0x00, - 0x0C, 0x00, 0x00, 0x00, 0x73, 0x6D, 0x6F, 0x6F, 0x74, 0x68, 0x5F, 0x75, 0x76, 0x28, 0x76, 0x66, - 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x75, 0x76, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x74, - 0x75, 0x72, 0x65, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x15, 0x00, 0x00, 0x00, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x28, 0x76, 0x66, 0x34, 0x3B, 0x76, - 0x66, 0x32, 0x3B, 0x76, 0x66, 0x32, 0x3B, 0x76, 0x66, 0x34, 0x3B, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x11, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x12, 0x00, 0x00, 0x00, 0x70, 0x6F, 0x73, 0x00, 0x05, 0x00, 0x05, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x73, 0x63, 0x72, 0x65, 0x65, 0x6E, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x14, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x73, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x70, 0x69, 0x78, 0x65, 0x6C, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x1B, 0x00, 0x00, 0x00, 0x73, 0x65, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x6E, 0x6F, 0x69, 0x73, 0x65, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x04, 0x00, 0x42, 0x00, 0x00, 0x00, 0x6E, 0x6F, 0x69, 0x73, 0x65, 0x5F, 0x63, 0x00, - 0x05, 0x00, 0x05, 0x00, 0x46, 0x00, 0x00, 0x00, 0x6E, 0x6F, 0x69, 0x73, 0x65, 0x5F, 0x74, 0x65, - 0x78, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x73, 0x68, 0x64, 0x5F, - 0x75, 0x6E, 0x69, 0x66, 0x6F, 0x72, 0x6D, 0x73, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, - 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x6D, 0x70, 0x6C, 0x69, 0x74, 0x75, 0x64, - 0x65, 0x00, 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x74, 0x69, 0x6D, 0x65, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x4A, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x73, 0x68, 0x6F, 0x77, 0x5F, 0x6E, 0x6F, 0x69, 0x73, 0x65, 0x00, 0x00, - 0x05, 0x00, 0x03, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x6F, 0x66, 0x66, 0x73, 0x65, 0x74, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x63, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x64, 0x00, 0x00, 0x00, - 0x77, 0x61, 0x74, 0x65, 0x72, 0x5F, 0x74, 0x65, 0x78, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x7C, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x7D, 0x00, 0x00, 0x00, 0x75, 0x5F, 0x69, 0x6D, 0x61, 0x67, 0x65, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x76, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x75, 0x6E, 0x69, 0x66, 0x6F, 0x72, 0x6D, 0x5F, 0x62, 0x6C, 0x6F, 0x63, - 0x6B, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x75, 0x5F, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5F, 0x73, 0x69, 0x7A, 0x65, 0x00, 0x00, - 0x06, 0x00, 0x07, 0x00, 0x81, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x5F, 0x61, 0x6C, - 0x70, 0x68, 0x61, 0x5F, 0x64, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x8C, 0x00, 0x00, 0x00, - 0x73, 0x63, 0x72, 0x65, 0x65, 0x6E, 0x5F, 0x75, 0x76, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x8D, 0x00, 0x00, 0x00, 0x76, 0x5F, 0x70, 0x6F, 0x73, 0x48, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, - 0x95, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x96, 0x00, 0x00, 0x00, - 0x76, 0x5F, 0x70, 0x6F, 0x73, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x98, 0x00, 0x00, 0x00, - 0x76, 0x5F, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x73, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x9B, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x9D, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0x9F, 0x00, 0x00, 0x00, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, - 0xB1, 0x00, 0x00, 0x00, 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, - 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x05, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x4A, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x64, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x80, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x81, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x83, 0x00, 0x00, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x83, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x8D, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x96, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x98, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0xB1, 0x00, 0x00, 0x00, - 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x21, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x21, 0x00, 0x07, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x40, 0x15, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x40, 0x2B, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x43, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x03, 0x00, - 0x44, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x45, 0x00, 0x00, 0x00, - 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x05, 0x00, 0x4A, 0x00, 0x00, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x4B, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4A, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x4B, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, - 0x4D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x4D, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0xCD, 0xCC, 0xCC, 0x3A, 0x2B, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, 0x89, 0x88, 0x08, 0x3B, 0x2C, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, 0x59, 0x00, 0x00, 0x00, 0x5A, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3B, 0x00, 0x04, 0x00, 0x45, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x02, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x74, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x45, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x04, 0x00, 0x81, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x4D, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x82, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x82, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x87, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x8F, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0x3F, 0x2B, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x80, 0xBF, 0x2C, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, - 0x8F, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x7F, 0x00, 0x00, 0x00, - 0x96, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x97, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xA2, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x04, 0x00, 0x35, 0x00, 0x00, 0x00, - 0xA8, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0xB0, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0xB0, 0x00, 0x00, 0x00, - 0xB1, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x9D, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, - 0x7D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, - 0x80, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x84, 0x00, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x87, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, - 0x5F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x86, 0x00, 0x00, 0x00, 0x89, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x06, 0x00, 0x07, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, - 0x84, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x8B, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x8A, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x7C, 0x00, 0x00, 0x00, 0x8B, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x8E, 0x00, 0x00, 0x00, 0x8D, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x92, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, - 0x91, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x99, 0x00, 0x00, 0x00, 0x9A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x9B, 0x00, 0x00, 0x00, 0x9C, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x9E, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x9D, 0x00, 0x00, 0x00, - 0x9E, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x9F, 0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, - 0x39, 0x00, 0x08, 0x00, 0x0E, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x99, 0x00, 0x00, 0x00, 0x9B, 0x00, 0x00, 0x00, 0x9D, 0x00, 0x00, 0x00, 0x9F, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x95, 0x00, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0xA2, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x4D, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, 0xA3, 0x00, 0x00, 0x00, - 0xAB, 0x00, 0x05, 0x00, 0x6E, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, 0xA4, 0x00, 0x00, 0x00, - 0x5F, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0xA7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0xA5, 0x00, 0x00, 0x00, 0xA6, 0x00, 0x00, 0x00, 0xA7, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0xA6, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x37, 0x00, 0x00, 0x00, - 0xA9, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0xA8, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0xA9, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x05, 0x00, - 0x6E, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0xAA, 0x00, 0x00, 0x00, 0x6D, 0x00, 0x00, 0x00, - 0xF9, 0x00, 0x02, 0x00, 0xA7, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xA7, 0x00, 0x00, 0x00, - 0xF5, 0x00, 0x07, 0x00, 0x6E, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00, 0xA6, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, - 0xAE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0x04, 0x00, 0xAC, 0x00, 0x00, 0x00, - 0xAD, 0x00, 0x00, 0x00, 0xAE, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xAD, 0x00, 0x00, 0x00, - 0xFC, 0x00, 0x01, 0x00, 0xF8, 0x00, 0x02, 0x00, 0xAE, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00, 0x95, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0xB1, 0x00, 0x00, 0x00, 0xB2, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x0D, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x0A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, - 0x0B, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x1A, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x06, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x1F, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, - 0x23, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, - 0x17, 0x00, 0x00, 0x00, 0xD1, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, - 0x25, 0x00, 0x00, 0x00, 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, - 0x24, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x29, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x50, 0x00, 0x05, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, - 0x0C, 0x00, 0x08, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x2A, 0x00, 0x00, 0x00, - 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, - 0x2B, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2E, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, - 0x88, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x2D, 0x00, 0x00, 0x00, - 0x2E, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x02, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, - 0x36, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, - 0x37, 0x00, 0x03, 0x00, 0x08, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x37, 0x00, 0x03, 0x00, 0x0F, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00, - 0x0F, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x32, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, 0x39, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x37, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x36, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x05, 0x00, 0x37, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x00, 0x00, 0x85, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, - 0x3F, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x41, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x00, - 0x47, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x48, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, - 0x50, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, - 0x51, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, - 0x49, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, 0x51, 0x00, 0x05, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x83, 0x00, 0x05, 0x00, 0x06, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, - 0x1D, 0x00, 0x00, 0x00, 0x50, 0x00, 0x07, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, - 0x56, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x42, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x37, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x5C, 0x00, 0x00, 0x00, - 0x8E, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, 0x00, - 0x5D, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, 0x4F, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, - 0x4C, 0x00, 0x00, 0x00, 0x5F, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x61, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x61, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x58, 0x00, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x44, 0x00, 0x00, 0x00, - 0x65, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x67, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, - 0x68, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x57, 0x00, 0x05, 0x00, - 0x0E, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, - 0x3E, 0x00, 0x03, 0x00, 0x63, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x41, 0x00, 0x05, 0x00, - 0x4F, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, 0x4C, 0x00, 0x00, 0x00, 0x6A, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x6B, 0x00, 0x00, 0x00, - 0xBA, 0x00, 0x05, 0x00, 0x6E, 0x00, 0x00, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, - 0x6D, 0x00, 0x00, 0x00, 0xF7, 0x00, 0x03, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xFA, 0x00, 0x04, 0x00, 0x6F, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x73, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x81, 0x00, 0x05, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x75, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x72, 0x00, 0x00, 0x00, - 0xF8, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x70, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x00, 0x00, 0xF9, 0x00, 0x02, 0x00, 0x72, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x02, 0x00, - 0x72, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x70, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x63, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, - 0x3D, 0x00, 0x04, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, - 0xFE, 0x00, 0x02, 0x00, 0x79, 0x00, 0x00, 0x00, 0x38, 0x00, 0x01, 0x00, +#ifndef __EMSCRIPTEN__ +static const uint8_t s_waves_shd_bytecode_blit_content[5160] = { + 0x03,0x02,0x23,0x07,0x00,0x03,0x01,0x00,0x0B,0x00,0x08,0x00,0xC3,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0B,0x00,0x06,0x00, + 0x01,0x00,0x00,0x00,0x47,0x4C,0x53,0x4C,0x2E,0x73,0x74,0x64,0x2E,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0F,0x00,0x0A,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, + 0x00,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0x91,0x00,0x00,0x00,0x94,0x00,0x00,0x00, + 0xA9,0x00,0x00,0x00,0xC1,0x00,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xC2,0x01,0x00,0x00, + 0x04,0x00,0x09,0x00,0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x68,0x61,0x64,0x69, + 0x6E,0x67,0x5F,0x6C,0x61,0x6E,0x67,0x75,0x61,0x67,0x65,0x5F,0x69,0x6E,0x63,0x6C, + 0x75,0x64,0x65,0x00,0x04,0x00,0x0A,0x00,0x47,0x4C,0x5F,0x47,0x4F,0x4F,0x47,0x4C, + 0x45,0x5F,0x63,0x70,0x70,0x5F,0x73,0x74,0x79,0x6C,0x65,0x5F,0x6C,0x69,0x6E,0x65, + 0x5F,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x76,0x65,0x00,0x00,0x05,0x00,0x04,0x00, + 0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00,0x05,0x00,0x07,0x00, + 0x0C,0x00,0x00,0x00,0x73,0x6D,0x6F,0x6F,0x74,0x68,0x5F,0x75,0x76,0x28,0x76,0x66, + 0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x0A,0x00,0x00,0x00, + 0x75,0x76,0x00,0x00,0x05,0x00,0x06,0x00,0x0B,0x00,0x00,0x00,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x10,0x00,0x00,0x00,0x53,0x68,0x61,0x64,0x65,0x72,0x50,0x61,0x72,0x61,0x6D,0x73, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x76,0x69,0x65,0x77,0x5F,0x70,0x6F,0x73,0x00,0x00,0x00,0x00,0x06,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x06,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x75,0x76,0x5F,0x6D,0x69,0x6E,0x00,0x00, + 0x06,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x75,0x76,0x5F,0x6D, + 0x61,0x78,0x00,0x00,0x06,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75,0x76,0x00,0x00,0x00,0x06,0x00,0x06,0x00, + 0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74, + 0x65,0x73,0x00,0x00,0x05,0x00,0x11,0x00,0x15,0x00,0x00,0x00,0x73,0x68,0x61,0x64, + 0x65,0x72,0x28,0x76,0x66,0x34,0x3B,0x73,0x74,0x72,0x75,0x63,0x74,0x2D,0x53,0x68, + 0x61,0x64,0x65,0x72,0x50,0x61,0x72,0x61,0x6D,0x73,0x2D,0x76,0x66,0x32,0x2D,0x76, + 0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76, + 0x66,0x34,0x31,0x3B,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x13,0x00,0x00,0x00, + 0x63,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x14,0x00,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x73,0x00,0x00,0x05,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x70,0x69,0x78,0x65,0x6C,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x1B,0x00,0x00,0x00, + 0x73,0x65,0x61,0x6D,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x32,0x00,0x00,0x00, + 0x6E,0x6F,0x69,0x73,0x65,0x5F,0x75,0x76,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x45,0x00,0x00,0x00,0x6E,0x6F,0x69,0x73,0x65,0x5F,0x63,0x00,0x05,0x00,0x05,0x00, + 0x49,0x00,0x00,0x00,0x6E,0x6F,0x69,0x73,0x65,0x5F,0x74,0x65,0x78,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x4D,0x00,0x00,0x00,0x73,0x68,0x64,0x5F,0x75,0x6E,0x69,0x66, + 0x6F,0x72,0x6D,0x73,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x4D,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x61,0x6D,0x70,0x6C,0x69,0x74,0x75,0x64,0x65,0x00,0x00,0x00, + 0x06,0x00,0x05,0x00,0x4D,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x74,0x69,0x6D,0x65, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x4D,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x73,0x68,0x6F,0x77,0x5F,0x6E,0x6F,0x69,0x73,0x65,0x00,0x00,0x05,0x00,0x03,0x00, + 0x4F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x5A,0x00,0x00,0x00, + 0x6F,0x66,0x66,0x73,0x65,0x74,0x00,0x00,0x05,0x00,0x03,0x00,0x65,0x00,0x00,0x00, + 0x63,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x66,0x00,0x00,0x00,0x77,0x61,0x74,0x65, + 0x72,0x5F,0x74,0x65,0x78,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x7F,0x00,0x00,0x00, + 0x63,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x80,0x00,0x00,0x00, + 0x75,0x5F,0x69,0x6D,0x61,0x67,0x65,0x00,0x05,0x00,0x04,0x00,0x83,0x00,0x00,0x00, + 0x76,0x5F,0x75,0x76,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x84,0x00,0x00,0x00, + 0x75,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x5F,0x62,0x6C,0x6F,0x63,0x6B,0x00,0x00,0x00, + 0x06,0x00,0x07,0x00,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x75,0x5F,0x74,0x65, + 0x78,0x74,0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00,0x06,0x00,0x07,0x00, + 0x84,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x75,0x5F,0x61,0x6C,0x70,0x68,0x61,0x5F, + 0x64,0x69,0x73,0x63,0x61,0x72,0x64,0x00,0x05,0x00,0x03,0x00,0x86,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x87,0x00,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x89,0x00,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x90,0x00,0x00,0x00,0x70,0x6F,0x73,0x00, + 0x05,0x00,0x04,0x00,0x91,0x00,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x93,0x00,0x00,0x00,0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75, + 0x76,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x94,0x00,0x00,0x00,0x76,0x5F,0x70,0x6F, + 0x73,0x48,0x00,0x00,0x05,0x00,0x03,0x00,0x9C,0x00,0x00,0x00,0x73,0x70,0x00,0x00, + 0x05,0x00,0x05,0x00,0xA9,0x00,0x00,0x00,0x76,0x5F,0x70,0x61,0x72,0x61,0x6D,0x73, + 0x00,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xAC,0x00,0x00,0x00,0x63,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xAD,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xAF,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xC1,0x00,0x00,0x00,0x72,0x65,0x73,0x75,0x6C,0x74,0x00,0x00, + 0x47,0x00,0x04,0x00,0x49,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x49,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x47,0x00,0x03,0x00,0x4D,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x4D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x4D,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x4D,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x66,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x66,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x80,0x00,0x00,0x00, + 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x80,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x83,0x00,0x00,0x00, + 0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x84,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x84,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x84,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x86,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x86,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x91,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x94,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0xA9,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0xC1,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x13,0x00,0x02,0x00, + 0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x21,0x00,0x05,0x00, + 0x09,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x0F,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x0E,0x00,0x00,0x00, + 0x1E,0x00,0x08,0x00,0x10,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x0E,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x21,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x1D,0x00,0x00,0x00, + 0x00,0x00,0x00,0x3F,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x00,0x00,0x00,0xBF,0x15,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x34,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x37,0x00,0x00,0x00, + 0x00,0x00,0xA0,0x40,0x15,0x00,0x04,0x00,0x38,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x38,0x00,0x00,0x00,0x39,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x3A,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x3F,0x00,0x00,0x00, + 0x00,0x00,0x70,0x40,0x2B,0x00,0x04,0x00,0x38,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x46,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0x00,0x03,0x00,0x47,0x00,0x00,0x00, + 0x46,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x48,0x00,0x00,0x00,0x49,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x1E,0x00,0x05,0x00,0x4D,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x4E,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x4D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x4E,0x00,0x00,0x00, + 0x4F,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x33,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x51,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x5B,0x00,0x00,0x00,0xCD,0xCC,0xCC,0x3A,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x5C,0x00,0x00,0x00,0x89,0x88,0x08,0x3B,0x2C,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x5D,0x00,0x00,0x00,0x5B,0x00,0x00,0x00,0x5C,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x33,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x48,0x00,0x00,0x00,0x66,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x33,0x00,0x00,0x00,0x6D,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x02,0x00, + 0x71,0x00,0x00,0x00,0x2C,0x00,0x07,0x00,0x0E,0x00,0x00,0x00,0x77,0x00,0x00,0x00, + 0x1D,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x1D,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x48,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x82,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x82,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x1E,0x00,0x04,0x00,0x84,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x33,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x85,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x84,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x85,0x00,0x00,0x00,0x86,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x8A,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x8F,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x8F,0x00,0x00,0x00,0x90,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x82,0x00,0x00,0x00,0x91,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x8F,0x00,0x00,0x00,0x93,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x82,0x00,0x00,0x00,0x94,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x96,0x00,0x00,0x00,0x00,0x00,0x80,0x3F, + 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x97,0x00,0x00,0x00,0x00,0x00,0x80,0xBF, + 0x2C,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x96,0x00,0x00,0x00, + 0x97,0x00,0x00,0x00,0x2C,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0xA1,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x33,0x00,0x00,0x00, + 0xA3,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x33,0x00,0x00,0x00, + 0xA7,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0xA8,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xA8,0x00,0x00,0x00, + 0xA9,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0xB2,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x38,0x00,0x00,0x00, + 0xB8,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0xC0,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xC0,0x00,0x00,0x00, + 0xC1,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x05,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00,0x7F,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x87,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x89,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x9C,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00,0xAC,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00,0xAD,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xAF,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x47,0x00,0x00,0x00,0x81,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x83,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x87,0x00,0x00,0x00,0x88,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x8A,0x00,0x00,0x00,0x8B,0x00,0x00,0x00,0x86,0x00,0x00,0x00, + 0x61,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x8C,0x00,0x00,0x00, + 0x8B,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x89,0x00,0x00,0x00,0x8C,0x00,0x00,0x00, + 0x39,0x00,0x06,0x00,0x07,0x00,0x00,0x00,0x8D,0x00,0x00,0x00,0x0C,0x00,0x00,0x00, + 0x87,0x00,0x00,0x00,0x89,0x00,0x00,0x00,0x57,0x00,0x05,0x00,0x0E,0x00,0x00,0x00, + 0x8E,0x00,0x00,0x00,0x81,0x00,0x00,0x00,0x8D,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x7F,0x00,0x00,0x00,0x8E,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x92,0x00,0x00,0x00,0x91,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x90,0x00,0x00,0x00, + 0x92,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x95,0x00,0x00,0x00, + 0x94,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x99,0x00,0x00,0x00, + 0x95,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x8E,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x9A,0x00,0x00,0x00,0x99,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x85,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x9B,0x00,0x00,0x00,0x9A,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x93,0x00,0x00,0x00,0x9B,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x9D,0x00,0x00,0x00,0x90,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x9E,0x00,0x00,0x00,0x9C,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x9E,0x00,0x00,0x00,0x9D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x9F,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0xA0,0x00,0x00,0x00,0x9C,0x00,0x00,0x00,0x50,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xA0,0x00,0x00,0x00,0x9F,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0xA2,0x00,0x00,0x00,0x9C,0x00,0x00,0x00,0x6D,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xA2,0x00,0x00,0x00,0xA1,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0xA4,0x00,0x00,0x00,0x9C,0x00,0x00,0x00,0xA3,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xA4,0x00,0x00,0x00,0xA1,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xA5,0x00,0x00,0x00,0x93,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0xA6,0x00,0x00,0x00,0x9C,0x00,0x00,0x00,0x34,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xA6,0x00,0x00,0x00,0xA5,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x0E,0x00,0x00,0x00,0xAA,0x00,0x00,0x00,0xA9,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x0F,0x00,0x00,0x00,0xAB,0x00,0x00,0x00,0x9C,0x00,0x00,0x00,0xA7,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xAB,0x00,0x00,0x00,0xAA,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x0E,0x00,0x00,0x00,0xAE,0x00,0x00,0x00,0x7F,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xAD,0x00,0x00,0x00,0xAE,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xB0,0x00,0x00,0x00,0x9C,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xAF,0x00,0x00,0x00, + 0xB0,0x00,0x00,0x00,0x39,0x00,0x06,0x00,0x0E,0x00,0x00,0x00,0xB1,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0xAD,0x00,0x00,0x00,0xAF,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xAC,0x00,0x00,0x00,0xB1,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0xB2,0x00,0x00,0x00, + 0xB3,0x00,0x00,0x00,0x86,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x33,0x00,0x00,0x00,0xB4,0x00,0x00,0x00,0xB3,0x00,0x00,0x00,0xAB,0x00,0x05,0x00, + 0x71,0x00,0x00,0x00,0xB5,0x00,0x00,0x00,0xB4,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0xF7,0x00,0x03,0x00,0xB7,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0xB5,0x00,0x00,0x00,0xB6,0x00,0x00,0x00,0xB7,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xB6,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3A,0x00,0x00,0x00,0xB9,0x00,0x00,0x00, + 0xAC,0x00,0x00,0x00,0xB8,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0xBA,0x00,0x00,0x00,0xB9,0x00,0x00,0x00,0xB4,0x00,0x05,0x00,0x71,0x00,0x00,0x00, + 0xBB,0x00,0x00,0x00,0xBA,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xB7,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0xB7,0x00,0x00,0x00,0xF5,0x00,0x07,0x00, + 0x71,0x00,0x00,0x00,0xBC,0x00,0x00,0x00,0xB5,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0xBB,0x00,0x00,0x00,0xB6,0x00,0x00,0x00,0xF7,0x00,0x03,0x00,0xBE,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0xBC,0x00,0x00,0x00,0xBD,0x00,0x00,0x00, + 0xBE,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0xBD,0x00,0x00,0x00,0xFC,0x00,0x01,0x00, + 0xF8,0x00,0x02,0x00,0xBE,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00, + 0xC2,0x00,0x00,0x00,0xAC,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xC1,0x00,0x00,0x00, + 0xC2,0x00,0x00,0x00,0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x08,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x0D,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0A,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x0B,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x19,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x17,0x00,0x00,0x00,0x1A,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x50,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x1D,0x00,0x00,0x00, + 0x1D,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00, + 0x1C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0C,0x00,0x06,0x00,0x07,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x1F,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x1B,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,0x83,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0xD1,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x88,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x24,0x00,0x00,0x00, + 0x26,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x29,0x00,0x00,0x00, + 0x28,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x2A,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x0C,0x00,0x08,0x00, + 0x07,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00, + 0x27,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x2A,0x00,0x00,0x00,0x81,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x2C,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x2B,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x17,0x00,0x00,0x00,0x2C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x2D,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x88,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x2D,0x00,0x00,0x00,0x2E,0x00,0x00,0x00, + 0xFE,0x00,0x02,0x00,0x2F,0x00,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00, + 0x0E,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x12,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x0F,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x11,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x16,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x5A,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x36,0x00,0x00,0x00, + 0x35,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x32,0x00,0x00,0x00,0x36,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x3A,0x00,0x00,0x00,0x3B,0x00,0x00,0x00,0x32,0x00,0x00,0x00, + 0x39,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x3C,0x00,0x00,0x00, + 0x3B,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x3D,0x00,0x00,0x00, + 0x3C,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3A,0x00,0x00,0x00, + 0x3E,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x3E,0x00,0x00,0x00,0x3D,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3A,0x00,0x00,0x00, + 0x41,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x85,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x3F,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x3A,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x32,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x44,0x00,0x00,0x00,0x43,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x47,0x00,0x00,0x00,0x4A,0x00,0x00,0x00,0x49,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x4B,0x00,0x00,0x00,0x32,0x00,0x00,0x00, + 0x8E,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x4B,0x00,0x00,0x00, + 0x1D,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x51,0x00,0x00,0x00,0x52,0x00,0x00,0x00, + 0x4F,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x53,0x00,0x00,0x00,0x52,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x54,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x81,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x55,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x54,0x00,0x00,0x00, + 0x57,0x00,0x05,0x00,0x0E,0x00,0x00,0x00,0x56,0x00,0x00,0x00,0x4A,0x00,0x00,0x00, + 0x55,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x57,0x00,0x00,0x00, + 0x56,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0x0E,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x58,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x45,0x00,0x00,0x00, + 0x59,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x3A,0x00,0x00,0x00,0x5E,0x00,0x00,0x00, + 0x45,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x5F,0x00,0x00,0x00,0x5E,0x00,0x00,0x00,0x8E,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x60,0x00,0x00,0x00,0x5D,0x00,0x00,0x00,0x5F,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x51,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x62,0x00,0x00,0x00, + 0x8E,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x63,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x5A,0x00,0x00,0x00,0x64,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x47,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x66,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x68,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x69,0x00,0x00,0x00, + 0x68,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x6A,0x00,0x00,0x00, + 0x5A,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x6B,0x00,0x00,0x00, + 0x69,0x00,0x00,0x00,0x6A,0x00,0x00,0x00,0x57,0x00,0x05,0x00,0x0E,0x00,0x00,0x00, + 0x6C,0x00,0x00,0x00,0x67,0x00,0x00,0x00,0x6B,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x65,0x00,0x00,0x00,0x6C,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x51,0x00,0x00,0x00, + 0x6E,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x6D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x6F,0x00,0x00,0x00,0x6E,0x00,0x00,0x00,0xBA,0x00,0x05,0x00, + 0x71,0x00,0x00,0x00,0x72,0x00,0x00,0x00,0x6F,0x00,0x00,0x00,0x70,0x00,0x00,0x00, + 0xF7,0x00,0x03,0x00,0x75,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0x72,0x00,0x00,0x00,0x74,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x74,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x76,0x00,0x00,0x00, + 0x45,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x0E,0x00,0x00,0x00,0x78,0x00,0x00,0x00, + 0x76,0x00,0x00,0x00,0x77,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x73,0x00,0x00,0x00, + 0x78,0x00,0x00,0x00,0xF9,0x00,0x02,0x00,0x75,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x79,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x7A,0x00,0x00,0x00, + 0x65,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x73,0x00,0x00,0x00,0x7A,0x00,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x75,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x75,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x7B,0x00,0x00,0x00,0x73,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x65,0x00,0x00,0x00,0x7B,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x0E,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0xFE,0x00,0x02,0x00, + 0x7C,0x00,0x00,0x00,0x38,0x00,0x01,0x00, }; +#endif +static const char s_waves_shd_bytecode_blit_glsl300_src[2359] = +"#version 300 es\n" +"precision mediump float;\n" +"precision highp int;\n" +"\n" +"struct ShaderParams\n" +"{\n" +" highp vec2 view_pos;\n" +" highp vec2 uv;\n" +" highp vec2 uv_min;\n" +" highp vec2 uv_max;\n" +" highp vec2 screen_uv;\n" +" highp vec4 attributes;\n" +"};\n" +"\n" +"layout(std140) uniform shd_uniforms\n" +"{\n" +" highp float amplitude;\n" +" highp float time;\n" +" highp float show_noise;\n" +"} _79;\n" +"\n" +"layout(std140) uniform uniform_block\n" +"{\n" +" highp vec2 u_texture_size;\n" +" int u_alpha_discard;\n" +"} _134;\n" +"\n" +"uniform highp sampler2D noise_tex;\n" +"uniform highp sampler2D water_tex;\n" +"uniform highp sampler2D u_image;\n" +"\n" +"in highp vec2 v_uv;\n" +"in highp vec2 v_pos;\n" +"in highp vec2 v_posH;\n" +"in highp vec4 v_params;\n" +"layout(location = 0) out highp vec4 result;\n" +"highp vec2 pos;\n" +"highp vec2 screen_uv;\n" +"\n" +"highp vec2 smooth_uv(highp vec2 uv, highp vec2 texture_size)\n" +"{\n" +" highp vec2 pixel = uv * texture_size;\n" +" highp vec2 seam = floor(pixel + vec2(0.5));\n" +" pixel = seam + clamp((pixel - seam) / fwidth(pixel), vec2(-0.5), vec2(0.5));\n" +" return pixel / texture_size;\n" +"}\n" +"\n" +"highp vec4 shader(highp vec4 color, ShaderParams params)\n" +"{\n" +" highp vec2 noise_uv = params.screen_uv;\n" +" noise_uv.x *= 5.0;\n" +" noise_uv.y *= 3.75;\n" +" highp vec4 noise_c = vec4(texture(noise_tex, (noise_uv * 0.5) + vec2(_79.time)).x - 0.5);\n" +" highp vec2 offset = (vec2(0.001562500023283064365386962890625, 0.00208333344198763370513916015625) * noise_c.x) * _79.amplitude;\n" +" highp vec4 c = texture(water_tex, params.screen_uv + offset);\n" +" highp vec4 _115;\n" +" if (_79.show_noise > 0.0)\n" +" {\n" +" _115 = noise_c + vec4(0.5);\n" +" }\n" +" else\n" +" {\n" +" _115 = c;\n" +" }\n" +" c = _115;\n" +" return c;\n" +"}\n" +"\n" +"void main()\n" +"{\n" +" highp vec2 param = v_uv;\n" +" highp vec2 param_1 = _134.u_texture_size;\n" +" highp vec4 color = texture(u_image, smooth_uv(param, param_1));\n" +" pos = v_pos;\n" +" screen_uv = ((v_posH + vec2(1.0, -1.0)) * 0.5) * vec2(1.0, -1.0);\n" +" ShaderParams sp;\n" +" sp.view_pos = pos;\n" +" sp.uv = v_uv;\n" +" sp.uv_min = vec2(0.0);\n" +" sp.uv_max = vec2(0.0);\n" +" sp.screen_uv = screen_uv;\n" +" sp.attributes = v_params;\n" +" highp vec4 param_2 = color;\n" +" ShaderParams param_3 = sp;\n" +" highp vec4 c = shader(param_2, param_3);\n" +" bool _181 = _134.u_alpha_discard != 0;\n" +" bool _188;\n" +" if (_181)\n" +" {\n" +" _188 = c.w == 0.0;\n" +" }\n" +" else\n" +" {\n" +" _188 = _181;\n" +" }\n" +" if (_188)\n" +" {\n" +" discard;\n" +" }\n" +" result = c;\n" +"}\n" +"\n" +""; static const char* s_waves_shd_bytecode_blit_image_names[3] = { "u_image", "water_tex", "noise_tex", }; +static int s_waves_shd_bytecode_blit_image_binding_slots[3] = { 0, 1, 2, }; static CF_ShaderUniformInfo s_waves_shd_bytecode_blit_uniforms[2] = { - { - .block_name = "uniform_block", - .block_index = 0, - .block_size = 16, - .num_members = 2, - }, - { - .block_name = "shd_uniforms", - .block_index = 1, - .block_size = 16, - .num_members = 3, - }, + { + .block_name = "uniform_block", + .block_index = 0, + .block_size = 16, + .num_members = 2, + }, + { + .block_name = "shd_uniforms", + .block_index = 1, + .block_size = 16, + .num_members = 3, + }, }; static CF_ShaderUniformMemberInfo s_waves_shd_bytecode_blit_uniform_members[5] = { - { - .name = "u_texture_size", - .type = CF_SHADER_INFO_TYPE_FLOAT2, - .offset = 0, - .array_length = 1, - }, - { - .name = "u_alpha_discard", - .type = CF_SHADER_INFO_TYPE_SINT, - .offset = 8, - .array_length = 1, - }, - { - .name = "amplitude", - .type = CF_SHADER_INFO_TYPE_FLOAT, - .offset = 0, - .array_length = 1, - }, - { - .name = "time", - .type = CF_SHADER_INFO_TYPE_FLOAT, - .offset = 4, - .array_length = 1, - }, - { - .name = "show_noise", - .type = CF_SHADER_INFO_TYPE_FLOAT, - .offset = 8, - .array_length = 1, - }, -}; -static CF_ShaderInputInfo* s_waves_shd_bytecode_blit_inputs = NULL; -static const CF_ShaderBytecode s_waves_shd_bytecode_blit = { - .content = s_waves_shd_bytecode_blit_content, - .size = 4652, - .shader_info = { - .num_samplers = 3, - .num_storage_textures = 0, - .num_storage_buffers = 0, - .num_images = 3, - .image_names = s_waves_shd_bytecode_blit_image_names, - .num_uniforms = 2, - .uniforms = s_waves_shd_bytecode_blit_uniforms, - .num_uniform_members = 5, - .uniform_members = s_waves_shd_bytecode_blit_uniform_members, - .num_inputs = 0, - .inputs = s_waves_shd_bytecode_blit_inputs, - }, + { + .name = "u_texture_size", + .type = CF_SHADER_INFO_TYPE_FLOAT2, + .offset = 0, + .array_length = 1, + }, + { + .name = "u_alpha_discard", + .type = CF_SHADER_INFO_TYPE_SINT, + .offset = 8, + .array_length = 1, + }, + { + .name = "amplitude", + .type = CF_SHADER_INFO_TYPE_FLOAT, + .offset = 0, + .array_length = 1, + }, + { + .name = "time", + .type = CF_SHADER_INFO_TYPE_FLOAT, + .offset = 4, + .array_length = 1, + }, + { + .name = "show_noise", + .type = CF_SHADER_INFO_TYPE_FLOAT, + .offset = 8, + .array_length = 1, + }, }; +#define s_waves_shd_bytecode_blit_inputs NULL static const CF_DrawShaderBytecode s_waves_shd_bytecode = { - .draw_shader = s_waves_shd_bytecode_draw, - .blit_shader = s_waves_shd_bytecode_blit, + .draw_shader = { +#ifndef __EMSCRIPTEN__ + .content = s_waves_shd_bytecode_draw_content, + .size = 22992, +#endif + .glsl300_src = s_waves_shd_bytecode_draw_glsl300_src, + .glsl300_src_size = 10540, + .shader_info = { + .num_samplers = 3, + .num_storage_textures = 0, + .num_storage_buffers = 0, + .num_readwrite_storage_textures = 0, + .num_readwrite_storage_buffers = 0, + .num_images = 3, + .image_names = s_waves_shd_bytecode_draw_image_names, + .image_binding_slots = s_waves_shd_bytecode_draw_image_binding_slots, + .num_uniforms = 2, + .uniforms = s_waves_shd_bytecode_draw_uniforms, + .num_uniform_members = 6, + .uniform_members = s_waves_shd_bytecode_draw_uniform_members, + .num_inputs = 0, + .inputs = s_waves_shd_bytecode_draw_inputs, + }, + }, + .blit_shader = { +#ifndef __EMSCRIPTEN__ + .content = s_waves_shd_bytecode_blit_content, + .size = 5160, +#endif + .glsl300_src = s_waves_shd_bytecode_blit_glsl300_src, + .glsl300_src_size = 2358, + .shader_info = { + .num_samplers = 3, + .num_storage_textures = 0, + .num_storage_buffers = 0, + .num_readwrite_storage_textures = 0, + .num_readwrite_storage_buffers = 0, + .num_images = 3, + .image_names = s_waves_shd_bytecode_blit_image_names, + .image_binding_slots = s_waves_shd_bytecode_blit_image_binding_slots, + .num_uniforms = 2, + .uniforms = s_waves_shd_bytecode_blit_uniforms, + .num_uniform_members = 5, + .uniform_members = s_waves_shd_bytecode_blit_uniform_members, + .num_inputs = 0, + .inputs = s_waves_shd_bytecode_blit_inputs, + }, + }, }; diff --git a/samples/window_resizing.cpp b/samples/window_resizing.cpp index edde25840..c172f1a0e 100644 --- a/samples/window_resizing.cpp +++ b/samples/window_resizing.cpp @@ -1,41 +1,131 @@ #include using namespace Cute; +enum ScaleMode +{ + SCALE_MODE_LETTERBOX, + SCALE_MODE_CROP, + SCALE_MODE_STRETCH, + SCALE_MODE_COUNT, +}; + +const char* scale_mode_names[] = { + "Letterbox", + "Crop", + "Stretch", +}; + +static CF_V2 calculate_dest_size(int game_w, int game_h, int window_w, int window_h, ScaleMode scale_mode) +{ + float game_aspect = (float)game_w / (float)game_h; + float window_aspect = (float)window_w / (float)window_h; + + float dest_w, dest_h; + + switch (scale_mode) { + case SCALE_MODE_STRETCH: + dest_w = (float)window_w; + dest_h = (float)window_h; + break; + + case SCALE_MODE_LETTERBOX: + if (window_aspect > game_aspect) { + // Window is wider - pillarbox (black bars on sides). + dest_h = (float)window_h; + dest_w = dest_h * game_aspect; + } else { + // Window is taller - letterbox (black bars on top/bottom). + dest_w = (float)window_w; + dest_h = dest_w / game_aspect; + } + break; + + case SCALE_MODE_CROP: + if (window_aspect > game_aspect) { + // Window is wider - fill width, crop top/bottom. + dest_w = (float)window_w; + dest_h = dest_w / game_aspect; + } else { + // Window is taller - fill height, crop sides. + dest_h = (float)window_h; + dest_w = dest_h * game_aspect; + } + break; + + default: + dest_w = (float)window_w; + dest_h = (float)window_h; + break; + } + + return V2(dest_w, dest_h); +} + int main(int argc, char* argv[]) { - int w = 640; - int h = 480; - make_app("Window Resizing", 0, 0, 0, w, h, CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT | CF_APP_OPTIONS_RESIZABLE_BIT, argv[0]); + // Fixed game resolution. + int game_w = 320; + int game_h = 240; + + // Initial window size (can be resized). + int window_w = 640; + int window_h = 480; + + make_app("Window Resizing", 0, 0, 0, window_w, window_h, CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT | CF_APP_OPTIONS_RESIZABLE_BIT, argv[0]); + + // Create a fixed-size canvas to draw to. + CF_Canvas canvas = make_canvas(canvas_defaults(game_w, game_h)); - draw_push_antialias(true); + // Set up projection for the game canvas. + draw_projection(ortho_2d(0, 0, (float)game_w, (float)game_h)); - printf("Press space to toggle on/off app canvas auto-resizing.\n"); - printf("Auto resizing is now enabled.\n"); - bool auto_resize = true; + ScaleMode scale_mode = SCALE_MODE_LETTERBOX; while (app_is_running()) { app_update(); + // Toggle scale mode with space bar. if (key_just_pressed(CF_KEY_SPACE)) { - auto_resize = !auto_resize; - printf("Canvas auto-resizing is now %s\n", auto_resize ? "enabled" : "disabled"); + scale_mode = (ScaleMode)((scale_mode + 1) % SCALE_MODE_COUNT); } - if (auto_resize && app_was_resized()) { - app_get_size(&w, &h); - w = CF_ALIGN_TRUNCATE(w, 2); - h = CF_ALIGN_TRUNCATE(h, 2); - app_set_size(w, h); - app_set_canvas_size(w, h); - draw_scale((float)w, (float)h); - printf("%d, %d\n", w, h); - } + // Draw some shapes to visualize the game area (centered coordinates). + float hw = game_w / 2.0f; + float hh = game_h / 2.0f; + draw_push_color(color_grey()); + draw_box(CF_Aabb { -hw, -hh, hw, hh }, 4.0f, 0); + draw_pop_color(); + draw_circle(V2(0, 0), 50.0f, 5.0f); + draw_line(V2(-hw, -hh), V2(hw, hh), 2.0f); + draw_line(V2(hw, -hh), V2(-hw, hh), 2.0f); + + // Draw instructions. + draw_push_color(color_white()); + char buf[128]; + snprintf(buf, sizeof(buf), "Mode: %s", scale_mode_names[scale_mode]); + draw_text(buf, V2(-hw + 10, hh - 20.0f), -1); + draw_text("Press SPACE to cycle modes", V2(-hw + 10, hh - 40.0f), -1); + draw_pop_color(); + + // Draw game content to the canvas. + render_to(canvas, true); + + // Calculate scaled size based on current mode. + app_get_size(&window_w, &window_h); + app_set_canvas_size(window_w, window_h); + CF_V2 dest = calculate_dest_size(game_w, game_h, window_w, window_h, scale_mode); + + // Draw the canvas scaled and centered in the window. + draw_projection(ortho_2d(0, 0, (float)window_w, (float)window_h)); + draw_canvas(canvas, V2(0,0), dest); - draw_circle(V2(0,0), 100.0f, 10.0f); + // Restore projection for next frame. + draw_projection(ortho_2d(0, 0, (float)game_w, (float)game_h)); - app_draw_onto_screen(); + app_draw_onto_screen(true); } + destroy_canvas(canvas); destroy_app(); return 0; diff --git a/src/cute_app.cpp b/src/cute_app.cpp index 6b50ad577..3188b6e95 100644 --- a/src/cute_app.cpp +++ b/src/cute_app.cpp @@ -19,9 +19,10 @@ #include #include #include -#include +#include #include #include +#include #include @@ -136,7 +137,7 @@ CF_DisplayOrientation cf_display_orientation(CF_DisplayID display_id) } } -CF_GLOBAL CF_App* app; +CF_API CF_App* app; using namespace Cute; @@ -177,6 +178,11 @@ CF_Result cf_make_app(const char* window_title, CF_DisplayID display_id, int x, #ifdef CF_EMSCRIPTEN // This is the only supported backend as of now use_opengl = true; +#elif defined(CF_ANDROID) + // On Android, default to Vulkan via SDL_GPU. GLES3 fallback is handled below. + if (!use_vulkan && !use_opengl) { + use_vulkan = true; + } #else if (use_opengl) { fprintf(stderr, "WARNING -- OpenGL ES is not intended for non-web builds! Use at your own risk (i.e. for developement or debugging).\n"); @@ -251,6 +257,16 @@ CF_Result cf_make_app(const char* window_title, CF_DisplayID display_id, int x, } init_gfx_result = cf_sdlgpu_init(device_name, debug, &gfx_backend_type); +# ifdef CF_ANDROID + // On Android, fall back to GLES3 if Vulkan/SDL_GPU fails. + if (cf_is_error(init_gfx_result)) { + init_gfx_result = cf_gles_init(debug); + if (!cf_is_error(init_gfx_result)) { + gfx_backend_type = CF_BACKEND_TYPE_GLES3; + use_opengl = true; + } + } +# endif #endif } else { init_gfx_result = cf_gles_init(debug); @@ -263,32 +279,34 @@ CF_Result cf_make_app(const char* window_title, CF_DisplayID display_id, int x, } } - Uint32 flags = 0; - flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY; // Turn on high DPI support for all platforms. - if (use_opengl) flags |= SDL_WINDOW_OPENGL; - if (use_metal) flags |= SDL_WINDOW_METAL; - if (options & CF_APP_OPTIONS_FULLSCREEN_BIT) flags |= SDL_WINDOW_FULLSCREEN; - if (options & CF_APP_OPTIONS_RESIZABLE_BIT) flags |= SDL_WINDOW_RESIZABLE; - if (options & CF_APP_OPTIONS_HIDDEN_BIT) flags |= (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED); - - SDL_Window* window; - SDL_PropertiesID props = SDL_CreateProperties(); - SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, window_title); - SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, w); - SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, h); - SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags); - if (options & CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT) { - SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED_DISPLAY(display_id)); - SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED_DISPLAY(display_id)); - } else { - if (display_id == 0) display_id = SDL_GetPrimaryDisplay(); - int x_offset = display_x(display_id); - int y_offset = display_y(display_id); - SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, x_offset+x); - SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y_offset+y); + SDL_Window* window = NULL; + if (use_gfx) { + Uint32 flags = 0; + flags |= SDL_WINDOW_HIGH_PIXEL_DENSITY; // Turn on high DPI support for all platforms. + if (use_opengl) flags |= SDL_WINDOW_OPENGL; + if (use_metal) flags |= SDL_WINDOW_METAL; + if (options & CF_APP_OPTIONS_FULLSCREEN_BIT) flags |= SDL_WINDOW_FULLSCREEN; + if (options & CF_APP_OPTIONS_RESIZABLE_BIT) flags |= SDL_WINDOW_RESIZABLE; + if (options & CF_APP_OPTIONS_HIDDEN_BIT) flags |= (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED); + + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, window_title); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, w); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, h); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_FLAGS_NUMBER, flags); + if (options & CF_APP_OPTIONS_WINDOW_POS_CENTERED_BIT) { + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED_DISPLAY(display_id)); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED_DISPLAY(display_id)); + } else { + if (display_id == 0) display_id = SDL_GetPrimaryDisplay(); + int x_offset = display_x(display_id); + int y_offset = display_y(display_id); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, x_offset+x); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, y_offset+y); + } + window = SDL_CreateWindowWithProperties(props); + SDL_DestroyProperties(props); } - window = SDL_CreateWindowWithProperties(props); - SDL_DestroyProperties(props); CF_App* app = (CF_App*)CF_ALLOC(sizeof(CF_App)); CF_PLACEMENT_NEW(app) CF_App; @@ -297,12 +315,14 @@ CF_Result cf_make_app(const char* window_title, CF_DisplayID display_id, int x, app->window = window; app->w = w; app->h = h; - SDL_GetWindowPosition(app->window, &app->x, &app->y); - app->dpi_scale = SDL_GetWindowDisplayScale(app->window); - app->dpi_scale_prev = app->dpi_scale; + if (window) { + SDL_GetWindowPosition(app->window, &app->x, &app->y); + app->dpi_scale = SDL_GetWindowDisplayScale(app->window); + app->dpi_scale_prev = app->dpi_scale; + } ::app = app; cf_make_aseprite_cache(); - cf_make_png_cache(); + cf_make_custom_sprite_cache(); if (use_gfx) { if (app->gfx_backend_type == CF_BACKEND_TYPE_GLES3) { @@ -321,9 +341,9 @@ CF_Result cf_make_app(const char* window_title, CF_DisplayID display_id, int x, // Load up a default image of 1x1 white pixel. // Used in various places as a placeholder or default. CF_Png img; - cf_png_cache_load_from_memory("cf_default_png", default_png_data, (size_t)default_png_sz, &img); + cf_custom_sprite_load_png_from_memory("cf_default_png", default_png_data, (size_t)default_png_sz, &img); app->default_image_id = img.id; - CF_ASSERT(app->default_image_id == CF_PNG_ID_RANGE_LO); + CF_ASSERT(app->default_image_id == CF_CUSTOM_SPRITE_ID_RANGE_LO); // Create the default font. make_font_from_memory(calibri_data, calibri_sz, "Calibri"); @@ -339,30 +359,19 @@ CF_Result cf_make_app(const char* window_title, CF_DisplayID display_id, int x, } #ifdef CF_WINDOWS - app->platform_handle = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); + if (window) { + app->platform_handle = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); + } #endif app->gfx_enabled = use_gfx; if (!(options & CF_APP_OPTIONS_NO_AUDIO_BIT)) { - int more_on_emscripten = 1; -#ifdef CF_EMSCRIPTEN - more_on_emscripten = 4; -#endif - cs_error_t err = cs_init(NULL, 44100, 1024 * more_on_emscripten, NULL); + cs_error_t err = cs_init(44100, NULL); if (err == CUTE_SOUND_ERROR_NONE) { -#ifndef CF_EMSCRIPTEN - cs_spawn_mix_thread(); - app->spawned_mix_thread = true; -#endif app->audio_needs_updates = true; - //cs_cull_duplicates(true); -- https://github.com/RandyGaul/cute_framework/issues/172 - } else { - CF_Result result; - result.code = -1; - result.details = cs_error_as_string(err); - return result; } + // If audio init fails, continue silently without audio. } CF_Result err = cf_fs_init(argv0); @@ -396,10 +405,10 @@ void cf_destroy_app() } } cf_destroy_aseprite_cache(); - cf_destroy_png_cache(); + cf_destroy_custom_sprite_cache(); cs_shutdown(); destroy_mutex(&app->on_sound_finish_mutex); - SDL_DestroyWindow(app->window); + if (app->window) SDL_DestroyWindow(app->window); SDL_Quit(); cs_shutdown(); CF_Image* easy_sprites = app->easy_sprites.items(); @@ -424,6 +433,7 @@ void cf_app_signal_shutdown() static void s_on_update(void* udata) { cf_pump_input_msgs(); + cf_binding_update(); if (app->audio_needs_updates) { cs_update(CF_DELTA_TIME); if (app->on_sound_finish_single_threaded) { @@ -446,16 +456,6 @@ static void s_on_update(void* udata) void cf_app_update(CF_OnUpdateFn* on_update) { if (app->gfx_enabled) { - // Deal with DPI scaling. - int pw = 0, ph = 0; - SDL_GetWindowSizeInPixels(app->window, &pw, &ph); - app->dpi_scale = (float)ph / (float)app->h; - app->dpi_scale_was_changed = false; - if (app->dpi_scale != app->dpi_scale_prev) { - app->dpi_scale_was_changed = true; - app->dpi_scale_prev = app->dpi_scale; - } - if (app->using_imgui) { if (app->gfx_backend_type == CF_BACKEND_TYPE_GLES3) { ImGui_ImplOpenGL3_NewFrame(); @@ -563,7 +563,6 @@ int cf_app_draw_onto_screen(bool clear) s_draw->alpha_discards.set_count(1); s_draw->colors.set_count(1); s_draw->antialias.set_count(1); - s_draw->antialias_scale.set_count(1); s_draw->render_states.set_count(1); s_draw->scissors.set_count(1); s_draw->viewports.set_count(1); diff --git a/src/cute_array.cpp b/src/cute_array.cpp deleted file mode 100644 index b5cd43e69..000000000 --- a/src/cute_array.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - Cute Framework - Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ - - This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info -*/ - -#include "cute_array.h" -#include "cute_math.h" - -#include - -using namespace Cute; - -void* cf_agrow(const void* a, int new_size, size_t element_size) -{ - CF_ACANARY(a); - CF_ASSERT(acap(a) <= (SIZE_MAX - 1)/2); - int new_capacity = max(2 * acap(a), max(new_size, 16)); - CF_ASSERT(new_size <= new_capacity); - CF_ASSERT(new_capacity <= (SIZE_MAX - sizeof(CF_Ahdr)) / element_size); - size_t total_size = sizeof(CF_Ahdr) + new_capacity * element_size; - CF_Ahdr* hdr; - if (a) { - if (!CF_AHDR(a)->is_static) { - hdr = (CF_Ahdr*)CF_REALLOC(CF_AHDR(a), total_size); - } else { - hdr = (CF_Ahdr*)CF_ALLOC(total_size); - CF_MEMCPY(hdr + 1, a, alen(a) * element_size); - hdr->size = asize(a); - hdr->cookie = CF_ACOOKIE; - } - } else { - hdr = (CF_Ahdr*)CF_ALLOC(total_size); - hdr->size = 0; - hdr->cookie = CF_ACOOKIE; - } - hdr->capacity = new_capacity; - hdr->is_static = false; - hdr->data = (char*)(hdr + 1); // For debugging convenience. - return (void*)(hdr + 1); -} - -void* cf_astatic(const void* a, int buffer_size, size_t element_size) -{ - CF_Ahdr* hdr = (CF_Ahdr*)a; - hdr->size = 0; - hdr->cookie = CF_ACOOKIE; - if (sizeof(CF_Ahdr) <= element_size) { - hdr->capacity = buffer_size / (int)element_size - 1; - } else { - int elements_taken = sizeof(CF_Ahdr) / (int)element_size + (sizeof(CF_Ahdr) % (int)element_size > 0); - hdr->capacity = buffer_size / (int)element_size - elements_taken; - } - hdr->data = (char*)(hdr + 1); // For debugging convenience. - hdr->is_static = true; - return (void*)(hdr + 1); -} - -void* cf_aset(const void* a, const void* b, size_t element_size) -{ - CF_ACANARY(a); - if (!b) { - aclear(a); - return (void*)a; - } - CF_ACANARY(b); - if (acap(a) < asize(b)) { - int len = asize(b); - a = cf_agrow(a, asize(b), element_size); - } - CF_MEMCPY((void*)a, b, asize(b) * element_size); - if (a) alen(a) = asize(b); - else CF_ASSERT(!asize(b)); - return (void*)a; -} - -void* cf_arev(const void* a_ptr, size_t element_size) -{ - CF_ACANARY(a_ptr); - void* a = (void*)a_ptr; - int ia = 0; - int ib = acount(a) - 1; - void* t = CF_ALLOC(element_size); // Maybe use alloca? malloc won't cause stack overflow. - void* b = (void*)((uintptr_t)a + (element_size * ib)); - - while (ia < ib) { - CF_MEMCPY(t, a, element_size); - CF_MEMCPY(a, b, element_size); - CF_MEMCPY(b, t, element_size); - a = (void*)((uintptr_t)a + (element_size * ia++)); - b = (void*)((uintptr_t)b + (element_size * ib++)); - } - - CF_FREE(t); - - return (void*)a_ptr; -} diff --git a/src/cute_aseprite_cache.cpp b/src/cute_aseprite_cache.cpp index 02024e573..6c735033e 100644 --- a/src/cute_aseprite_cache.cpp +++ b/src/cute_aseprite_cache.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include #include @@ -16,30 +18,31 @@ #define CUTE_ASEPRITE_IMPLEMENTATION #include -using namespace Cute; +#include -struct CF_AsepriteCacheEntry -{ - const char* path = NULL; - ase_t* ase = NULL; - htbl CF_Animation** animations = NULL; - dyna CF_SpriteSlice* slices = NULL; - dyna v2* pivots = NULL; - dyna CF_Aabb* center_patches = NULL; -}; +using namespace Cute; struct CF_AsepriteCache { - Map aseprites; - Map id_to_pixels; + Map id_to_pixels; uint64_t id_gen = CF_ASEPRITE_ID_RANGE_LO; + dyna CF_SpriteAsset* assets = NULL; + Map path_to_asset_id; }; -CF_GLOBAL static CF_AsepriteCache* cache; +CF_GLOBAL static CF_AsepriteCache* g_ase_cache; + +CF_SpriteAsset* cf_sprite_get_asset(uint64_t asset_id) +{ + CF_ASSERT(asset_id < (uint64_t)asize(g_ase_cache->assets)); + CF_SpriteAsset* asset = g_ase_cache->assets + asset_id; + CF_ASSERT(asset->path); + return asset; +} void cf_aseprite_cache_get_pixels(uint64_t image_id, void* buffer, int bytes_to_fill) { - auto pixels_ptr = cache->id_to_pixels.try_find(image_id); + auto pixels_ptr = g_ase_cache->id_to_pixels.try_find(image_id); if (!pixels_ptr) { CF_DEBUG_PRINTF("Aseprite cache -- unable to find id %lld.\n", (long long int)image_id); CF_MEMSET(buffer, 0, bytes_to_fill); @@ -49,30 +52,68 @@ void cf_aseprite_cache_get_pixels(uint64_t image_id, void* buffer, int bytes_to_ } } +CF_Image cf_sprite_get_pixels(CF_Sprite* sprite, int blend_index, const char* animation, int frame_index) +{ + CF_Image img = { 0 }; + if (!sprite || sprite->id == CF_SPRITE_ID_INVALID) return img; + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + if (blend_index < 0 || blend_index >= asset->blend_count) return img; + const CF_Animation* anim = map_get(asset->animations, sintern(animation)); + if (!anim) return img; + if (frame_index < 0 || frame_index >= asize(anim->frames)) return img; + int global_frame = frame_index + anim->frame_offset; + uint64_t id = asset->blend_frame_ids[blend_index][global_frame]; + img.w = sprite->w; + img.h = sprite->h; + int bytes = img.w * img.h * (int)sizeof(CF_Pixel); + img.pix = (CF_Pixel*)CF_ALLOC(bytes); + cf_aseprite_cache_get_pixels(id, img.pix, bytes); + return img; +} + void cf_make_aseprite_cache() { - cache = CF_NEW(CF_AsepriteCache); + g_ase_cache = CF_NEW(CF_AsepriteCache); } -void cf_destroy_aseprite_cache() +static void s_free_asset(CF_SpriteAsset* asset) { - int count = cache->aseprites.count(); - CF_AsepriteCacheEntry* entries = cache->aseprites.items(); - for (int i = 0; i < count; ++i) { - CF_AsepriteCacheEntry* entry = entries + i; - int animation_count = hcount(entry->animations); - for (int j = 0; j < animation_count; ++j) { - afree(entry->animations[j]->frames); - CF_FREE(entry->animations[j]); + if (asset->ase) { + // Ase-owned asset: free animation data. + CF_Animation** anim_vals = map_items(asset->animations); + for (int i = 0; i < map_size(asset->animations); ++i) { + afree(anim_vals[i]->frames); + CF_FREE(anim_vals[i]); } + map_free(asset->animations); + cute_aseprite_free(asset->ase); + } + // External assets (ase == NULL) don't own their animations. + // Free blend data (index 0 is asset->frame_ids, skip it). + for (int i = 1; i < asize(asset->blend_frame_ids); ++i) { + afree(asset->blend_frame_ids[i]); + } + afree(asset->blend_frame_ids); + for (int i = 0; i < asize(asset->owned_pixel_buffers); ++i) { + CF_FREE(asset->owned_pixel_buffers[i]); + } + afree(asset->owned_pixel_buffers); + afree(asset->slices); + afree(asset->pivots); + afree(asset->center_patches); + afree(asset->frame_ids); +} - hfree(entry->animations); - afree(entry->slices); - afree(entry->pivots); - cute_aseprite_free(entry->ase); +void cf_destroy_aseprite_cache() +{ + for (int i = 0; i < asize(g_ase_cache->assets); ++i) { + CF_SpriteAsset* asset = g_ase_cache->assets + i; + if (!asset->path) continue; + s_free_asset(asset); } - cache->~CF_AsepriteCache(); - CF_FREE(cache); + afree(g_ase_cache->assets); + g_ase_cache->~CF_AsepriteCache(); + CF_FREE(g_ase_cache); } static CF_PlayDirection s_play_direction(ase_animation_direction_t direction) @@ -85,31 +126,29 @@ static CF_PlayDirection s_play_direction(ase_animation_direction_t direction) return CF_PLAY_DIRECTION_FORWARDS; } -static void s_sprite(CF_AsepriteCacheEntry entry, CF_Sprite* sprite) +static void s_sprite(CF_SpriteAsset* asset, CF_Sprite* sprite, uint64_t asset_id) { - sprite->name = entry.path; - sprite->animations = (const CF_Animation**)entry.animations; - sprite->w = entry.ase->w; - sprite->h = entry.ase->h; - sprite->pivots = entry.pivots; - sprite->center_patches = entry.center_patches; - sprite->slices = entry.slices; - if (entry.ase->tag_count == 0) { + sprite->name = asset->path; + sprite->w = asset->w; + sprite->h = asset->h; + sprite->id = asset_id; + if (asset->ase->tag_count == 0) { cf_sprite_play(sprite, "default"); } else { - cf_sprite_play(sprite, sprite->animations[0]->name); + CF_Animation** first_anim = map_items(asset->animations); + cf_sprite_play(sprite, (*first_anim)->name); } } static CF_Result s_aseprite_cache_load_from_memory(const char* unique_name, const void* data, int sz, CF_Sprite* sprite_out) { - ase_t* ase = cute_aseprite_load_from_memory(data, (int)sz, NULL); + ase_t* ase = cute_aseprite_load_from_memory(data, (int)sz, NULL); if (!ase) return cf_result_error("Unable to open ase file at `aseprite_path`."); // Allocate internal cache data structure entries. - CF_Animation** animations = NULL; + CF_MAP(CF_Animation*) animations = NULL; Array ids; - v2* pivots = NULL; + CF_V2* pivots = NULL; CF_Aabb* center_patches = NULL; afit(pivots, ase->frame_count); afit(center_patches, ase->frame_count); @@ -117,7 +156,7 @@ static CF_Result s_aseprite_cache_load_from_memory(const char* unique_name, cons for (int i = 0; i < ase->frame_count; ++i) { // Unique sprite id. - uint64_t id = cache->id_gen++; + uint64_t id = g_ase_cache->id_gen++; ids.add(id); // Premultiply alpha. @@ -136,11 +175,11 @@ static CF_Result s_aseprite_cache_load_from_memory(const char* unique_name, cons pix[i * ase->w + j].b = (uint8_t)(b * 255.0f); } } - cache->id_to_pixels.insert(id, ase->frames[i].pixels); + g_ase_cache->id_to_pixels.insert(id, ase->frames[i].pixels); // Fill in zero'd out pivots initially. These can get overwritten from slice data // if a slice has a pivot. - apush(pivots, V2(0,0)); + apush(pivots, cf_v2(0, 0)); } // Fill out the animation table from the aseprite file. @@ -161,9 +200,9 @@ static CF_Result s_aseprite_cache_load_from_memory(const char* unique_name, cons CF_Frame frame; frame.delay = ase->frames[i].duration_milliseconds / 1000.0f; frame.id = id; - cf_animation_add_frame(animation, frame); + apush(animation->frames, frame); } - hadd(animations, animation->name, animation); + map_set(animations, (uint64_t)animation->name, animation); } } else { // Treat the entire frame set as a single animation if there are no tags. @@ -177,21 +216,16 @@ static CF_Result s_aseprite_cache_load_from_memory(const char* unique_name, cons CF_Frame frame; frame.delay = ase->frames[i].duration_milliseconds / 1000.0f; frame.id = id; - cf_animation_add_frame(animation, frame); + apush(animation->frames, frame); } - hadd(animations, animation->name, animation); + map_set(animations, (uint64_t)animation->name, animation); } // Fill out slices. - // Look for slice information to define the sprite's local offset. - // The slice named "origin"'s center is used to define the local offset. - CF_AsepriteCacheEntry entry; - entry.pivots = pivots; - entry.center_patches = center_patches; - afit(entry.slices, ase->slice_count); + CF_SpriteSlice* slices = NULL; + afit(slices, ase->slice_count); float sw = (float)ase->w; float sh = (float)ase->h; - const char* origin_slice_name = sintern("origin"); for (int i = 0; i < ase->slice_count; ++i) { ase_slice_t* slice = ase->slices + i; float x = (float)slice->origin_x - sw*0.5f; @@ -206,7 +240,7 @@ static CF_Result s_aseprite_cache_load_from_memory(const char* unique_name, cons // Record the slice. CF_Aabb bb = make_aabb(V2(x,y-h), V2(x+w,y)); const char* slice_name = sintern(slice->name); - apush(entry.slices, CF_SpriteSlice { + apush(slices, CF_SpriteSlice { .frame_index = slice->frame_number, .name = slice_name, .box = bb @@ -217,28 +251,45 @@ static CF_Result s_aseprite_cache_load_from_memory(const char* unique_name, cons CF_V2 max = cf_v2((float)slice->center_x + slice->center_w, (float)slice->center_y + slice->center_h); CF_Aabb center_patch = cf_make_aabb(min, max); for (int frame_number = slice->frame_number; frame_number < ase->frame_count; ++frame_number) { - entry.center_patches[frame_number] = center_patch; + center_patches[frame_number] = center_patch; } } - if (slice->has_pivot && slice_name == origin_slice_name) { + if (slice->has_pivot) { v2 pivot = V2((float)slice->pivot_x, (float)slice->pivot_y); // Transform from CF's (0, 0) at center to ase's (0, 0) at top-left. pivot.x = pivot.x - sw * 0.5f + 0.5f; pivot.y = pivot.y - sh * 0.5f + 0.5f; for (int frame_number = slice->frame_number; frame_number < ase->frame_count; ++frame_number) { - entry.pivots[frame_number] = pivot; + pivots[frame_number] = pivot; } } } - // Cache the ase and animation. - entry.path = unique_name; - entry.ase = ase; - entry.animations = animations; - cache->aseprites.insert(unique_name, entry); - - s_sprite(entry, sprite_out); + // Build asset and store in flat array. + uint64_t asset_id = (uint64_t)asize(g_ase_cache->assets); + CF_SpriteAsset asset = { 0 }; + asset.path = unique_name; + asset.ase = ase; + asset.w = ase->w; + asset.h = ase->h; + CF_MEMCPY(&asset.animations, &animations, sizeof(animations)); + asset.slices = slices; + asset.pivots = pivots; + asset.center_patches = center_patches; + asset.frame_ids = NULL; + for (int i = 0; i < ids.size(); ++i) { + apush(asset.frame_ids, ids[i]); + } + asset.blend_count = 1; + asset.blend_frame_ids = NULL; + apush(asset.blend_frame_ids, asset.frame_ids); + asset.owned_pixel_buffers = NULL; + apush(g_ase_cache->assets, asset); + g_ase_cache->path_to_asset_id.insert(unique_name, asset_id); + + CF_SpriteAsset* cached_asset = g_ase_cache->assets + asset_id; + s_sprite(cached_asset, sprite_out, asset_id); return cf_result_success(); } @@ -246,9 +297,10 @@ CF_Result cf_aseprite_cache_load_from_memory(const char* unique_name, const void { // First see if this ase was already cached. unique_name = sintern(unique_name); - auto entry_ptr = cache->aseprites.try_find(unique_name); - if (entry_ptr) { - s_sprite(*entry_ptr, sprite_out); + uint64_t* asset_id_ptr = g_ase_cache->path_to_asset_id.try_find(unique_name); + if (asset_id_ptr) { + CF_SpriteAsset* asset = g_ase_cache->assets + *asset_id_ptr; + s_sprite(asset, sprite_out, *asset_id_ptr); return cf_result_success(); } @@ -259,9 +311,10 @@ CF_Result cf_aseprite_cache_load(const char* aseprite_path, CF_Sprite* sprite) { // First see if this ase was already cached. aseprite_path = sintern(aseprite_path); - auto entry_ptr = cache->aseprites.try_find(aseprite_path); - if (entry_ptr) { - s_sprite(*entry_ptr, sprite); + uint64_t* asset_id_ptr = g_ase_cache->path_to_asset_id.try_find(aseprite_path); + if (asset_id_ptr) { + CF_SpriteAsset* asset = g_ase_cache->assets + *asset_id_ptr; + s_sprite(asset, sprite, *asset_id_ptr); return cf_result_success(); } @@ -277,38 +330,247 @@ CF_Result cf_aseprite_cache_load(const char* aseprite_path, CF_Sprite* sprite) void cf_aseprite_cache_unload(const char* aseprite_path) { aseprite_path = sintern(aseprite_path); - auto entry_ptr = cache->aseprites.try_find(aseprite_path); - if (!entry_ptr) return; - - CF_AsepriteCacheEntry entry = *entry_ptr; - for (int i = 0; i < hcount(entry.animations); ++i) { - CF_Animation* animation = entry.animations[i]; - for (int j = 0; j < alen(animation->frames); ++j) { - uint64_t id = animation->frames[j].id; - cache->id_to_pixels.remove(id); - spritebatch_invalidate(&s_draw->sb, id); - } + uint64_t* asset_id_ptr = g_ase_cache->path_to_asset_id.try_find(aseprite_path); + if (!asset_id_ptr) return; + + CF_SpriteAsset* asset = g_ase_cache->assets + *asset_id_ptr; - afree(animation->frames); - CF_FREE(animation); + // Invalidate all frame IDs in the spritebatch. + for (int i = 0; i < asize(asset->frame_ids); ++i) { + uint64_t id = asset->frame_ids[i]; + g_ase_cache->id_to_pixels.remove(id); + spritebatch_invalidate(&s_draw->sb, id); } - hfree(entry.animations); - cute_aseprite_free(entry.ase); - cache->aseprites.remove(aseprite_path); + s_free_asset(asset); + asset->path = NULL; + g_ase_cache->path_to_asset_id.remove(aseprite_path); } CF_Result cf_aseprite_cache_load_ase(const char* aseprite_path, ase_t** ase) { aseprite_path = sintern(aseprite_path); - CF_Sprite s; + CF_Sprite s = cf_sprite_defaults(); CF_Result err = cf_aseprite_cache_load(aseprite_path, &s); if (cf_is_error(err)) return err; - auto entry_ptr = cache->aseprites.try_find(aseprite_path); - if (!entry_ptr) return cf_result_error("Unable to load aseprite."); - else { - *ase = entry_ptr->ase; - return cf_result_success(); + uint64_t* asset_id_ptr = g_ase_cache->path_to_asset_id.try_find(aseprite_path); + if (!asset_id_ptr) return cf_result_error("Unable to load aseprite."); + *ase = (g_ase_cache->assets + *asset_id_ptr)->ase; + return cf_result_success(); +} + +void cf_aseprite_cache_reload_asset(uint64_t asset_id, ase_t* new_ase) +{ + CF_SpriteAsset* asset = g_ase_cache->assets + asset_id; + CF_ASSERT(asset->path); + CF_ASSERT(asset->ase); // Must be an ase-owned asset. + + int old_count = asize(asset->frame_ids); + int new_count = new_ase->frame_count; + int common = old_count < new_count ? old_count : new_count; + + // Reuse existing frame IDs for overlapping frames, replacing pixel data. + for (int i = 0; i < common; ++i) { + uint64_t id = asset->frame_ids[i]; + g_ase_cache->id_to_pixels.insert(id, new_ase->frames[i].pixels); + spritebatch_invalidate(&s_draw->sb, id); + } + + // Allocate new IDs for additional frames. + for (int i = common; i < new_count; ++i) { + uint64_t id = g_ase_cache->id_gen++; + apush(asset->frame_ids, id); + g_ase_cache->id_to_pixels.insert(id, new_ase->frames[i].pixels); + } + + // Remove excess old frame IDs. + for (int i = common; i < old_count; ++i) { + uint64_t id = asset->frame_ids[i]; + g_ase_cache->id_to_pixels.remove(id); + spritebatch_invalidate(&s_draw->sb, id); } + if (new_count < old_count) { + asetlen(asset->frame_ids, new_count); + } + + // Free old animation data. + CF_Animation** old_anim_vals = map_items(asset->animations); + for (int i = 0; i < map_size(asset->animations); ++i) { + afree(old_anim_vals[i]->frames); + CF_FREE(old_anim_vals[i]); + } + map_free(asset->animations); + afree(asset->slices); + afree(asset->pivots); + afree(asset->center_patches); + + // Rebuild animations from new ase. + CF_MAP(CF_Animation*) animations = NULL; + if (new_ase->tag_count) { + for (int i = 0; i < new_ase->tag_count; ++i) { + ase_tag_t* tag = new_ase->tags + i; + int from = tag->from_frame; + int to = tag->to_frame; + CF_Animation* animation = (CF_Animation*)CF_ALLOC(sizeof(CF_Animation)); + CF_MEMSET(animation, 0, sizeof(CF_Animation)); + animation->name = sintern(tag->name); + animation->play_direction = s_play_direction(tag->loop_animation_direction); + animation->frame_offset = from; + for (int j = from; j <= to; ++j) { + CF_Frame frame; + frame.delay = new_ase->frames[j].duration_milliseconds / 1000.0f; + frame.id = asset->frame_ids[j]; + apush(animation->frames, frame); + } + map_set(animations, (uint64_t)animation->name, animation); + } + } else { + CF_Animation* animation = (CF_Animation*)CF_ALLOC(sizeof(CF_Animation)); + CF_MEMSET(animation, 0, sizeof(CF_Animation)); + animation->name = sintern("default"); + animation->play_direction = CF_PLAY_DIRECTION_FORWARDS; + for (int i = 0; i < new_count; ++i) { + CF_Frame frame; + frame.delay = new_ase->frames[i].duration_milliseconds / 1000.0f; + frame.id = asset->frame_ids[i]; + apush(animation->frames, frame); + } + map_set(animations, (uint64_t)animation->name, animation); + } + CF_MEMCPY(&asset->animations, &animations, sizeof(animations)); + + // Rebuild pivots and center_patches. + CF_V2* pivots = NULL; + CF_Aabb* center_patches = NULL; + afit(pivots, new_count); + afit(center_patches, new_count); + for (int i = 0; i < new_count; ++i) { + apush(pivots, cf_v2(0, 0)); + CF_Aabb zero_aabb = { 0 }; + apush(center_patches, zero_aabb); + } + + // Rebuild slices. + CF_SpriteSlice* slices = NULL; + afit(slices, new_ase->slice_count); + float sw = (float)new_ase->w; + float sh = (float)new_ase->h; + for (int i = 0; i < new_ase->slice_count; ++i) { + ase_slice_t* slice = new_ase->slices + i; + float x = (float)slice->origin_x - sw * 0.5f; + float y = (float)slice->origin_y; + float w = (float)slice->w; + float h = (float)slice->h; + y = sh - y; + y = y - sh * 0.5f; + CF_Aabb bb = make_aabb(V2(x, y - h), V2(x + w, y)); + const char* slice_name = sintern(slice->name); + apush(slices, CF_SpriteSlice { + .frame_index = slice->frame_number, + .name = slice_name, + .box = bb + }); + if (slice->has_center_as_9_slice) { + CF_V2 min = cf_v2((float)slice->center_x, (float)slice->center_y); + CF_V2 max = cf_v2((float)slice->center_x + slice->center_w, (float)slice->center_y + slice->center_h); + CF_Aabb center_patch = cf_make_aabb(min, max); + for (int frame_number = slice->frame_number; frame_number < new_count; ++frame_number) { + center_patches[frame_number] = center_patch; + } + } + if (slice->has_pivot) { + v2 pivot = V2((float)slice->pivot_x, (float)slice->pivot_y); + pivot.x = pivot.x - sw * 0.5f + 0.5f; + pivot.y = pivot.y - sh * 0.5f + 0.5f; + for (int frame_number = slice->frame_number; frame_number < new_count; ++frame_number) { + pivots[frame_number] = pivot; + } + } + } + asset->slices = slices; + asset->pivots = pivots; + asset->center_patches = center_patches; + + // Swap in the new ase, free the old one. + cute_aseprite_free(asset->ase); + asset->ase = new_ase; + asset->w = new_ase->w; + asset->h = new_ase->h; +} + +uint64_t cf_aseprite_layer_mask(ase_t* ase, const char** layer_names, int count) +{ + uint64_t mask = 0; + for (int i = 0; i < count; ++i) { + for (int li = 0; li < ase->layer_count; ++li) { + if (strcmp(ase->layers[li].name, layer_names[i]) == 0) { + mask |= (1ULL << li); + } + } + } + return mask; +} + +int cf_aseprite_cache_add_blend(const char* path, uint64_t layer_mask) +{ + path = sintern(path); + uint64_t* asset_id_ptr = g_ase_cache->path_to_asset_id.try_find(path); + CF_ASSERT(asset_id_ptr); + CF_SpriteAsset* asset = g_ase_cache->assets + *asset_id_ptr; + ase_t* ase = asset->ase; + CF_ASSERT(ase); + + int frame_count = ase->frame_count; + int pixel_bytes = ase->w * ase->h * (int)sizeof(ase_color_t); + + // Allocate pixel buffers and blend layers. + ase_color_t** pixel_bufs = (ase_color_t**)CF_ALLOC(sizeof(ase_color_t*) * frame_count); + for (int i = 0; i < frame_count; ++i) { + pixel_bufs[i] = (ase_color_t*)CF_ALLOC(pixel_bytes); + CF_MEMSET(pixel_bufs[i], 0, pixel_bytes); + } + cute_aseprite_blend_layers(ase, layer_mask, pixel_bufs); + + // Generate IDs, premultiply alpha, register pixels. + dyna uint64_t* blend_ids = NULL; + for (int i = 0; i < frame_count; ++i) { + // Premultiply alpha. + ase_color_t* pix = pixel_bufs[i]; + for (int p = 0; p < ase->w * ase->h; ++p) { + float a = pix[p].a / 255.0f; + pix[p].r = (uint8_t)(pix[p].r / 255.0f * a * 255.0f); + pix[p].g = (uint8_t)(pix[p].g / 255.0f * a * 255.0f); + pix[p].b = (uint8_t)(pix[p].b / 255.0f * a * 255.0f); + } + + uint64_t id = g_ase_cache->id_gen++; + g_ase_cache->id_to_pixels.insert(id, pixel_bufs[i]); + apush(blend_ids, id); + apush(asset->owned_pixel_buffers, (void*)pixel_bufs[i]); + } + CF_FREE(pixel_bufs); + + apush(asset->blend_frame_ids, blend_ids); + int blend_index = asset->blend_count++; + return blend_index; +} + +uint64_t cf_register_external_sprite_asset(const char* name, int w, int h, CF_MAP(CF_Animation*) animations) +{ + name = sintern(name); + uint64_t* existing = g_ase_cache->path_to_asset_id.try_find(name); + if (existing) return *existing; + + uint64_t asset_id = (uint64_t)asize(g_ase_cache->assets); + CF_SpriteAsset asset; + CF_MEMSET(&asset, 0, sizeof(asset)); + asset.path = name; + asset.ase = NULL; // External: caller owns animations. + asset.w = w; + asset.h = h; + CF_MEMCPY(&asset.animations, &animations, sizeof(animations)); + apush(g_ase_cache->assets, asset); + g_ase_cache->path_to_asset_id.insert(name, asset_id); + return asset_id; } diff --git a/src/cute_audio.cpp b/src/cute_audio.cpp index a53ec3496..06aa4f9be 100644 --- a/src/cute_audio.cpp +++ b/src/cute_audio.cpp @@ -144,14 +144,14 @@ void cf_music_crossfade(CF_Audio audio_source, float cross_fade_time) return cs_music_crossfade((cs_audio_source_t*)audio_source.id, cross_fade_time); } -int cf_music_get_sample_index() +double cf_music_get_time() { - return cs_music_get_sample_index(); + return cs_music_get_time(); } -CF_Result cf_music_set_sample_index(int sample_index) +CF_Result cf_music_set_time(double time_in_seconds) { - return s_result(cs_music_set_sample_index(sample_index)); + return s_result(cs_music_set_time(time_in_seconds)); } void cf_music_set_pitch(float pitch) @@ -169,7 +169,7 @@ CF_Sound cf_play_sound(CF_Audio audio_source, CF_SoundParams params) csparams.volume = params.volume; csparams.pan = params.pan; csparams.pitch = params.pitch; - csparams.sample_index = params.sample_index; + csparams.start_time = params.start_time; CF_Sound result; cs_playing_sound_t csresult = cs_play_sound((cs_audio_source_t*)audio_source.id, csparams); result.id = csresult.id; @@ -240,10 +240,10 @@ float cf_sound_get_pitch(CF_Sound sound) return cs_sound_get_pitch(cssound); } -int cf_sound_get_sample_index(CF_Sound sound) +double cf_sound_get_time(CF_Sound sound) { cs_playing_sound_t cssound = { sound.id }; - return cs_sound_get_sample_index(cssound); + return cs_sound_get_time(cssound); } void cf_sound_set_is_paused(CF_Sound sound, bool true_for_paused) @@ -264,10 +264,10 @@ void cf_sound_set_volume(CF_Sound sound, float volume) cs_sound_set_volume(cssound, volume); } -void cf_sound_set_sample_index(CF_Sound sound, int sample_index) +CF_Result cf_sound_set_time(CF_Sound sound, double time_in_seconds) { cs_playing_sound_t cssound = { sound.id }; - cs_sound_set_sample_index(cssound, sample_index); + return s_result(cs_sound_set_time(cssound, time_in_seconds)); } void cf_sound_stop(CF_Sound sound) @@ -282,11 +282,6 @@ void cf_sound_set_pitch(CF_Sound sound, float pitch) cs_sound_set_pitch(cssound, pitch); } -void cf_audio_cull_duplicates(bool true_to_cull_duplicates) -{ - cs_cull_duplicates(true_to_cull_duplicates); -} - int cf_audio_sample_rate(CF_Audio audio) { cs_audio_source_t* src = (cs_audio_source_t*)audio.id; diff --git a/src/cute_binding.cpp b/src/cute_binding.cpp new file mode 100644 index 000000000..cc03e0876 --- /dev/null +++ b/src/cute_binding.cpp @@ -0,0 +1,870 @@ +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +#include +#include +#include +#include + +#include + +#include + +// Original implementation and API design by Noel Berry. + +//-------------------------------------------------------------------------------------------------- +// Internal types. + +static float s_binding_deadzone = 0.15f; +#define CF_BINDING_MAX_JOYPADS 8 + +enum CF_InputType +{ + CF_INPUT_TYPE_KEY, + CF_INPUT_TYPE_MOUSE, + CF_INPUT_TYPE_BUTTON, + CF_INPUT_TYPE_TRIGGER, +}; + +struct CF_InputBinding +{ + CF_InputType type; + bool positive; + float threshold; + union { + int key; + int button; + int axis; + }; +}; + +struct CF_ButtonBindingInternal +{ + int index; + float press_buffer; + dyna CF_InputBinding* inputs = NULL; + float last_timestep = 0; + float last_press_time = -1; + float last_release_time = -1; + bool is_down = false; + float v = 0; + float v_raw = 0; + bool pressed = false; + bool released = false; + bool press_consumed = false; + bool release_consumed = false; + float deadzone = -1; + float repeat_delay = -1; + float repeat_interval = 0; + float next_repeat_time = 0; + bool repeated = false; +}; + +struct CF_AxisBindingInternal +{ + int index; + CF_AxisConflict conflict = CF_AXIS_CONFLICT_NEWEST; + CF_ButtonBinding negative; + CF_ButtonBinding positive; +}; + +struct CF_StickBindingInternal +{ + CF_AxisBinding x_axis; + CF_AxisBinding y_axis; + int player_index = 0; + int analog_x_axis = -1; + int analog_y_axis = -1; + float circular_deadzone = -1; +}; + +struct CF_JoypadInfo +{ + int index; + bool connected; + CF_JoypadPowerLevel power_level; + const char* name; + CF_JoypadType type; + uint16_t vendor; + uint16_t product_id; + const char* serial_number; + uint16_t firmware_version; + uint16_t product_version; +}; + +struct CF_JoypadConnectCallback +{ + void* udata; + void (*fn)(int, bool, void*); +}; + +//-------------------------------------------------------------------------------------------------- +// File-scope state. + +static dyna CF_ButtonBindingInternal** s_button_bindings; +static dyna CF_JoypadInfo* s_joypads; +static dyna CF_JoypadConnectCallback* s_on_joypad_connects; +static bool s_binding_initialized; + +//-------------------------------------------------------------------------------------------------- +// Private helpers. + +static float s_int16_to_float_raw(int16_t v) +{ + return (float)v / 32767.0f; +} + +static float s_int16_to_float_dz(int16_t v, float dz) +{ + float normalized_v = (float)v / 32767.0f; + float abs_v = fabsf(normalized_v); + if (abs_v < dz) { + return 0; + } + float sign = normalized_v > 0 ? 1.0f : -1.0f; + float remapped = (abs_v - dz) / (1.0f - dz); + return sign * remapped; +} + +static float s_int16_to_float(int16_t v) +{ + return s_int16_to_float_dz(v, s_binding_deadzone); +} + +static bool s_axis_is_down_raw(CF_InputBinding input, float v) +{ + if ((v > 0 && input.positive) || (v < 0 && !input.positive)) { + if (fabsf(v) >= input.threshold) { + return true; + } + } + return false; +} + +static bool s_axis_is_down(int index, CF_InputBinding input, float dz) +{ + float v = s_int16_to_float_dz(cf_joypad_axis(index, (CF_JoypadAxis)input.axis), dz); + return s_axis_is_down_raw(input, v); +} + +static bool s_axis_prev_is_down(int index, CF_InputBinding input, float dz) +{ + float v = s_int16_to_float_dz(cf_joypad_axis_prev(index, (CF_JoypadAxis)input.axis), dz); + return s_axis_is_down_raw(input, v); +} + +static bool s_input_is_down(int index, const dyna CF_InputBinding* inputs, float dz) +{ + for (int i = 0; i < asize(inputs); ++i) { + CF_InputBinding input = inputs[i]; + if (input.type == CF_INPUT_TYPE_KEY) { + if (cf_key_down((CF_KeyButton)input.key)) return true; + } else if (input.type == CF_INPUT_TYPE_MOUSE) { + if (cf_mouse_down((CF_MouseButton)input.button)) return true; + } else if (input.type == CF_INPUT_TYPE_BUTTON) { + if (cf_joypad_button_down(index, (CF_JoypadButton)input.button)) return true; + } else if (input.type == CF_INPUT_TYPE_TRIGGER) { + if (s_axis_is_down(index, input, dz)) return true; + } + } + return false; +} + +static bool s_input_get_pressed(int index, const dyna CF_InputBinding* inputs, float dz) +{ + for (int i = 0; i < asize(inputs); ++i) { + CF_InputBinding input = inputs[i]; + if (input.type == CF_INPUT_TYPE_KEY) { + if (cf_key_just_pressed((CF_KeyButton)input.key)) return true; + } else if (input.type == CF_INPUT_TYPE_MOUSE) { + if (cf_mouse_just_pressed((CF_MouseButton)input.button)) return true; + } else if (input.type == CF_INPUT_TYPE_BUTTON) { + if (cf_joypad_button_just_pressed(index, (CF_JoypadButton)input.button)) return true; + } else if (input.type == CF_INPUT_TYPE_TRIGGER) { + if (s_axis_is_down(index, input, dz) && !s_axis_prev_is_down(index, input, dz)) return true; + } + } + return false; +} + +static bool s_input_get_released(int index, const dyna CF_InputBinding* inputs, float dz) +{ + for (int i = 0; i < asize(inputs); ++i) { + CF_InputBinding input = inputs[i]; + if (input.type == CF_INPUT_TYPE_KEY) { + if (cf_key_just_released((CF_KeyButton)input.key)) return true; + } else if (input.type == CF_INPUT_TYPE_MOUSE) { + if (cf_mouse_just_released((CF_MouseButton)input.button)) return true; + } else if (input.type == CF_INPUT_TYPE_BUTTON) { + if (cf_joypad_button_just_released(index, (CF_JoypadButton)input.button)) return true; + } else if (input.type == CF_INPUT_TYPE_TRIGGER) { + if (!s_axis_is_down(index, input, dz) && s_axis_prev_is_down(index, input, dz)) return true; + } + } + return false; +} + +static float s_input_get_value(int index, const dyna CF_InputBinding* inputs, float dz) +{ + float highest_value = 0; + for (int i = 0; i < asize(inputs); ++i) { + CF_InputBinding input = inputs[i]; + if (input.type == CF_INPUT_TYPE_KEY) { + if (cf_key_down((CF_KeyButton)input.key)) return 1.0f; + } else if (input.type == CF_INPUT_TYPE_MOUSE) { + if (cf_mouse_down((CF_MouseButton)input.button)) return 1.0f; + } else if (input.type == CF_INPUT_TYPE_BUTTON) { + if (cf_joypad_button_down(index, (CF_JoypadButton)input.button)) return 1.0f; + } else if (input.type == CF_INPUT_TYPE_TRIGGER) { + float v = s_int16_to_float_dz(cf_joypad_axis(index, (CF_JoypadAxis)input.axis), dz); + if (s_axis_is_down_raw(input, v)) { + float abs_v = fabsf(v); + if (abs_v > highest_value) highest_value = abs_v; + } + } + } + return highest_value; +} + +static float s_input_get_value_raw(int index, const dyna CF_InputBinding* inputs) +{ + float highest_value = 0; + for (int i = 0; i < asize(inputs); ++i) { + CF_InputBinding input = inputs[i]; + if (input.type == CF_INPUT_TYPE_KEY) { + if (cf_key_down((CF_KeyButton)input.key)) return 1.0f; + } else if (input.type == CF_INPUT_TYPE_MOUSE) { + if (cf_mouse_down((CF_MouseButton)input.button)) return 1.0f; + } else if (input.type == CF_INPUT_TYPE_BUTTON) { + if (cf_joypad_button_down(index, (CF_JoypadButton)input.button)) return 1.0f; + } else if (input.type == CF_INPUT_TYPE_TRIGGER) { + float v = s_int16_to_float_raw(cf_joypad_axis(index, (CF_JoypadAxis)input.axis)); + float abs_v = fabsf(v); + if (abs_v > highest_value) highest_value = abs_v; + } + } + return highest_value; +} + +static float s_effective_deadzone(CF_ButtonBindingInternal* b) +{ + return b->deadzone >= 0 ? b->deadzone : s_binding_deadzone; +} + +static void s_button_binding_update(CF_ButtonBindingInternal* b) +{ + float dz = s_effective_deadzone(b); + b->press_consumed = false; + b->release_consumed = false; + + if (s_input_get_pressed(b->index, b->inputs, dz)) { + b->last_timestep = (float)CF_SECONDS; + b->last_press_time = (float)CF_SECONDS; + b->pressed = true; + } else { + b->pressed = false; + } + + if (s_input_get_released(b->index, b->inputs, dz)) { + b->last_release_time = (float)CF_SECONDS; + b->released = true; + } else { + b->released = false; + } + + b->is_down = s_input_is_down(b->index, b->inputs, dz); + b->v = s_input_get_value(b->index, b->inputs, dz); + b->v_raw = s_input_get_value_raw(b->index, b->inputs); + + // Key repeat. + b->repeated = false; + if (b->is_down && b->repeat_delay >= 0) { + if (b->pressed) { + b->next_repeat_time = (float)CF_SECONDS + b->repeat_delay; + } else if ((float)CF_SECONDS >= b->next_repeat_time) { + b->repeated = true; + b->next_repeat_time = (float)CF_SECONDS + b->repeat_interval; + } + } +} + +static CF_JoypadInfo s_get_joypad_info(int index) +{ + CF_JoypadInfo joy; + joy.index = index; + joy.connected = cf_joypad_is_connected(index); + joy.power_level = cf_joypad_power_level(index); + joy.name = cf_joypad_name(index); + joy.type = cf_joypad_type(index); + joy.vendor = cf_joypad_vendor(index); + joy.product_id = cf_joypad_product_id(index); + joy.serial_number = cf_joypad_serial_number(index); + joy.firmware_version = cf_joypad_firmware_version(index); + joy.product_version = cf_joypad_product_version(index); + return joy; +} + +static void s_binding_init() +{ + if (s_binding_initialized) return; + s_binding_initialized = true; + for (int i = 0; i < CF_BINDING_MAX_JOYPADS; ++i) { + apush(s_joypads, s_get_joypad_info(i)); + } +} + +//-------------------------------------------------------------------------------------------------- +// ButtonBinding API. + +CF_ButtonBinding cf_make_button_binding(int player_index, float press_buffer) +{ + s_binding_init(); + CF_ButtonBindingInternal* b = CF_NEW(CF_ButtonBindingInternal); + b->index = player_index; + b->press_buffer = press_buffer; + apush(s_button_bindings, b); + CF_ButtonBinding result; + result.id = (uint64_t)b; + return result; +} + +void cf_destroy_button_binding(CF_ButtonBinding handle) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + for (int i = 0; i < asize(s_button_bindings); ++i) { + if (s_button_bindings[i] == b) { + adel(s_button_bindings, i); + break; + } + } + afree(b->inputs); + CF_FREE(b); +} + +void cf_button_binding_add_key(CF_ButtonBinding handle, CF_KeyButton key) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + CF_InputBinding input = {}; + input.type = CF_INPUT_TYPE_KEY; + input.key = (int)key; + apush(b->inputs, input); +} + +void cf_button_binding_add_mouse_button(CF_ButtonBinding handle, CF_MouseButton button) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + CF_InputBinding input = {}; + input.type = CF_INPUT_TYPE_MOUSE; + input.button = (int)button; + apush(b->inputs, input); +} + +void cf_button_binding_add_joypad_button(CF_ButtonBinding handle, CF_JoypadButton button) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + CF_InputBinding input = {}; + input.type = CF_INPUT_TYPE_BUTTON; + input.button = (int)button; + apush(b->inputs, input); +} + +void cf_button_binding_add_trigger(CF_ButtonBinding handle, CF_JoypadAxis axis, float threshold, bool positive) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + CF_InputBinding input = {}; + input.type = CF_INPUT_TYPE_TRIGGER; + input.axis = (int)axis; + input.threshold = threshold; + input.positive = positive; + apush(b->inputs, input); +} + +bool cf_button_binding_pressed(CF_ButtonBinding handle) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + if (b->press_consumed) return false; + if (b->last_press_time >= 0 && ((float)CF_SECONDS - b->last_press_time) <= b->press_buffer) { + return true; + } + return b->pressed; +} + +bool cf_button_binding_released(CF_ButtonBinding handle) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + if (b->release_consumed) return false; + if (b->last_release_time >= 0 && ((float)CF_SECONDS - b->last_release_time) <= b->press_buffer) { + return true; + } + return b->released; +} + +bool cf_button_binding_down(CF_ButtonBinding handle) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + return b->is_down; +} + +float cf_button_binding_value(CF_ButtonBinding handle) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + return b->v; +} + +float cf_button_binding_sign(CF_ButtonBinding handle) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + if (b->v > 0) return 1.0f; + else if (b->v == 0) return 0; + else return -1.0f; +} + +bool cf_button_binding_consume_press(CF_ButtonBinding handle) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + bool had = cf_button_binding_pressed(handle); + b->press_consumed = true; + b->last_press_time = -1; + return had; +} + +bool cf_button_binding_consume_release(CF_ButtonBinding handle) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + bool had = cf_button_binding_released(handle); + b->release_consumed = true; + b->last_release_time = -1; + return had; +} + +void cf_button_binding_set_deadzone(CF_ButtonBinding handle, float deadzone) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + b->deadzone = deadzone; +} + +float cf_button_binding_value_raw(CF_ButtonBinding handle) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + return b->v_raw; +} + +void cf_button_binding_set_repeat(CF_ButtonBinding handle, float delay, float interval) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + b->repeat_delay = delay; + b->repeat_interval = interval; +} + +bool cf_button_binding_repeated(CF_ButtonBinding handle) +{ + CF_ButtonBindingInternal* b = (CF_ButtonBindingInternal*)handle.id; + return b->repeated; +} + +//-------------------------------------------------------------------------------------------------- +// AxisBinding API. + +CF_AxisBinding cf_make_axis_binding(int player_index) +{ + CF_AxisBindingInternal* a = CF_NEW(CF_AxisBindingInternal); + a->index = player_index; + a->negative = cf_make_button_binding(player_index, 0); + a->positive = cf_make_button_binding(player_index, 0); + CF_AxisBinding result; + result.id = (uint64_t)a; + return result; +} + +void cf_destroy_axis_binding(CF_AxisBinding handle) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + cf_destroy_button_binding(a->negative); + cf_destroy_button_binding(a->positive); + CF_FREE(a); +} + +void cf_axis_binding_add_keys(CF_AxisBinding handle, CF_KeyButton negative, CF_KeyButton positive) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + cf_button_binding_add_key(a->negative, negative); + cf_button_binding_add_key(a->positive, positive); +} + +void cf_axis_binding_add_mouse_buttons(CF_AxisBinding handle, CF_MouseButton negative, CF_MouseButton positive) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + cf_button_binding_add_mouse_button(a->negative, negative); + cf_button_binding_add_mouse_button(a->positive, positive); +} + +void cf_axis_binding_add_joypad_buttons(CF_AxisBinding handle, CF_JoypadButton negative, CF_JoypadButton positive) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + cf_button_binding_add_joypad_button(a->negative, negative); + cf_button_binding_add_joypad_button(a->positive, positive); +} + +void cf_axis_binding_add_triggers(CF_AxisBinding handle, CF_JoypadAxis negative, CF_JoypadAxis positive, float threshold) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + cf_button_binding_add_trigger(a->negative, negative, threshold, false); + cf_button_binding_add_trigger(a->positive, positive, threshold, true); +} + +void cf_axis_binding_add_left_stick_x(CF_AxisBinding handle, float threshold) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + cf_button_binding_add_trigger(a->negative, CF_JOYPAD_AXIS_LEFTX, threshold, false); + cf_button_binding_add_trigger(a->positive, CF_JOYPAD_AXIS_LEFTX, threshold, true); +} + +void cf_axis_binding_add_left_stick_y(CF_AxisBinding handle, float threshold) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + cf_button_binding_add_trigger(a->negative, CF_JOYPAD_AXIS_LEFTY, threshold, true); + cf_button_binding_add_trigger(a->positive, CF_JOYPAD_AXIS_LEFTY, threshold, false); +} + +void cf_axis_binding_add_right_stick_x(CF_AxisBinding handle, float threshold) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + cf_button_binding_add_trigger(a->negative, CF_JOYPAD_AXIS_RIGHTX, threshold, false); + cf_button_binding_add_trigger(a->positive, CF_JOYPAD_AXIS_RIGHTX, threshold, true); +} + +void cf_axis_binding_add_right_stick_y(CF_AxisBinding handle, float threshold) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + cf_button_binding_add_trigger(a->negative, CF_JOYPAD_AXIS_RIGHTY, threshold, true); + cf_button_binding_add_trigger(a->positive, CF_JOYPAD_AXIS_RIGHTY, threshold, false); +} + +void cf_axis_binding_set_conflict(CF_AxisBinding handle, CF_AxisConflict mode) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + a->conflict = mode; +} + +bool cf_axis_binding_pressed(CF_AxisBinding handle) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + return cf_button_binding_pressed(a->negative) || cf_button_binding_pressed(a->positive); +} + +bool cf_axis_binding_released(CF_AxisBinding handle) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + return cf_button_binding_released(a->negative) || cf_button_binding_released(a->positive); +} + +float cf_axis_binding_value(CF_AxisBinding handle) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + float negative = cf_button_binding_value(a->negative); + float positive = cf_button_binding_value(a->positive); + + if (negative <= 0 && positive <= 0) return 0; + if (negative > 0 && positive <= 0) return -negative; + if (positive > 0 && negative <= 0) return positive; + + CF_ButtonBindingInternal* neg = (CF_ButtonBindingInternal*)a->negative.id; + CF_ButtonBindingInternal* pos = (CF_ButtonBindingInternal*)a->positive.id; + + if (a->conflict == CF_AXIS_CONFLICT_CANCEL) return 0; + else if (a->conflict == CF_AXIS_CONFLICT_OLDEST) { + if (neg->last_timestep < pos->last_timestep) { + return -negative; + } else { + return positive; + } + } else if (a->conflict == CF_AXIS_CONFLICT_NEWEST) { + if (neg->last_timestep < pos->last_timestep) { + return positive; + } else { + return -negative; + } + } + return 0; +} + +float cf_axis_binding_sign(CF_AxisBinding handle) +{ + float v = cf_axis_binding_value(handle); + if (v > 0) return 1; + if (v < 0) return -1; + return 0; +} + +bool cf_axis_binding_consume_press(CF_AxisBinding handle) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + bool a0 = cf_button_binding_consume_press(a->negative); + bool a1 = cf_button_binding_consume_press(a->positive); + return a0 | a1; +} + +bool cf_axis_binding_consume_release(CF_AxisBinding handle) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + bool a0 = cf_button_binding_consume_release(a->negative); + bool a1 = cf_button_binding_consume_release(a->positive); + return a0 | a1; +} + +void cf_axis_binding_set_deadzone(CF_AxisBinding handle, float deadzone) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + cf_button_binding_set_deadzone(a->negative, deadzone); + cf_button_binding_set_deadzone(a->positive, deadzone); +} + +float cf_axis_binding_value_raw(CF_AxisBinding handle) +{ + CF_AxisBindingInternal* a = (CF_AxisBindingInternal*)handle.id; + float negative = cf_button_binding_value_raw(a->negative); + float positive = cf_button_binding_value_raw(a->positive); + if (negative <= 0 && positive <= 0) return 0; + if (negative > 0 && positive <= 0) return -negative; + if (positive > 0 && negative <= 0) return positive; + return 0; +} + +//-------------------------------------------------------------------------------------------------- +// StickBinding API. + +CF_StickBinding cf_make_stick_binding(int player_index) +{ + CF_StickBindingInternal* s = CF_NEW(CF_StickBindingInternal); + s->x_axis = cf_make_axis_binding(player_index); + s->y_axis = cf_make_axis_binding(player_index); + s->player_index = player_index; + CF_StickBinding result; + result.id = (uint64_t)s; + return result; +} + +void cf_destroy_stick_binding(CF_StickBinding handle) +{ + CF_StickBindingInternal* s = (CF_StickBindingInternal*)handle.id; + cf_destroy_axis_binding(s->x_axis); + cf_destroy_axis_binding(s->y_axis); + CF_FREE(s); +} + +void cf_stick_binding_add_keys(CF_StickBinding handle, CF_KeyButton up, CF_KeyButton down, CF_KeyButton left, CF_KeyButton right) +{ + CF_StickBindingInternal* s = (CF_StickBindingInternal*)handle.id; + cf_axis_binding_add_keys(s->x_axis, left, right); + cf_axis_binding_add_keys(s->y_axis, up, down); +} + +void cf_stick_binding_add_wasd(CF_StickBinding handle) +{ + cf_stick_binding_add_keys(handle, CF_KEY_W, CF_KEY_S, CF_KEY_A, CF_KEY_D); +} + +void cf_stick_binding_add_arrow_keys(CF_StickBinding handle) +{ + cf_stick_binding_add_keys(handle, CF_KEY_UP, CF_KEY_DOWN, CF_KEY_LEFT, CF_KEY_RIGHT); +} + +void cf_stick_binding_add_dpad(CF_StickBinding handle) +{ + CF_StickBindingInternal* s = (CF_StickBindingInternal*)handle.id; + cf_axis_binding_add_joypad_buttons(s->x_axis, CF_JOYPAD_BUTTON_DPAD_LEFT, CF_JOYPAD_BUTTON_DPAD_RIGHT); + cf_axis_binding_add_joypad_buttons(s->y_axis, CF_JOYPAD_BUTTON_DPAD_DOWN, CF_JOYPAD_BUTTON_DPAD_UP); +} + +void cf_stick_binding_add_left_stick(CF_StickBinding handle, float threshold) +{ + CF_StickBindingInternal* s = (CF_StickBindingInternal*)handle.id; + cf_axis_binding_add_left_stick_x(s->x_axis, threshold); + cf_axis_binding_add_left_stick_y(s->y_axis, threshold); + s->analog_x_axis = CF_JOYPAD_AXIS_LEFTX; + s->analog_y_axis = CF_JOYPAD_AXIS_LEFTY; +} + +void cf_stick_binding_add_right_stick(CF_StickBinding handle, float threshold) +{ + CF_StickBindingInternal* s = (CF_StickBindingInternal*)handle.id; + cf_axis_binding_add_right_stick_x(s->x_axis, threshold); + cf_axis_binding_add_right_stick_y(s->y_axis, threshold); + s->analog_x_axis = CF_JOYPAD_AXIS_RIGHTX; + s->analog_y_axis = CF_JOYPAD_AXIS_RIGHTY; +} + +bool cf_stick_binding_pressed(CF_StickBinding handle) +{ + CF_StickBindingInternal* s = (CF_StickBindingInternal*)handle.id; + return cf_axis_binding_pressed(s->x_axis) || cf_axis_binding_pressed(s->y_axis); +} + +bool cf_stick_binding_released(CF_StickBinding handle) +{ + CF_StickBindingInternal* s = (CF_StickBindingInternal*)handle.id; + return cf_axis_binding_released(s->x_axis) || cf_axis_binding_released(s->y_axis); +} + +CF_V2 cf_stick_binding_value(CF_StickBinding handle) +{ + CF_StickBindingInternal* s = (CF_StickBindingInternal*)handle.id; + + if (s->circular_deadzone >= 0 && s->analog_x_axis >= 0 && s->analog_y_axis >= 0) { + // Check if any digital input is active (keys, dpad, buttons) -- if so, use the standard axis path. + CF_AxisBindingInternal* ax = (CF_AxisBindingInternal*)s->x_axis.id; + CF_AxisBindingInternal* ay = (CF_AxisBindingInternal*)s->y_axis.id; + CF_ButtonBindingInternal* xn = (CF_ButtonBindingInternal*)ax->negative.id; + CF_ButtonBindingInternal* xp = (CF_ButtonBindingInternal*)ax->positive.id; + CF_ButtonBindingInternal* yn = (CF_ButtonBindingInternal*)ay->negative.id; + CF_ButtonBindingInternal* yp = (CF_ButtonBindingInternal*)ay->positive.id; + + // Check for digital input on any of the four button bindings. + bool has_digital = false; + CF_ButtonBindingInternal* btns[4] = { xn, xp, yn, yp }; + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < asize(btns[i]->inputs); ++j) { + CF_InputBinding input = btns[i]->inputs[j]; + if (input.type == CF_INPUT_TYPE_KEY && cf_key_down((CF_KeyButton)input.key)) { has_digital = true; break; } + if (input.type == CF_INPUT_TYPE_MOUSE && cf_mouse_down((CF_MouseButton)input.button)) { has_digital = true; break; } + if (input.type == CF_INPUT_TYPE_BUTTON && cf_joypad_button_down(btns[i]->index, (CF_JoypadButton)input.button)) { has_digital = true; break; } + } + if (has_digital) break; + } + + if (has_digital) { + // Fall back to standard per-axis values (digital override). + CF_V2 result; + result.x = cf_axis_binding_value(s->x_axis); + result.y = cf_axis_binding_value(s->y_axis); + return result; + } + + // Circular deadzone: get raw normalized values directly from joypad. + float rx = s_int16_to_float_raw(cf_joypad_axis(s->player_index, (CF_JoypadAxis)s->analog_x_axis)); + float ry = s_int16_to_float_raw(cf_joypad_axis(s->player_index, (CF_JoypadAxis)s->analog_y_axis)); + float mag = sqrtf(rx * rx + ry * ry); + float dz = s->circular_deadzone; + if (mag < dz) { + CF_V2 result = { 0, 0 }; + return result; + } + float remapped_mag = (mag - dz) / (1.0f - dz); + float scale = remapped_mag / mag; + CF_V2 result; + result.x = rx * scale; + result.y = ry * scale; + return result; + } + + CF_V2 result; + result.x = cf_axis_binding_value(s->x_axis); + result.y = cf_axis_binding_value(s->y_axis); + return result; +} + +CF_V2 cf_stick_binding_sign(CF_StickBinding handle) +{ + CF_V2 value = cf_stick_binding_value(handle); + CF_V2 result; + result.x = (value.x == 0) ? 0 : (value.x > 0 ? 1.0f : -1.0f); + result.y = (value.y == 0) ? 0 : (value.y > 0 ? 1.0f : -1.0f); + return result; +} + +bool cf_stick_binding_consume_press(CF_StickBinding handle) +{ + CF_StickBindingInternal* s = (CF_StickBindingInternal*)handle.id; + bool a0 = cf_axis_binding_consume_press(s->x_axis); + bool a1 = cf_axis_binding_consume_press(s->y_axis); + return a0 | a1; +} + +bool cf_stick_binding_consume_release(CF_StickBinding handle) +{ + CF_StickBindingInternal* s = (CF_StickBindingInternal*)handle.id; + bool a0 = cf_axis_binding_consume_release(s->x_axis); + bool a1 = cf_axis_binding_consume_release(s->y_axis); + return a0 | a1; +} + +void cf_stick_binding_set_circular_deadzone(CF_StickBinding handle, float deadzone) +{ + CF_StickBindingInternal* s = (CF_StickBindingInternal*)handle.id; + s->circular_deadzone = deadzone; + // Set both underlying axis deadzones to 0 so analog values pass through raw-normalized. + cf_axis_binding_set_deadzone(s->x_axis, 0); + cf_axis_binding_set_deadzone(s->y_axis, 0); +} + +CF_V2 cf_stick_binding_value_raw(CF_StickBinding handle) +{ + CF_StickBindingInternal* s = (CF_StickBindingInternal*)handle.id; + CF_V2 result; + result.x = cf_axis_binding_value_raw(s->x_axis); + result.y = cf_axis_binding_value_raw(s->y_axis); + return result; +} + +//-------------------------------------------------------------------------------------------------- +// Joypad connection tracking. + +void cf_register_joypad_connect_callback(void (*fn)(int player_index, bool connected, void* udata), void* udata) +{ + s_binding_init(); + CF_JoypadConnectCallback callback; + callback.fn = fn; + callback.udata = udata; + apush(s_on_joypad_connects, callback); +} + +//-------------------------------------------------------------------------------------------------- +// Deadzone. + +float cf_binding_get_deadzone() +{ + return s_binding_deadzone; +} + +void cf_binding_set_deadzone(float deadzone) +{ + s_binding_deadzone = deadzone; +} + +//-------------------------------------------------------------------------------------------------- +// Per-frame update (called from cf_app_update). + +void cf_binding_update() +{ + if (!s_binding_initialized) return; + + // Handle joypad connect/disconnect. + for (int i = 0; i < asize(s_joypads); ++i) { + CF_JoypadInfo* joypad = s_joypads + i; + CF_JoypadInfo refresh = s_get_joypad_info(i); + if (refresh.connected) { + if (!joypad->connected) { + *joypad = refresh; + for (int j = 0; j < asize(s_on_joypad_connects); ++j) { + s_on_joypad_connects[j].fn(i, true, s_on_joypad_connects[j].udata); + } + } + } else { + if (joypad->connected) { + for (int j = 0; j < asize(s_on_joypad_connects); ++j) { + s_on_joypad_connects[j].fn(i, false, s_on_joypad_connects[j].udata); + } + joypad->connected = false; + } + } + } + + // Update all button bindings. + for (int i = 0; i < asize(s_button_bindings); ++i) { + s_button_binding_update(s_button_bindings[i]); + } +} diff --git a/src/cute_ckit.cpp b/src/cute_ckit.cpp new file mode 100644 index 000000000..33d0adf7f --- /dev/null +++ b/src/cute_ckit.cpp @@ -0,0 +1,11 @@ +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +#include + +#define CKIT_IMPLEMENTATION +#include diff --git a/src/cute_clipboard.cpp b/src/cute_clipboard.cpp index be47595d8..772406b07 100644 --- a/src/cute_clipboard.cpp +++ b/src/cute_clipboard.cpp @@ -6,6 +6,8 @@ */ #include +#include +#include #include char* cf_clipboard_get() @@ -20,3 +22,50 @@ CF_Result cf_clipboard_set(const char* string) if (ret) return cf_result_error("Unable to set clipboard data."); else return cf_result_success(); } + +static void* s_clipboard_data = NULL; +static int s_clipboard_data_size = 0; + +static const void* s_clipboard_data_callback(void* userdata, const char* mime_type, size_t* size) +{ + *size = (size_t)s_clipboard_data_size; + return s_clipboard_data; +} + +static void s_clipboard_cleanup_callback(void* userdata) +{ + cf_free(s_clipboard_data); + s_clipboard_data = NULL; + s_clipboard_data_size = 0; +} + +CF_Result cf_clipboard_set_data(const void* data, int size, const char** mime_types, int num_mime_types) +{ + cf_free(s_clipboard_data); + s_clipboard_data = cf_alloc(size); + CF_MEMCPY(s_clipboard_data, data, size); + s_clipboard_data_size = size; + bool ok = SDL_SetClipboardData(s_clipboard_data_callback, s_clipboard_cleanup_callback, NULL, mime_types, (size_t)num_mime_types); + if (!ok) return cf_result_error("Unable to set clipboard data."); + return cf_result_success(); +} + +void* cf_clipboard_get_data(const char* mime_type, int* size) +{ + size_t sdl_size = 0; + void* sdl_data = SDL_GetClipboardData(mime_type, &sdl_size); + if (!sdl_data || sdl_size == 0) { + if (size) *size = 0; + return NULL; + } + void* result = cf_alloc((size_t)sdl_size); + CF_MEMCPY(result, sdl_data, sdl_size); + SDL_free(sdl_data); + if (size) *size = (int)sdl_size; + return result; +} + +bool cf_clipboard_has_data(const char* mime_type) +{ + return SDL_HasClipboardData(mime_type); +} diff --git a/src/cute_coroutine.cpp b/src/cute_coroutine.cpp index 4ec7b1f27..d0a16c26e 100644 --- a/src/cute_coroutine.cpp +++ b/src/cute_coroutine.cpp @@ -49,6 +49,7 @@ CF_Coroutine cf_make_coroutine(CF_CoroutineFn* fn, int stack_size, void* udata) void cf_destroy_coroutine(CF_Coroutine co_handle) { CF_CoroutineInternal* co = (CF_CoroutineInternal*)co_handle.id; + if (!co) return; mco_state state = mco_status(co->mco); CF_ASSERT(state == MCO_DEAD || state == MCO_SUSPENDED); mco_result res = mco_destroy(co->mco); diff --git a/src/cute_custom_sprite.cpp b/src/cute_custom_sprite.cpp new file mode 100644 index 000000000..bf60565b6 --- /dev/null +++ b/src/cute_custom_sprite.cpp @@ -0,0 +1,223 @@ +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +struct CF_CustomSpriteCache +{ + CF_MAP(void*) id_to_pixels = NULL; + dyna CF_Animation** animations_list = NULL; + CF_MAP(CF_AnimationTable) animation_tables = NULL; + CF_MAP(CF_Png) pngs = NULL; + CF_MAP(CF_Animation*) animations = NULL; + uint64_t id_gen = CF_CUSTOM_SPRITE_ID_RANGE_LO; +}; + +CF_GLOBAL static CF_CustomSpriteCache* cache; + +void cf_custom_sprite_get_pixels(uint64_t image_id, void* buffer, int bytes_to_fill) +{ + void** pixels_ptr = map_get_ptr(cache->id_to_pixels, image_id); + if (!pixels_ptr) { + CF_DEBUG_PRINTF("custom sprite cache -- unable to find id %lld.", (long long int)image_id); + CF_MEMSET(buffer, 0, bytes_to_fill); + } else { + CF_MEMCPY(buffer, *pixels_ptr, bytes_to_fill); + } +} + +CF_API void cf_make_custom_sprite_cache() +{ + cache = CF_NEW(CF_CustomSpriteCache); +} + +CF_API void cf_destroy_custom_sprite_cache() +{ + for (int i = 0; i < asize(cache->animations_list); ++i) { + afree(cache->animations_list[i]->frames); + CF_FREE(cache->animations_list[i]); + } + afree(cache->animations_list); + + for (int i = 0; i < map_size(cache->animation_tables); ++i) { + CF_AnimationTable inner = map_items(cache->animation_tables)[i]; + map_free(inner); + } + map_free(cache->animation_tables); + + int i = 0; + while (map_size(cache->pngs)) { + CF_Png png = map_items(cache->pngs)[i]; + cf_custom_sprite_unload_png(png); + } + map_free(cache->pngs); + map_free(cache->id_to_pixels); + map_free(cache->animations); + + CF_FREE(cache); + cache = NULL; +} + +CF_Result cf_custom_sprite_load_png(const char* png_path, CF_Png* png) +{ + CF_Image img; + CF_Result err = cf_image_load_png(png_path, &img); + if (cf_is_error(err)) return err; + cf_image_premultiply(&img); + CF_Png entry; + entry.path = sintern(png_path); + entry.id = cache->id_gen++; + entry.pix = img.pix; + entry.w = img.w; + entry.h = img.h; + map_set(cache->id_to_pixels, entry.id, img.pix); + map_set(cache->pngs, entry.id, entry); + if (png) *png = entry; + return cf_result_success(); +} + +CF_Result cf_custom_sprite_load_png_from_memory(const char* png_path, const void* memory, size_t size, CF_Png* png) +{ + CF_Image img; + CF_Result err = cf_image_load_png_from_memory(memory, (int)size, &img); + if (cf_is_error(err)) return err; + CF_Png entry; + entry.path = sintern(png_path); + entry.id = cache->id_gen++; + entry.pix = img.pix; + entry.w = img.w; + entry.h = img.h; + map_set(cache->id_to_pixels, entry.id, img.pix); + map_set(cache->pngs, entry.id, entry); + if (png) *png = entry; + return cf_result_success(); +} + +void cf_custom_sprite_unload_png(CF_Png png) +{ + CF_Image img; + img.pix = png.pix; + img.w = png.w; + img.h = png.h; + cf_image_free(&img); + map_del(cache->id_to_pixels, png.id); + map_del(cache->pngs, png.id); +} + +const CF_Animation* cf_make_custom_sprite_animation(const char* name, const CF_Png* pngs, int pngs_count, const float* delays, int delays_count) +{ + CF_ASSERT(pngs_count == delays_count); + CF_ASSERT(pngs_count > 0); + for (int i = 1; i < pngs_count; ++i) { + CF_ASSERT(pngs[i].w == pngs[0].w && pngs[i].h == pngs[0].h); + } + name = sintern(name); + + // If already made, just return the old animation. + if (map_size(cache->animations)) { + CF_Animation** ptr = map_get_ptr(cache->animations, (uint64_t)name); + if (ptr) return *ptr; + } + + // Otherwise allocate a new animation. + CF_Animation* animation = (CF_Animation*)CF_ALLOC(sizeof(CF_Animation)); + CF_MEMSET(animation, 0, sizeof(CF_Animation)); + animation->name = name; + map_set(cache->animations, (uint64_t)name, animation); + apush(cache->animations_list, animation); + + for (int i = 0; i < pngs_count; ++i) { + CF_Frame frame; + frame.id = pngs[i].id; + frame.delay = delays[i]; + cf_animation_add_frame(animation, frame); + } + + return animation; +} + +const CF_Animation* cf_custom_sprite_get_animation(const char* name) +{ + CF_Animation** ptr = map_get_ptr(cache->animations, (uint64_t)sintern(name)); + return ptr ? *ptr : NULL; +} + +const CF_AnimationTable* cf_make_custom_sprite_animation_table(const char* sprite_name, const CF_Animation* const* animations, int animations_count) +{ + sprite_name = sintern(sprite_name); + + // If already made, just return the old table. + if (map_size(cache->animation_tables)) { + CF_AnimationTable* table = map_get_ptr(cache->animation_tables, (uint64_t)sprite_name); + if (table && map_size(*table)) return table; + } + + // Otherwise allocate a new table entry. + CF_AnimationTable new_table = NULL; + for (int i = 0; i < animations_count; ++i) { + map_set(new_table, (uint64_t)animations[i]->name, animations[i]); + } + map_set(cache->animation_tables, (uint64_t)sprite_name, new_table); + + return map_get_ptr(cache->animation_tables, (uint64_t)sprite_name); +} + +const CF_AnimationTable* cf_custom_sprite_get_animation_table(const char* sprite_name) +{ + return map_get_ptr(cache->animation_tables, (uint64_t)sintern(sprite_name)); +} + +CF_Sprite cf_make_custom_sprite(const char* sprite_name, const CF_AnimationTable* table) +{ + sprite_name = sintern(sprite_name); + CF_Sprite sprite = cf_sprite_defaults(); + sprite.name = sprite_name; + + if (table) { + const CF_Animation** first_anim = (const CF_Animation**)map_items(*table); + CF_Png* png_ptr = map_get_ptr(cache->pngs, (*first_anim)->frames[0].id); + CF_ASSERT(png_ptr && png_ptr->path); + sprite.w = png_ptr->w; + sprite.h = png_ptr->h; + sprite.id = cf_register_external_sprite_asset(sprite_name, sprite.w, sprite.h, (CF_MAP(CF_Animation*))*table); + cf_sprite_play(&sprite, (*first_anim)->name); + } else { + CF_Png entry; + CF_Result result = cf_custom_sprite_load_png(sprite_name, &entry); + if (cf_is_error(result)) { + return cf_sprite_defaults(); + } else { + sprite.w = entry.w; + sprite.h = entry.h; + sprite.easy_sprite_id = entry.id; + } + } + + return sprite; +} + +namespace Cute +{ + +const CF_Animation* make_custom_sprite_animation(const char* name, const Array& pngs, const Array& delays) +{ + return cf_make_custom_sprite_animation(name, pngs.data(), pngs.count(), delays.data(), delays.count()); +} +const CF_AnimationTable* make_custom_sprite_animation_table(const char* sprite_name, const Array& animations) +{ + return cf_make_custom_sprite_animation_table(sprite_name, animations.data(), animations.count()); +} + +} diff --git a/src/cute_draw.cpp b/src/cute_draw.cpp index d9a228e8c..e4a713afa 100644 --- a/src/cute_draw.cpp +++ b/src/cute_draw.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include #include @@ -50,13 +50,13 @@ static const char* s_text_without_markups = NULL; using namespace Cute; -#define VA_TYPE_SPRITE (0) -#define VA_TYPE_TEXT (1) -#define VA_TYPE_BOX (2) -#define VA_TYPE_SEGMENT (3) -#define VA_TYPE_TRIANGLE (4) -#define VA_TYPE_TRIANGLE_SDF (5) -#define VA_TYPE_POLYGON (6) +#define VA_TYPE_SPRITE (0.0f) +#define VA_TYPE_TEXT (1.0f) +#define VA_TYPE_BOX (2.0f) +#define VA_TYPE_SEGMENT (3.0f) +#define VA_TYPE_TRIANGLE (4.0f) +#define VA_TYPE_TRIANGLE_SDF (5.0f) +#define VA_TYPE_POLYGON (6.0f) SPRITEBATCH_U64 cf_generate_texture_handle(void* pixels, int w, int h, void* udata) { @@ -86,8 +86,8 @@ void cf_get_pixels(SPRITEBATCH_U64 image_id, void* buffer, int bytes_to_fill, vo CF_UNUSED(udata); if (image_id >= CF_ASEPRITE_ID_RANGE_LO && image_id <= CF_ASEPRITE_ID_RANGE_HI) { cf_aseprite_cache_get_pixels(image_id, buffer, bytes_to_fill); - } else if (image_id >= CF_PNG_ID_RANGE_LO && image_id <= CF_PNG_ID_RANGE_HI) { - cf_png_cache_get_pixels(image_id, buffer, bytes_to_fill); + } else if (image_id >= CF_CUSTOM_SPRITE_ID_RANGE_LO && image_id <= CF_CUSTOM_SPRITE_ID_RANGE_HI) { + cf_custom_sprite_get_pixels(image_id, buffer, bytes_to_fill); } else if (image_id >= CF_FONT_ID_RANGE_LO && image_id <= CF_FONT_ID_RANGE_HI) { CF_Pixel** pixels = app->font_pixels.try_get(image_id); if (pixels) { @@ -155,20 +155,20 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ case BATCH_GEOMETRY_TYPE_TRI: { for (int i = 0; i < 3; ++i) { - out[i].color = s->geom.color; + out[i].color = s->geom.use_tri_colors ? s->geom.tri_colors[i] : s->geom.color; out[i].radius = 0; out[i].stroke = 0; out[i].type = VA_TYPE_TRIANGLE; - out[i].alpha = (uint8_t)(s->geom.alpha * 255.0f); - out[i].fill = 255; + out[i].alpha = s->geom.alpha; + out[i].fill = 1.0f; out[i].aa = 0; - out[i].attributes = geom.user_params; + out[i].attributes = s->geom.use_tri_attributes ? s->geom.tri_attributes[i] : geom.user_params; } out[0].posH = geom.shape[0]; out[1].posH = geom.shape[1]; out[2].posH = geom.shape[2]; - + vert_count += 3; } break; @@ -185,8 +185,8 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ out[i].stroke = s->geom.stroke; out[i].aa = s->geom.aa; out[i].type = VA_TYPE_TRIANGLE_SDF; - out[i].alpha = (uint8_t)(s->geom.alpha * 255.0f); - out[i].fill = s->geom.fill ? 255 : 0; + out[i].alpha = s->geom.alpha; + out[i].fill = s->geom.fill ? 1.0f : 0.0f; out[i].attributes = geom.user_params; } @@ -204,8 +204,8 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ out[i].stroke = s->geom.stroke; out[i].aa = s->geom.aa; out[i].type = VA_TYPE_BOX; - out[i].alpha = (uint8_t)(s->geom.alpha * 255.0f); - out[i].fill = s->geom.fill ? 255 : 0; + out[i].alpha = s->geom.alpha; + out[i].fill = s->geom.fill ? 1.0f : 0.0f; out[i].attributes = geom.user_params; } @@ -229,7 +229,7 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ case BATCH_GEOMETRY_TYPE_SPRITE: { for (int i = 0; i < 6; ++i) { - out[i].alpha = (uint8_t)(s->geom.alpha * 255.0f); + out[i].alpha = s->geom.alpha; if (s->geom.is_sprite) { out[i].type = VA_TYPE_SPRITE; } else if (s->geom.is_text) { @@ -239,6 +239,10 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ } out[i].color = s->geom.color; out[i].attributes = geom.user_params; + out[i].uv_bounds[0] = s->minx; + out[i].uv_bounds[1] = s->maxy; // y-flip. + out[i].uv_bounds[2] = s->maxx; + out[i].uv_bounds[3] = s->miny; // y-flip. } out[0].posH = geom.shape[0]; @@ -282,8 +286,8 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ out[i].stroke = s->geom.stroke; out[i].aa = s->geom.aa; out[i].type = VA_TYPE_SEGMENT; - out[i].alpha = (uint8_t)(s->geom.alpha * 255.0f); - out[i].fill = s->geom.fill ? 255 : 0; + out[i].alpha = s->geom.alpha; + out[i].fill = s->geom.fill ? 1.0f : 0.0f; out[i].attributes = geom.user_params; } @@ -301,8 +305,8 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ out[i].stroke = s->geom.stroke; out[i].aa = s->geom.aa; out[i].type = VA_TYPE_SEGMENT; - out[i].alpha = (uint8_t)(s->geom.alpha * 255.0f); - out[i].fill = s->geom.fill ? 255 : 0; + out[i].alpha = s->geom.alpha; + out[i].fill = s->geom.fill ? 1.0f : 0.0f; out[i].attributes = geom.user_params; } @@ -330,8 +334,8 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ out[i].radius = s->geom.radius; out[i].aa = s->geom.aa; out[i].type = VA_TYPE_POLYGON; - out[i].alpha = (uint8_t)(s->geom.alpha * 255.0f); - out[i].fill = 255; + out[i].alpha = s->geom.alpha; + out[i].fill = 1.0f; out[i].attributes = geom.user_params; } @@ -360,11 +364,19 @@ static void s_draw_report(spritebatch_sprite_t* sprites, int count, int texture_ cf_material_set_uniform_fs(s_draw->material, "u_texture_size", &u_texture_size, CF_UNIFORM_TYPE_FLOAT2, 1); v2 u_texel_size = cf_v2(1.0f / (float)texture_w, 1.0f / (float)texture_h); cf_material_set_uniform_fs(s_draw->material, "u_texel_size", &u_texel_size, CF_UNIFORM_TYPE_FLOAT2, 1); - cf_material_set_uniform_fs(s_draw->material, "u_alpha_discard", &cmd.alpha_discard, CF_UNIFORM_TYPE_INT, 1); + int alpha_discard = cmd.alpha_discard == 0.0f ? 0 : 1; + cf_material_set_uniform_fs(s_draw->material, "u_alpha_discard", &alpha_discard, CF_UNIFORM_TYPE_INT, 1); + // u_use_smooth_uv: 0 = apply shader smooth_uv function, 1 = use plain v_uv (hardware filtering only) + int use_smooth_uv = cmd.filter_mode == CF_DRAW_FILTER_SMOOTH ? 0 : 1; + cf_material_set_uniform_fs(s_draw->material, "u_use_smooth_uv", &use_smooth_uv, CF_UNIFORM_TYPE_INT, 1); // Apply render state. cf_material_set_render_state(s_draw->material, cmd.render_state); + // Set sampler filter based on filter mode. + void* sampler_override = (cmd.filter_mode == CF_DRAW_FILTER_NEAREST) ? s_draw->sampler_nearest : s_draw->sampler_linear; + cf_set_sampler_override(sampler_override); + // Kick off a draw call. cf_apply_shader(cmd.shader, s_draw->material); @@ -416,7 +428,6 @@ void CF_Draw::reset_cam() cam_stack.clear(); cam_stack.add(cf_make_identity()); mvp = projection; - s_draw->antialias_scale.set_count(1); s_draw->set_aaf(); } @@ -424,10 +435,9 @@ void CF_Draw::reset_cam() // This factor remains constant-size despite zooming in/out with the camera. void CF_Draw::set_aaf() { - float on_or_off = s_draw->antialias.last() ? 1.0f : 0.0f; float inv_cam_scale = 1.0f / len(s_draw->cam_stack.last().m.y); - float scale = s_draw->antialias_scale.last(); - aaf = scale * inv_cam_scale * on_or_off; + float scale = s_draw->antialias.last(); + aaf = scale * inv_cam_scale; } void cf_make_draw() @@ -439,89 +449,17 @@ void cf_make_draw() // Mesh + vertex attributes. Array attrs; - attrs.add({ - .name = "in_pos", - .format = CF_VERTEX_FORMAT_FLOAT2, - .offset = CF_OFFSET_OF(CF_Vertex, p) - }); - - attrs.add({ - .name = "in_posH", - .format = CF_VERTEX_FORMAT_FLOAT2, - .offset = CF_OFFSET_OF(CF_Vertex, posH), - }); - - attrs.add({ - .name = "in_n", - .format = CF_VERTEX_FORMAT_INT, - .offset = CF_OFFSET_OF(CF_Vertex, n), - }); - - attrs.add({ - .name = "in_ab", - .format = CF_VERTEX_FORMAT_FLOAT4, - .offset = CF_OFFSET_OF(CF_Vertex, shape[0]), - }); - - attrs.add({ - .name = "in_cd", - .format = CF_VERTEX_FORMAT_FLOAT4, - .offset = CF_OFFSET_OF(CF_Vertex, shape[2]), - }); - - attrs.add({ - .name = "in_ef", - .format = CF_VERTEX_FORMAT_FLOAT4, - .offset = CF_OFFSET_OF(CF_Vertex, shape[4]), - }); - - attrs.add({ - .name = "in_gh", - .format = CF_VERTEX_FORMAT_FLOAT4, - .offset = CF_OFFSET_OF(CF_Vertex, shape[6]), - }); - - attrs.add({ - .name = "in_uv", - .format = CF_VERTEX_FORMAT_FLOAT2, - .offset = CF_OFFSET_OF(CF_Vertex, uv), - }); - - attrs.add({ - .name = "in_col", - .format = CF_VERTEX_FORMAT_UBYTE4_NORM, - .offset = CF_OFFSET_OF(CF_Vertex, color), - }); - - attrs.add({ - .name = "in_radius", - .format = CF_VERTEX_FORMAT_FLOAT, - .offset = CF_OFFSET_OF(CF_Vertex, radius), - }); - - attrs.add({ - .name = "in_stroke", - .format = CF_VERTEX_FORMAT_FLOAT, - .offset = CF_OFFSET_OF(CF_Vertex, stroke), - }); - - attrs.add({ - .name = "in_aa", - .format = CF_VERTEX_FORMAT_FLOAT, - .offset = CF_OFFSET_OF(CF_Vertex, aa), - }); - - attrs.add({ - .name = "in_params", - .format = CF_VERTEX_FORMAT_UBYTE4_NORM, - .offset = CF_OFFSET_OF(CF_Vertex, type), - }); - - attrs.add({ - .name = "in_user_params", - .format = CF_VERTEX_FORMAT_FLOAT4, - .offset = CF_OFFSET_OF(CF_Vertex, attributes), - }); + attrs.add({ .name = "in_pos_uv", .format = CF_VERTEX_FORMAT_FLOAT4, .offset = CF_OFFSET_OF(CF_Vertex, p) }); + attrs.add({ .name = "in_n", .format = CF_VERTEX_FORMAT_INT, .offset = CF_OFFSET_OF(CF_Vertex, n) }); + attrs.add({ .name = "in_ab", .format = CF_VERTEX_FORMAT_FLOAT4, .offset = CF_OFFSET_OF(CF_Vertex, shape[0]) }); + attrs.add({ .name = "in_cd", .format = CF_VERTEX_FORMAT_FLOAT4, .offset = CF_OFFSET_OF(CF_Vertex, shape[2]) }); + attrs.add({ .name = "in_ef", .format = CF_VERTEX_FORMAT_FLOAT4, .offset = CF_OFFSET_OF(CF_Vertex, shape[4]) }); + attrs.add({ .name = "in_gh", .format = CF_VERTEX_FORMAT_FLOAT4, .offset = CF_OFFSET_OF(CF_Vertex, shape[6]) }); + attrs.add({ .name = "in_col", .format = CF_VERTEX_FORMAT_UBYTE4_NORM, .offset = CF_OFFSET_OF(CF_Vertex, color) }); + attrs.add({ .name = "in_shape", .format = CF_VERTEX_FORMAT_FLOAT4, .offset = CF_OFFSET_OF(CF_Vertex, radius) }); + attrs.add({ .name = "in_blend_posH", .format = CF_VERTEX_FORMAT_FLOAT4, .offset = CF_OFFSET_OF(CF_Vertex, alpha) }); + attrs.add({ .name = "in_user", .format = CF_VERTEX_FORMAT_FLOAT4, .offset = CF_OFFSET_OF(CF_Vertex, attributes) }); + attrs.add({ .name = "in_uv_bounds", .format = CF_VERTEX_FORMAT_FLOAT4, .offset = CF_OFFSET_OF(CF_Vertex, uv_bounds) }); s_draw->mesh = cf_make_mesh(CF_MB * 5, attrs.data(), attrs.count(), sizeof(CF_Vertex)); // Shaders. @@ -543,6 +481,10 @@ void cf_make_draw() // Spritebatcher. s_init_sb(2048, 2048); + // Create samplers for filter mode switching. + s_draw->sampler_nearest = cf_create_draw_sampler(CF_FILTER_NEAREST); + s_draw->sampler_linear = cf_create_draw_sampler(CF_FILTER_LINEAR); + // Create an initial draw command. s_draw->add_cmd(); } @@ -552,6 +494,8 @@ void cf_destroy_draw() if (s_draw->blit_init) { cf_destroy_mesh(s_draw->blit_mesh); } + cf_destroy_draw_sampler(s_draw->sampler_nearest); + cf_destroy_draw_sampler(s_draw->sampler_linear); spritebatch_term(&s_draw->sb); cf_destroy_mesh(s_draw->mesh); cf_destroy_material(s_draw->material); @@ -575,8 +519,16 @@ void cf_draw_sprite(const CF_Sprite* sprite) s.maxy = 1; bool apply_border_scale = true; - if (sprite->animation) { - s.image_id = sprite->animation->frames[sprite->frame_index].id; + if (sprite->id != CF_SPRITE_ID_INVALID) { + if (sprite->blend_index > 0) { + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + const char* anim_name = sprite->animation_name; + const CF_Animation* anim = anim_name ? map_get(asset->animations, anim_name) : NULL; + int global_frame = sprite->frame_index + (anim ? anim->frame_offset : 0); + s.image_id = asset->blend_frame_ids[sprite->blend_index][global_frame]; + } else { + s.image_id = sprite->_image_id; + } } else if (sprite->easy_sprite_id >= CF_PREMADE_ID_RANGE_LO && sprite->easy_sprite_id <= CF_PREMADE_ID_RANGE_HI) { CF_AtlasSubImage sub_image = s_draw->premade_sub_image_id_to_sub_image.find(sprite->easy_sprite_id); s.minx = sub_image.minx; @@ -593,7 +545,7 @@ void cf_draw_sprite(const CF_Sprite* sprite) s.h = sprite->h; s.geom.type = BATCH_GEOMETRY_TYPE_SPRITE; - v2 offset = sprite->offset + (sprite->pivots ? sprite->pivots[sprite->frame_index] : V2(0,0)); + v2 offset = sprite->offset + (sprite->id != CF_SPRITE_ID_INVALID ? sprite->_pivot : V2(0,0)); v2 p = cf_add(sprite->transform.p, cf_mul(offset, sprite->scale)); v2 scale = V2(sprite->scale.x * s.w, sprite->scale.y * s.h); @@ -631,10 +583,10 @@ void cf_draw_sprite(const CF_Sprite* sprite) } CF_M3x2 m = s_draw->mvp; - s.geom.shape[0] = mul(m, quad[0]); - s.geom.shape[1] = mul(m, quad[1]); - s.geom.shape[2] = mul(m, quad[2]); - s.geom.shape[3] = mul(m, quad[3]); + CF_MUL_M32_V2(s.geom.shape[0], m, quad[0]); + CF_MUL_M32_V2(s.geom.shape[1], m, quad[1]); + CF_MUL_M32_V2(s.geom.shape[2], m, quad[2]); + CF_MUL_M32_V2(s.geom.shape[3], m, quad[3]); s.geom.is_sprite = true; s.geom.color = premultiply(pixel_white()); s.geom.alpha = sprite->opacity; @@ -648,19 +600,24 @@ void cf_draw_sprite_9_slice(const CF_Sprite* sprite) int frame_index = sprite->frame_index; SPRITEBATCH_U64 image_id = 0; - if (sprite->animation) { - frame_index = sprite->frame_index + sprite->animation->frame_offset; - image_id = sprite->animation->frames[sprite->frame_index].id; - } - else if (sprite->easy_sprite_id >= CF_PREMADE_ID_RANGE_LO && sprite->easy_sprite_id <= CF_PREMADE_ID_RANGE_HI) { + if (sprite->id != CF_SPRITE_ID_INVALID) { + if (sprite->blend_index > 0) { + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + const char* anim_name = sprite->animation_name; + const CF_Animation* anim = anim_name ? map_get(asset->animations, anim_name) : NULL; + int global_frame = sprite->frame_index + (anim ? anim->frame_offset : 0); + image_id = asset->blend_frame_ids[sprite->blend_index][global_frame]; + } else { + image_id = sprite->_image_id; + } + } else if (sprite->easy_sprite_id >= CF_PREMADE_ID_RANGE_LO && sprite->easy_sprite_id <= CF_PREMADE_ID_RANGE_HI) { CF_ASSERT(!"Not implemented yet."); - } - else { + } else { CF_ASSERT(!"Not implemented yet."); } // revert back to default cf_draw since center patch is 0 - CF_Aabb center_patch = sprite->center_patches[frame_index]; + CF_Aabb center_patch = sprite->_center_patch; if (center_patch.min.x == 0.0f && center_patch.min.y == 0.0f && center_patch.max.x == 0.0f && center_patch.max.y == 0.0f) { cf_draw_sprite(sprite); @@ -780,7 +737,7 @@ void cf_draw_sprite_9_slice(const CF_Sprite* sprite) }, }; - v2 offset = sprite->offset + (sprite->pivots ? sprite->pivots[sprite->frame_index] : V2(0, 0)); + v2 offset = sprite->offset + sprite->_pivot; v2 p = cf_add(sprite->transform.p, cf_mul_v2(offset, sprite->scale)); v2 scale = V2(sprite->scale.x * sprite->w, sprite->scale.y * sprite->h); @@ -820,10 +777,10 @@ void cf_draw_sprite_9_slice(const CF_Sprite* sprite) } CF_M3x2 m = s_draw->mvp; - s.geom.shape[0] = mul(m, quad[0]); - s.geom.shape[1] = mul(m, quad[1]); - s.geom.shape[2] = mul(m, quad[2]); - s.geom.shape[3] = mul(m, quad[3]); + CF_MUL_M32_V2(s.geom.shape[0], m, quad[0]); + CF_MUL_M32_V2(s.geom.shape[1], m, quad[1]); + CF_MUL_M32_V2(s.geom.shape[2], m, quad[2]); + CF_MUL_M32_V2(s.geom.shape[3], m, quad[3]); s.geom.is_sprite = true; s.geom.color = premultiply(pixel_white()); s.geom.alpha = sprite->opacity; @@ -840,19 +797,24 @@ void cf_draw_sprite_9_slice_tiled(const CF_Sprite* sprite) int frame_index = sprite->frame_index; SPRITEBATCH_U64 image_id = 0; - if (sprite->animation) { - frame_index = sprite->frame_index + sprite->animation->frame_offset; - image_id = sprite->animation->frames[sprite->frame_index].id; - } - else if (sprite->easy_sprite_id >= CF_PREMADE_ID_RANGE_LO && sprite->easy_sprite_id <= CF_PREMADE_ID_RANGE_HI) { + if (sprite->id != CF_SPRITE_ID_INVALID) { + if (sprite->blend_index > 0) { + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + const char* anim_name = sprite->animation_name; + const CF_Animation* anim = anim_name ? map_get(asset->animations, anim_name) : NULL; + int global_frame = sprite->frame_index + (anim ? anim->frame_offset : 0); + image_id = asset->blend_frame_ids[sprite->blend_index][global_frame]; + } else { + image_id = sprite->_image_id; + } + } else if (sprite->easy_sprite_id >= CF_PREMADE_ID_RANGE_LO && sprite->easy_sprite_id <= CF_PREMADE_ID_RANGE_HI) { CF_ASSERT(!"Not implemented yet."); - } - else { + } else { CF_ASSERT(!"Not implemented yet."); } // revert back to default cf_draw since center patch is 0 - CF_Aabb center_patch = sprite->center_patches[frame_index]; + CF_Aabb center_patch = sprite->_center_patch; if (center_patch.min.x == 0.0f && center_patch.min.y == 0.0f && center_patch.max.x == 0.0f && center_patch.max.y == 0.0f) { cf_draw_sprite(sprite); @@ -974,7 +936,7 @@ void cf_draw_sprite_9_slice_tiled(const CF_Sprite* sprite) }, }; - v2 offset = sprite->offset + (sprite->pivots ? sprite->pivots[sprite->frame_index] : V2(0, 0)); + v2 offset = sprite->offset + sprite->_pivot; v2 p = cf_add(sprite->transform.p, cf_mul_v2(offset, sprite->scale)); v2 scale = V2(sprite->scale.x * sprite->w, sprite->scale.y * sprite->h); @@ -1011,10 +973,10 @@ void cf_draw_sprite_9_slice_tiled(const CF_Sprite* sprite) } CF_M3x2 m = s_draw->mvp; - s.geom.shape[0] = mul(m, quad[0]); - s.geom.shape[1] = mul(m, quad[1]); - s.geom.shape[2] = mul(m, quad[2]); - s.geom.shape[3] = mul(m, quad[3]); + CF_MUL_M32_V2(s.geom.shape[0], m, quad[0]); + CF_MUL_M32_V2(s.geom.shape[1], m, quad[1]); + CF_MUL_M32_V2(s.geom.shape[2], m, quad[2]); + CF_MUL_M32_V2(s.geom.shape[3], m, quad[3]); s.geom.is_sprite = true; s.geom.color = premultiply(pixel_white()); s.geom.alpha = sprite->opacity; @@ -1118,9 +1080,11 @@ void cf_draw_sprite_9_slice_tiled(const CF_Sprite* sprite) void cf_draw_prefetch(const CF_Sprite* sprite) { - if (sprite->animation) { - for (int i = 0; i < hsize(sprite->animations); ++i) { - const CF_Animation* animation = sprite->animations[i]; + if (sprite->id != CF_SPRITE_ID_INVALID) { + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + CF_Animation** anim_vals = map_items(asset->animations); + for (int i = 0; i < map_size(asset->animations); ++i) { + CF_Animation* animation = anim_vals[i]; for (int j = 0; j < asize(animation->frames); ++j) { CF_Frame* frame = animation->frames + j; spritebatch_prefetch(&s_draw->sb, frame->id, sprite->w, sprite->h); @@ -1156,10 +1120,10 @@ static void s_draw_quad(CF_V2 p0, CF_V2 p1, CF_V2 p2, CF_V2 p3, float stroke, fl s.geom.box[1] = p1; s.geom.box[2] = p2; s.geom.box[3] = p3; - s.geom.boxH[0] = mul(m, p0); - s.geom.boxH[1] = mul(m, p1); - s.geom.boxH[2] = mul(m, p2); - s.geom.boxH[3] = mul(m, p3); + CF_MUL_M32_V2(s.geom.boxH[0], m, p0); + CF_MUL_M32_V2(s.geom.boxH[1], m, p1); + CF_MUL_M32_V2(s.geom.boxH[2], m, p2); + CF_MUL_M32_V2(s.geom.boxH[3], m, p3); s.geom.shape[0] = c; s.geom.shape[1] = he; s.geom.shape[2] = u; @@ -1236,10 +1200,10 @@ static void s_draw_circle(v2 position, float stroke, float radius, bool fill) s.geom.box[1] = s.geom.box[1]; s.geom.box[2] = s.geom.box[2]; s.geom.box[3] = s.geom.box[3]; - s.geom.boxH[0] = mul(m, s.geom.box[0]); - s.geom.boxH[1] = mul(m, s.geom.box[1]); - s.geom.boxH[2] = mul(m, s.geom.box[2]); - s.geom.boxH[3] = mul(m, s.geom.box[3]); + CF_MUL_M32_V2(s.geom.boxH[0], m, s.geom.box[0]); + CF_MUL_M32_V2(s.geom.boxH[1], m, s.geom.box[1]); + CF_MUL_M32_V2(s.geom.boxH[2], m, s.geom.box[2]); + CF_MUL_M32_V2(s.geom.boxH[3], m, s.geom.box[3]); s.geom.shape[0] = position; s.geom.shape[1] = position; s.geom.shape[2] = position; @@ -1293,10 +1257,10 @@ static void s_draw_capsule(v2 a, v2 b, float stroke, float radius, bool fill) s.geom.type = BATCH_GEOMETRY_TYPE_CAPSULE; s_bounding_box_of_capsule(a, b, radius, stroke, s.geom.box); - s.geom.boxH[0] = mul(m, s.geom.box[0]); - s.geom.boxH[1] = mul(m, s.geom.box[1]); - s.geom.boxH[2] = mul(m, s.geom.box[2]); - s.geom.boxH[3] = mul(m, s.geom.box[3]); + CF_MUL_M32_V2(s.geom.boxH[0], m, s.geom.box[0]); + CF_MUL_M32_V2(s.geom.boxH[1], m, s.geom.box[1]); + CF_MUL_M32_V2(s.geom.boxH[2], m, s.geom.box[2]); + CF_MUL_M32_V2(s.geom.boxH[3], m, s.geom.box[3]); s.geom.shape[0] = a; s.geom.shape[1] = b; s.geom.shape[2] = a; @@ -1376,18 +1340,18 @@ static void s_draw_tri(v2 a, v2 b, v2 c, float stroke, float radius, bool fill) s.geom.box[1] = s.geom.box[1]; s.geom.box[2] = s.geom.box[2]; s.geom.box[3] = s.geom.box[3]; - s.geom.boxH[0] = mul(m, s.geom.box[0]); - s.geom.boxH[1] = mul(m, s.geom.box[1]); - s.geom.boxH[2] = mul(m, s.geom.box[2]); - s.geom.boxH[3] = mul(m, s.geom.box[3]); + CF_MUL_M32_V2(s.geom.boxH[0], m, s.geom.box[0]); + CF_MUL_M32_V2(s.geom.boxH[1], m, s.geom.box[1]); + CF_MUL_M32_V2(s.geom.boxH[2], m, s.geom.box[2]); + CF_MUL_M32_V2(s.geom.boxH[3], m, s.geom.box[3]); s.geom.shape[0] = a; s.geom.shape[1] = b; s.geom.shape[2] = c; } else { s.geom.type = BATCH_GEOMETRY_TYPE_TRI; - s.geom.shape[0] = mul(m, a); - s.geom.shape[1] = mul(m, b); - s.geom.shape[2] = mul(m, c); + CF_MUL_M32_V2(s.geom.shape[0], m, a); + CF_MUL_M32_V2(s.geom.shape[1], m, b); + CF_MUL_M32_V2(s.geom.shape[2], m, c); } s.geom.color = premultiply(to_pixel(s_draw->colors.last())); @@ -1397,6 +1361,23 @@ static void s_draw_tri(v2 a, v2 b, v2 c, float stroke, float radius, bool fill) s.geom.fill = fill; s.geom.aa = s_draw->aaf; s.geom.user_params = s_draw->user_params.last(); + + // Per-vertex triangle colors. + s.geom.use_tri_colors = s_draw->tri_colors0.count() > 1; + if (s.geom.use_tri_colors) { + s.geom.tri_colors[0] = premultiply(to_pixel(s_draw->tri_colors0.last())); + s.geom.tri_colors[1] = premultiply(to_pixel(s_draw->tri_colors1.last())); + s.geom.tri_colors[2] = premultiply(to_pixel(s_draw->tri_colors2.last())); + } + + // Per-vertex triangle attributes. + s.geom.use_tri_attributes = s_draw->tri_attributes0.count() > 1; + if (s.geom.use_tri_attributes) { + s.geom.tri_attributes[0] = s_draw->tri_attributes0.last(); + s.geom.tri_attributes[1] = s_draw->tri_attributes1.last(); + s.geom.tri_attributes[2] = s_draw->tri_attributes2.last(); + } + DRAW_PUSH_ITEM(s); } @@ -1466,10 +1447,10 @@ void cf_draw_polyline(const CF_V2* pts, int count, float thickness, bool loop) auto submit = [&](v2 a, v2 b, v2 c, bool solo = false) { if (skip) return; if (debug) { - draw_push_antialias(false); + draw_push_shape_aa(false); draw_tri(a, b, c); draw_tri_fill(a, b, c); - draw_pop_antialias(); + draw_pop_shape_aa(); } else { s.geom.shape[0] = p0; s.geom.shape[1] = p1; @@ -1477,9 +1458,9 @@ void cf_draw_polyline(const CF_V2* pts, int count, float thickness, bool loop) s.geom.box[0] = a; s.geom.box[1] = b; s.geom.box[2] = c; - s.geom.boxH[0] = mul(m, a); - s.geom.boxH[1] = mul(m, b); - s.geom.boxH[2] = mul(m, c); + CF_MUL_M32_V2(s.geom.boxH[0], m, a); + CF_MUL_M32_V2(s.geom.boxH[1], m, b); + CF_MUL_M32_V2(s.geom.boxH[2], m, c); DRAW_PUSH_ITEM(s); } }; @@ -1610,10 +1591,10 @@ void cf_draw_polygon_fill(const CF_V2* points, int count, float chubbiness) s.geom.box[1] = box[1]; s.geom.box[2] = box[2]; s.geom.box[3] = box[3]; - s.geom.boxH[0] = mul(m, s.geom.box[0]); - s.geom.boxH[1] = mul(m, s.geom.box[1]); - s.geom.boxH[2] = mul(m, s.geom.box[2]); - s.geom.boxH[3] = mul(m, s.geom.box[3]); + CF_MUL_M32_V2(s.geom.boxH[0], m, s.geom.box[0]); + CF_MUL_M32_V2(s.geom.boxH[1], m, s.geom.box[1]); + CF_MUL_M32_V2(s.geom.boxH[2], m, s.geom.box[2]); + CF_MUL_M32_V2(s.geom.boxH[3], m, s.geom.box[3]); s.geom.n = count; for (int i = 0; i < count; ++i) { s.geom.shape[i] = points[i]; @@ -1842,6 +1823,11 @@ CF_Font* cf_font_get(const char* font_name) return app->fonts.get(sintern(font_name)); } +float cf_font_scale_for_pixel_height(CF_Font* font, float pixel_height) +{ + return stbtt_ScaleForPixelHeight(&font->info, pixel_height); +} + CF_INLINE uint64_t cf_glyph_key(int cp, float font_size, int blur) { int k0 = cp; @@ -2001,7 +1987,9 @@ CF_Glyph* cf_font_get_glyph(CF_Font* font, int code, float font_size, int blur) float cf_font_get_kern(CF_Font* font, float font_size, int code0, int code1) { uint64_t key = CF_KERN_KEY(code0, code1); - return font->kerning.get(key) * stbtt_ScaleForPixelHeight(&font->info, font_size); + int* val = font->kerning.try_get(key); + if (!val) return 0; + return *val * stbtt_ScaleForPixelHeight(&font->info, font_size); } void cf_push_font(const char* font) @@ -2184,7 +2172,7 @@ static const char* s_find_end_of_line(CF_Font* font, const char* text, float wra while (*text) { const char* text_prev = text; - text = cf_decode_UTF8(text, &cp); + text = cf_string_decode_UTF8(text, &cp); CF_Glyph* glyph = cf_font_get_glyph(font, cp, font_size, blur); if (cp == '\n') { @@ -2232,11 +2220,11 @@ struct CF_CodeParseState bool done() { return in >= end; } void append(int ch) { sanitized.append(ch); ++glyph_count; } void ltrim() { while (!done()) { int cp = *in; if (s_is_space(cp)) ++in; else break; } } - int next(bool trim = true) { if (trim) ltrim(); int cp; in = cf_decode_UTF8(in, &cp); return cp; } - int peek(bool trim = true) { if (trim) ltrim(); int cp; cf_decode_UTF8(in, &cp); return cp; } - void skip(bool trim = true) { if (trim) ltrim(); int cp; in = cf_decode_UTF8(in, &cp); } + int next(bool trim = true) { if (trim) ltrim(); int cp; in = cf_string_decode_UTF8(in, &cp); return cp; } + int peek(bool trim = true) { if (trim) ltrim(); int cp; cf_string_decode_UTF8(in, &cp); return cp; } + void skip(bool trim = true) { if (trim) ltrim(); int cp; in = cf_string_decode_UTF8(in, &cp); } bool expect(int ch) { int cp = next(); if (cp != ch) { return false; } return true; } - bool try_next(int ch, bool trim = true) { if (trim) ltrim(); int cp; const char* next = cf_decode_UTF8(in, &cp); if (cp == ch) { in = next; return true; } return false; } + bool try_next(int ch, bool trim = true) { if (trim) ltrim(); int cp; const char* next = cf_string_decode_UTF8(in, &cp); if (cp == ch) { in = next; return true; } return false; } }; static String s_parse_code_name(CF_CodeParseState* s) @@ -2277,26 +2265,25 @@ static CF_Color s_parse_color(CF_CodeParseState* s) { String string; s->expect('#'); - int digits = 0; while (!s->done()) { int cp = s->peek(); if (!s_is_hex_alphanum(cp)) { break; } else { string.append(cp); - ++digits; s->skip(); } } - int hex = 0; + uint32_t rgba = 0x000000FF; if (!string.empty()) { - hex = (int)string.to_hex(); - if (digits == 6) { - // Treat the color as opaque if only 3 bytes were found. - hex = hex << 8 | 0xFF; - } + rgba = (uint32_t)string.to_hex(); } - CF_Color result = make_color(hex); + CF_Color result = cf_make_color_rgba( + (uint8_t)((rgba >> 24) & 0xFF), + (uint8_t)((rgba >> 16) & 0xFF), + (uint8_t)((rgba >> 8) & 0xFF), + (uint8_t)(rgba & 0xFF) + ); return result; } @@ -2617,7 +2604,7 @@ static v2 s_draw_text(const char* text, CF_V2 position, int text_length, bool re // Used by the line-wrapping algorithm to skip characters. auto skip_to_next = [&]() { - text = cf_decode_UTF8(text, &cp); + text = cf_string_decode_UTF8(text, &cp); effect_cleanup(); ++index; }; @@ -2666,7 +2653,7 @@ static v2 s_draw_text(const char* text, CF_V2 position, int text_length, bool re cp_prev = cp; const char* prev_text = text; if ((render || markups) && do_effects) effect_spawn(); - text = cf_decode_UTF8(text, &cp); + text = cf_string_decode_UTF8(text, &cp); ++index; CF_DEFER(effect_cleanup()); @@ -2782,10 +2769,10 @@ static v2 s_draw_text(const char* text, CF_V2 position, int text_length, bool re // Actually render the sprite. if (visible && render) { CF_M3x2 m = s_draw->mvp; - s.geom.shape[0] = mul(m, V2(q0.x, q1.y)); - s.geom.shape[1] = mul(m, V2(q1.x, q1.y)); - s.geom.shape[2] = mul(m, V2(q1.x, q0.y)); - s.geom.shape[3] = mul(m, V2(q0.x, q0.y)); + CF_MUL_M32_V2(s.geom.shape[0], m, V2(q0.x, q1.y)); + CF_MUL_M32_V2(s.geom.shape[1], m, V2(q1.x, q1.y)); + CF_MUL_M32_V2(s.geom.shape[2], m, V2(q1.x, q0.y)); + CF_MUL_M32_V2(s.geom.shape[3], m, V2(q0.x, q0.y)); s.geom.color = premultiply(to_pixel(color)); s.geom.is_text = true; DRAW_PUSH_ITEM(s); @@ -2891,16 +2878,16 @@ CF_Color cf_draw_peek_color() return s_draw->colors.last(); } -void cf_draw_push_antialias(bool antialias) +void cf_draw_push_shape_aa(float aa) { - s_draw->antialias.add(antialias); + s_draw->antialias.add(aa); s_draw->set_aaf(); } -bool cf_draw_pop_antialias() +float cf_draw_pop_shape_aa() { if (s_draw->antialias.count() > 1) { - bool result = s_draw->antialias.pop(); + float result = s_draw->antialias.pop(); s_draw->set_aaf(); return result; } else { @@ -2908,51 +2895,75 @@ bool cf_draw_pop_antialias() } } -bool cf_draw_peek_antialias() +float cf_draw_peek_shape_aa() { return s_draw->antialias.last(); } -void cf_draw_push_antialias_scale(float scale) +void cf_draw_push_vertex_attributes(float r, float g, float b, float a) { - s_draw->antialias_scale.add(scale); - s_draw->set_aaf(); + s_draw->user_params.add(cf_make_color_rgba_f(r, g, b, a)); } -float cf_draw_pop_antialias_scale() +void cf_draw_push_vertex_attributes2(CF_Color attributes) { - if (s_draw->antialias_scale.count() > 1) { - float scale = s_draw->antialias_scale.pop(); - s_draw->set_aaf(); - return scale; - } else { - return s_draw->antialias_scale.last(); - } + s_draw->user_params.add(attributes); } -float cf_draw_peek_antialias_scale() +CF_Color cf_draw_pop_vertex_attributes() { - return s_draw->antialias_scale.last(); + return s_draw->user_params.count() > 1 ? s_draw->user_params.pop() : s_draw->user_params.last(); } -void cf_draw_push_vertex_attributes(float r, float g, float b, float a) +CF_Color cf_draw_peek_vertex_attributes() { - s_draw->user_params.add(cf_make_color_rgba_f(r, g, b, a)); + return s_draw->user_params.last(); } -void cf_draw_push_vertex_attributes2(CF_Color attributes) +void cf_draw_push_tri_colors(CF_Color c0, CF_Color c1, CF_Color c2) { - s_draw->user_params.add(attributes); + s_draw->tri_colors0.add(c0); + s_draw->tri_colors1.add(c1); + s_draw->tri_colors2.add(c2); } -CF_Color cf_draw_pop_vertex_attributes() +void cf_draw_pop_tri_colors() { - return s_draw->user_params.count() > 1 ? s_draw->user_params.pop() : s_draw->user_params.last(); + if (s_draw->tri_colors0.count() > 1) { + s_draw->tri_colors0.pop(); + s_draw->tri_colors1.pop(); + s_draw->tri_colors2.pop(); + } } -CF_Color cf_draw_peek_vertex_attributes() +void cf_draw_peek_tri_colors(CF_Color* c0, CF_Color* c1, CF_Color* c2) { - return s_draw->user_params.last(); + if (c0) *c0 = s_draw->tri_colors0.last(); + if (c1) *c1 = s_draw->tri_colors1.last(); + if (c2) *c2 = s_draw->tri_colors2.last(); +} + +void cf_draw_push_tri_attributes(CF_Color a0, CF_Color a1, CF_Color a2) +{ + s_draw->tri_attributes0.add(a0); + s_draw->tri_attributes1.add(a1); + s_draw->tri_attributes2.add(a2); +} + +void cf_draw_pop_tri_attributes() +{ + if (s_draw->tri_attributes0.count() > 1) { + s_draw->tri_attributes0.pop(); + s_draw->tri_attributes1.pop(); + s_draw->tri_attributes2.pop(); + } +} + +void cf_draw_peek_tri_attributes(CF_Color* a0, CF_Color* a1, CF_Color* a2) +{ + if (a0) *a0 = s_draw->tri_attributes0.last(); + if (a1) *a1 = s_draw->tri_attributes1.last(); + if (a2) *a2 = s_draw->tri_attributes2.last(); } void cf_set_vertex_callback(CF_VertexFn* vertex_fn) @@ -3021,6 +3032,7 @@ CF_Shader cf_make_draw_shader(const char* path) CF_Shader blit_shd = cf_make_draw_blit_shader_internal(path); CF_Shader draw_shd = cf_make_draw_shader_internal(path); s_draw->draw_shd_to_blit_shd.add(draw_shd.id, blit_shd.id); + if (draw_shd.id) s_draw->shader_paths.add(draw_shd.id, sintern(path)); return draw_shd; } @@ -3042,6 +3054,20 @@ CF_Shader cf_make_draw_shader_from_bytecode(CF_DrawShaderBytecode bytecode) return draw_shd; } +bool cf_shader_reload(CF_Shader* shader) +{ + const char** path_ptr = s_draw->shader_paths.try_find(shader->id); + if (!path_ptr) return false; + const char* path = *path_ptr; + + CF_Shader new_shd = cf_make_draw_shader(path); + if (!new_shd.id) return false; + + cf_destroy_shader(*shader); + *shader = new_shd; + return true; +} + void cf_draw_push_shader(CF_Shader shader) { CF_ASSERT(shader.id); @@ -3083,6 +3109,26 @@ bool cf_draw_peek_alpha_discard() return s_draw->alpha_discards.last() == 0 ? false : true; } +void cf_draw_push_filter(CF_DrawFilterMode filter_mode) +{ + PUSH_DRAW_VAR_AND_ADD_CMD_IF_NEEDED(filter_mode); +} + +static CF_DrawFilterMode s_pop_filter_mode() +{ + POP_DRAW_VAR_AND_ADD_CMD_IF_NEEDED(filter_mode); +} + +CF_DrawFilterMode cf_draw_pop_filter() +{ + return s_pop_filter_mode(); +} + +CF_DrawFilterMode cf_draw_peek_filter() +{ + return s_draw->filter_modes.last(); +} + void cf_draw_set_texture(const char* name, CF_Texture texture) { CF_DrawUniform u; @@ -3179,9 +3225,15 @@ void cf_draw_canvas(CF_Canvas canvas, CF_V2 position, CF_V2 scale) swap(cmd.canvas_verts[1], cmd.canvas_verts[2]); } for (int i = 0; i < 4; ++i) { - cmd.canvas_verts_posH[i] = mul(s_draw->mvp, cmd.canvas_verts[i]); + CF_MUL_M32_V2(cmd.canvas_verts_posH[i], s_draw->mvp, cmd.canvas_verts[i]); } cmd.canvas_attributes = s_draw->user_params.last(); + + // Ensure subsequent draw items don't land on this canvas command. + // Without this, any draws after cf_draw_canvas that don't trigger a new + // command (e.g. no layer/shader/state change) silently append to the canvas + // command's items array, which s_process_command ignores for canvas blits. + s_draw->add_cmd(); } void static s_blit(CF_Command* cmd, CF_Canvas src, CF_Canvas dst, bool clear_dst) @@ -3264,11 +3316,19 @@ void static s_blit(CF_Command* cmd, CF_Canvas src, CF_Canvas dst, bool clear_dst cf_canvas_get_size(cmd->canvas, &w, &h); v2 canvas_dims = V2((float)w, (float)h); cf_material_set_uniform_fs(s_draw->material, "u_texture_size", &canvas_dims, CF_UNIFORM_TYPE_FLOAT2, 1); - cf_material_set_uniform_fs(s_draw->material, "u_alpha_discard", &cmd->alpha_discard, CF_UNIFORM_TYPE_INT, 1); + int alpha_discard = cmd->alpha_discard == 0.0f ? 0 : 1; + cf_material_set_uniform_fs(s_draw->material, "u_alpha_discard", &alpha_discard, CF_UNIFORM_TYPE_INT, 1); + // u_use_smooth_uv: 0 = apply shader smooth_uv function, 1 = use plain v_uv (hardware filtering only) + int use_smooth_uv = cmd->filter_mode == CF_DRAW_FILTER_SMOOTH ? 0 : 1; + cf_material_set_uniform_fs(s_draw->material, "u_use_smooth_uv", &use_smooth_uv, CF_UNIFORM_TYPE_INT, 1); // Apply render state. cf_material_set_render_state(s_draw->material, cmd->render_state); + // Set sampler filter based on filter mode. + void* sampler_override = (cmd->filter_mode == CF_DRAW_FILTER_NEAREST) ? s_draw->sampler_nearest : s_draw->sampler_linear; + cf_set_sampler_override(sampler_override); + // Apply shader. cf_apply_shader(*blit, s_draw->material); @@ -3304,6 +3364,14 @@ static void s_process_command(CF_Canvas canvas, CF_Command* cmd, CF_Command* nex // Blit canvas. // ...Incurs an entire extra draw call by itself. if (cmd->is_canvas) { + // Flush any accumulated geometry before the blit. + if (s_draw->need_flush) { + s_draw->need_flush = false; + if (!s_draw->delay_defrag) { + spritebatch_defrag(&s_draw->sb); + } + spritebatch_flush(&s_draw->sb); + } s_blit(cmd, cmd->canvas, canvas, clear); clear = false; // Only clear `canvas` once. s_draw->has_drawn_something = true; @@ -3311,10 +3379,11 @@ static void s_process_command(CF_Canvas canvas, CF_Command* cmd, CF_Command* nex } // Collate all of the drawable items into the spritebatch. - if (!cmd->items.count()) return; - s_draw->need_flush = true; - for (int j = 0; j < cmd->items.count(); ++j) { - spritebatch_push(&s_draw->sb, cmd->items[j]); + if (cmd->items.count()) { + s_draw->need_flush = true; + for (int j = 0; j < cmd->items.count(); ++j) { + spritebatch_push(&s_draw->sb, cmd->items[j]); + } } // Merge with the next command if identical. @@ -3332,6 +3401,7 @@ static void s_process_command(CF_Canvas canvas, CF_Command* cmd, CF_Command* nex same = false; } else if (!( next->alpha_discard == cmd->alpha_discard && + next->filter_mode == cmd->filter_mode && next->render_state == cmd->render_state && next->scissor == cmd->scissor && next->shader == cmd->shader && @@ -3343,7 +3413,7 @@ static void s_process_command(CF_Canvas canvas, CF_Command* cmd, CF_Command* nex same = false; } - if (!same) { + if (!same && s_draw->need_flush) { // Process the collated drawable items. Might get split up into multiple draw calls depending on // the atlas compiler. s_draw->need_flush = false; @@ -3413,14 +3483,16 @@ void cf_render_to(CF_Canvas canvas, bool clear) CF_V2 cf_draw_mul(CF_V2 v) { - return mul(s_draw->cam_stack.last(), v); + CF_V2 r; + CF_MUL_M32_V2(r, s_draw->cam_stack.last(), v); + return r; } void cf_draw_transform(CF_M3x2 m) { - m = mul(s_draw->cam_stack.last(), m); + CF_MUL_M32_M32(m, s_draw->cam_stack.last(), m); s_draw->cam_stack.last() = m; - s_draw->mvp = mul(s_draw->projection, m); + CF_MUL_M32_M32(s_draw->mvp, s_draw->projection, m); s_draw->set_aaf(); } @@ -3454,14 +3526,20 @@ void cf_draw_rotate(float radians) void cf_draw_TSR(CF_V2 position, CF_V2 scale, float radians) { - cf_draw_transform(make_transform(position, scale, radians)); + CF_M3x2 m; + CF_MAKE_TSR(m, position, scale, radians); + CF_MUL_M32_M32(m, s_draw->cam_stack.last(), m); + s_draw->cam_stack.last() = m; + CF_MUL_M32_M32(s_draw->mvp, s_draw->projection, m); + s_draw->set_aaf(); } void cf_draw_TSR_absolute(CF_V2 position, CF_V2 scale, float radians) { - CF_M3x2 m = make_transform(position, scale, radians); + CF_M3x2 m; + CF_MAKE_TSR(m, position, scale, radians); s_draw->cam_stack.last() = m; - s_draw->mvp = mul(s_draw->projection, m); + CF_MUL_M32_M32(s_draw->mvp, s_draw->projection, m); s_draw->set_aaf(); } @@ -3469,6 +3547,7 @@ void cf_draw_push() { CF_M3x2 m = s_draw->cam_stack.last(); s_draw->cam_stack.add(m); + s_draw->projection_stack.add(s_draw->projection); } void cf_draw_pop() @@ -3476,8 +3555,12 @@ void cf_draw_pop() if (s_draw->cam_stack.size() > 1) { s_draw->cam_stack.pop(); } + if (s_draw->projection_stack.size() > 0) { + s_draw->projection = s_draw->projection_stack.last(); + s_draw->projection_stack.pop(); + } CF_M3x2 m = s_draw->cam_stack.last(); - s_draw->mvp = mul(s_draw->projection, m); + CF_MUL_M32_M32(s_draw->mvp, s_draw->projection, m); s_draw->set_aaf(); } @@ -3489,12 +3572,12 @@ CF_M3x2 cf_draw_peek() void cf_draw_projection(CF_M3x2 projection) { s_draw->projection = projection; - s_draw->mvp = mul(projection, s_draw->cam_stack.last()); + CF_MUL_M32_M32(s_draw->mvp, projection, s_draw->cam_stack.last()); } CF_V2 cf_world_to_screen(CF_V2 CF_V2) { - CF_V2 = mul(s_draw->mvp, CF_V2); + CF_MUL_M32_V2(CF_V2, s_draw->mvp, CF_V2); CF_V2.x = (CF_V2.x + 1.0f) * (float)app->w * 0.5f; CF_V2.y = (1.0f - CF_V2.y) * (float)app->h * 0.5f; return CF_V2; @@ -3533,12 +3616,12 @@ CF_TemporaryImage cf_fetch_image(const CF_Sprite* sprite) return image; } else { uint64_t image_id; - if (sprite->animation) { - image_id = sprite->animation->frames[sprite->frame_index].id; + if (sprite->id != CF_SPRITE_ID_INVALID) { + image_id = sprite->_image_id; } else { image_id = sprite->easy_sprite_id; } - + spritebatch_sprite_t s = spritebatch_fetch(&s_draw->sb, image_id, sprite->w, sprite->h); CF_TemporaryImage image; image.tex = { s.texture_id }; diff --git a/src/cute_file_system.cpp b/src/cute_file_system.cpp index 945bbf020..f05ad9f91 100644 --- a/src/cute_file_system.cpp +++ b/src/cute_file_system.cpp @@ -16,188 +16,6 @@ #define CF_FILE_SYSTEM_BUFFERED_IO_SIZE (2 * CF_MB) -char* cf_path_get_filename(const char* path) -{ - if (!path || path[0] == '\0') { return NULL; } - - int at = slast_index_of(path, '/'); - - const char *f = path + at + 1; - if (f[0] != '\0') { - return smake(f); - } - - // The last character was "/" so there was no filename - return NULL; -} - -char* cf_path_get_filename_no_ext(const char* path) -{ - char* s = cf_path_get_filename(path); - - if (!s) { return NULL; } - - int at = slast_index_of(s, '.'); - if (at == 0) { - // The filename only has an extension - sfree(s); - return NULL; - } - - if (at == -1) { - // The filename has no extension - return s; - } - - // Remove the extension from the filename - serase(s, at, slen(s) - at); - return s; -} - -char* cf_path_get_ext(const char* path) -{ - int at = slast_index_of(path, '.'); - if (at == -1 || path[at + 1] == 0 || path[at + 1] == '/') return NULL; - return smake(path + at); -} - -bool cf_path_ext_equ(const char* path, const char* ext) -{ - int at = slast_index_of(path, '.'); - if (at == -1 || path[at + 1] == 0 || path[at + 1] == '/') return false; - return sequ(path + at, ext); -} - -char* cf_path_pop(const char* path) -{ - char* in = (char*)path; - if (sisdyna(in)) { - if (slast(in) == '/') spop(in); - int at = slast_index_of(path, '/'); - if (at == -1 || at == 0) return sset(in, "/"); - serase(in, at, slen(in) - at); - return in; - } else { - char* s = sdup(path); - if (slast(s) == '/') spop(s); - int at = slast_index_of(path, '/'); - if (at == -1 || at == 0) return sset(s, "/"); - serase(s, at, slen(s) - at); - return s; - } -} - -char* cf_path_pop_n(const char* path, int n) -{ - char* s = (char*)path; - while (n--) { - s = sppop(s); - } - return s; -} - -char* cf_path_compact(const char* path, int n) -{ - int len = (int)CF_STRLEN(path); - if (n <= 6) return NULL; - if (len < n) return sdup(path); - int at = slast_index_of(path, '/'); - if (at == -1 || at == 0) { - char* s = sdup(path); - serase(s, n, slen(s) - n); - serase(s, n - 3, 3); - return sappend(s, "..."); - } - int remaining = len - at - 1; - if (remaining >= n - 3) { - char* s = smake("..."); - sappend_range(s, path, path + at - 6); - return sappend(s, "..."); - } else { - char* s = sdup(path); - int len_s = slen(s); - int to_erase = len_s - (remaining - 3); - serase(s, remaining - 3, to_erase); - sappend(s, "..."); - return sappend(s, path + at); - } -} - -char* cf_path_directory_of(const char* path) -{ - if (!*path || (*path == '.' && CF_STRLEN(path) < 3)) return NULL; - if (sequ(path, "../")) return NULL; - if (sequ(path, "/")) return NULL; - int at = slast_index_of(path, '/'); - if (at == -1) return NULL; - if (at == 0) return smake("/"); - char* s = smake(path); - serase(s, at, slen(s) - at); - at = slast_index_of(s, '/'); - if (at == -1) { - int l = slen(s); - if (slen(s) == 2) { - return s; - } else { - s[0] = '/'; - return s; - } - } - return serase(s, 0, at); -} - -char* cf_path_top_directory(const char* path) -{ - int at = sfirst_index_of(path, '/'); - if (at == -1) return NULL; - int next = sfirst_index_of(path + at + 1, '/'); - if (next == -1) return smake("/"); - char* s = sdup(path + at); - return serase(s, next + 1, slen(s) - (next + 1)); -} - -char* cf_path_normalize(const char* path) -{ - char* result = NULL; - int len = (int)CF_STRLEN(path); - if (*path != '\\' && *path != '/') { - bool windows_drive = len >= 2 && path[1] == ':'; - if (!windows_drive) { - spush(result, '/'); - } - } - bool prev_was_dot = false; - bool prev_was_dotdot = false; - for (int i = 0; i < len; ++i) { - char c = path[i]; - int l = slen(result); - if (c == '\\' || c == '/') { - if (!prev_was_dot) { - spush(result, '/'); - } else if (prev_was_dotdot) { - sppop(result); - spush(result, '/'); - } - prev_was_dot = false; - prev_was_dotdot = false; - } else if (c == '.') { - if (prev_was_dot) { - prev_was_dotdot = true; - } - prev_was_dot = true; - } else { - if (prev_was_dot) { - spush(result, '.'); - } - spush(result, c); - prev_was_dot = false; - prev_was_dotdot = false; - } - } - sreplace(result, "//", "/"); - if (slen(result) > 1 && slast(result) == '/') spop(result); - return result; -} //-------------------------------------------------------------------------------------------------- @@ -464,7 +282,7 @@ CF_Result cf_fs_write_string_range_to_file(const char* virtual_path, const char* CF_Result cf_fs_init(const char* argv0) { - if (!PHYSFS_init(argv0)) { + if (!PHYSFS_init(argv0 ? argv0 : "")) { return cf_result_error(PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); } else { return cf_result_success(); diff --git a/src/cute_graphics.cpp b/src/cute_graphics.cpp index 4d7318248..9ff027042 100644 --- a/src/cute_graphics.cpp +++ b/src/cute_graphics.cpp @@ -14,26 +14,28 @@ #include #include -#include "cute_shader/cute_shader.h" -#include "cute_shader/builtin_shaders.h" +#include "cute_shader.h" +#include "builtin_shaders.h" #include "data/builtin_shaders_bytecode.h" #include using namespace Cute; +static Map s_compute_shader_paths; + static void s_shader_directory_recursive(CF_Path path) { Array dir = CF_Directory::enumerate(app->shader_directory + path); for (int i = 0; i < dir.size(); ++i) { CF_Path p = app->shader_directory + path + dir[i]; if (p.is_directory()) { - s_shader_directory_recursive(p); + s_shader_directory_recursive(path + dir[i]); } else { CF_Stat stat; fs_stat(p, &stat); String ext = p.ext(); - if (ext == ".vs" || ext == ".fs" || ext == ".shd") { + if (ext == ".vs" || ext == ".fs" || ext == ".shd" || ext == ".c_shd") { // Exclude app->shader_directory for easier lookups. // e.g. app->shader_directory is "/shaders" and contains // "/shaders/my_shader.shd", the user needs to only reference it by: @@ -69,12 +71,12 @@ static void s_shader_watch_recursive(CF_Path path) for (int i = 0; i < dir.size(); ++i) { CF_Path p = app->shader_directory + path + dir[i]; if (p.is_directory()) { - s_shader_directory_recursive(p); + s_shader_watch_recursive(path + dir[i]); } else { CF_Stat stat; fs_stat(p, &stat); String ext = p.ext(); - if (ext == ".vs" || ext == ".fs" || ext == ".shd") { + if (ext == ".vs" || ext == ".fs" || ext == ".shd" || ext == ".c_shd") { const char* key = sintern(path + dir[i]); CF_ShaderFileInfo& info = app->shader_file_infos.find(key); if (info.stat.last_modified_time < stat.last_modified_time) { @@ -117,6 +119,7 @@ static CF_ShaderBytecode cf_compile_shader_to_bytecode_internal(const char* shad default: CF_ASSERT(false); break; // No valid stage provided. case CF_SHADER_STAGE_VERTEX: stage = CUTE_SHADER_STAGE_VERTEX; break; case CF_SHADER_STAGE_FRAGMENT: stage = CUTE_SHADER_STAGE_FRAGMENT; break; + case CF_SHADER_STAGE_COMPUTE: stage = CUTE_SHADER_STAGE_COMPUTE; break; } // Setup builtin includes @@ -233,6 +236,8 @@ void cf_unload_internal_shaders() void cf_destroy_shader(CF_Shader shader_handle) { + s_draw->shader_paths.remove(shader_handle.id); + // Draw shaders automatically have blit shaders generated, so clean that up as well, // if it exists. See `cf_make_draw_shader`. CF_Shader* blit = (CF_Shader*)s_draw->draw_shd_to_blit_shd.try_get(shader_handle.id); @@ -346,7 +351,7 @@ CF_TextureParams cf_texture_defaults(int w, int h) params.width = w; params.height = h; params.mip_count = 0; - params.generate_mipmaps = false; + params.allocate_mipmaps = false; params.mip_lod_bias = 0.0f; params.max_anisotropy = 1.0f; params.stream = false; @@ -458,11 +463,19 @@ void cf_material_set_texture_fs(CF_Material material_handle, const char* name, C s_material_set_texture(material, &material->fs, name, texture); } +void cf_material_set_texture_cs(CF_Material material_handle, const char* name, CF_Texture texture) +{ + CF_MaterialInternal* material = (CF_MaterialInternal*)material_handle.id; + name = sintern(name); + s_material_set_texture(material, &material->cs, name, texture); +} + void cf_material_clear_textures(CF_Material material_handle) { CF_MaterialInternal* material = (CF_MaterialInternal*)material_handle.id; material->vs.textures.clear(); material->fs.textures.clear(); + material->cs.textures.clear(); material->dirty = true; } @@ -492,11 +505,17 @@ static void s_material_set_uniform(CF_Arena* arena, CF_MaterialState* state, con CF_MEMCPY(uniform->data, data, size); } +static const char* s_uniform_block_name() +{ + static const char* s = sintern("uniform_block"); + return s; +} + void cf_material_set_uniform_vs(CF_Material material_handle, const char* name, void* data, CF_UniformType type, int array_length) { CF_MaterialInternal* material = (CF_MaterialInternal*)material_handle.id; name = sintern(name); - s_material_set_uniform(&material->uniform_arena, &material->vs, sintern("uniform_block"), name, data, type, array_length); + s_material_set_uniform(&material->uniform_arena, &material->vs, s_uniform_block_name(), name, data, type, array_length); } void cf_material_set_uniform_vs_internal(CF_Material material_handle, const char* block_name, const char* name, void* data, CF_UniformType type, int array_length) @@ -510,7 +529,7 @@ void cf_material_set_uniform_fs(CF_Material material_handle, const char* name, v { CF_MaterialInternal* material = (CF_MaterialInternal*)material_handle.id; name = sintern(name); - s_material_set_uniform(&material->uniform_arena, &material->fs, sintern("uniform_block"), name, data, type, array_length); + s_material_set_uniform(&material->uniform_arena, &material->fs, s_uniform_block_name(), name, data, type, array_length); } void cf_material_set_uniform_fs_internal(CF_Material material_handle, const char* block_name, const char* name, void* data, CF_UniformType type, int array_length) @@ -520,12 +539,20 @@ void cf_material_set_uniform_fs_internal(CF_Material material_handle, const char s_material_set_uniform(&material->uniform_arena, &material->fs, sintern(block_name), name, data, type, array_length); } +void cf_material_set_uniform_cs(CF_Material material_handle, const char* name, void* data, CF_UniformType type, int array_length) +{ + CF_MaterialInternal* material = (CF_MaterialInternal*)material_handle.id; + name = sintern(name); + s_material_set_uniform(&material->uniform_arena, &material->cs, s_uniform_block_name(), name, data, type, array_length); +} + void cf_material_clear_uniforms(CF_Material material_handle) { CF_MaterialInternal* material = (CF_MaterialInternal*)material_handle.id; arena_reset(&material->uniform_arena); material->vs.uniforms.clear(); material->fs.uniforms.clear(); + material->cs.uniforms.clear(); } void cf_clear_color(float red, float green, float blue, float alpha) @@ -557,6 +584,46 @@ CF_Shader cf_make_shader_from_source(const char* vertex_src, const char* fragmen return cf_make_shader_from_source_internal(vertex_src, fragment_src, NULL); } +CF_ComputeShader cf_make_compute_shader(const char* path) +{ + CF_Path p = CF_Path("/") + path; + const char* path_s = sintern(p); + CF_ShaderFileInfo* info = app->shader_file_infos.try_find(path_s); + if (!info) return { 0 }; + char* shd = fs_read_entire_file_to_memory_and_nul_terminate(info->path); + if (!shd) return { 0 }; + CF_ComputeShader result = cf_make_compute_shader_from_source(shd); + cf_free(shd); + if (result.id) s_compute_shader_paths.add(result.id, sintern(path)); + return result; +} + +CF_ComputeShader cf_make_compute_shader_from_source(const char* src) +{ + CF_ShaderBytecode bytecode = cf_compile_shader_to_bytecode_internal(src, CF_SHADER_STAGE_COMPUTE, NULL); + if (bytecode.content == NULL) { + CF_ComputeShader result = { 0 }; + return result; + } + CF_ComputeShader shader = cf_make_compute_shader_from_bytecode(bytecode); + cf_free_shader_bytecode(bytecode); + return shader; +} + +bool cf_compute_shader_reload(CF_ComputeShader* shader) +{ + const char** path_ptr = s_compute_shader_paths.try_find(shader->id); + if (!path_ptr) return false; + const char* path = *path_ptr; + + CF_ComputeShader new_shd = cf_make_compute_shader(path); + if (!new_shd.id) return false; + + cf_destroy_compute_shader(*shader); + *shader = new_shd; + return true; +} + //-------------------------------------------------------------------------------------------------- // Backend dispatch shims. @@ -617,6 +684,11 @@ CF_DISPATCH_SHIM(CF_Texture, canvas_get_target, (CF_Canvas canvas_handle), canva CF_DISPATCH_SHIM(CF_Texture, canvas_get_depth_stencil_target, (CF_Canvas canvas_handle), canvas_handle) CF_DISPATCH_SHIM_VOID(canvas_get_size, (CF_Canvas canvas_handle, int* w, int* h), canvas_handle, w, h) CF_DISPATCH_SHIM_VOID(clear_canvas, (CF_Canvas canvas_handle), canvas_handle) +CF_DISPATCH_SHIM(CF_Readback, canvas_readback, (CF_Canvas canvas), canvas) +CF_DISPATCH_SHIM(bool, readback_ready, (CF_Readback readback), readback) +CF_DISPATCH_SHIM(int, readback_data, (CF_Readback readback, void* data, int size), readback, data, size) +CF_DISPATCH_SHIM(int, readback_size, (CF_Readback readback), readback) +CF_DISPATCH_SHIM_VOID(destroy_readback, (CF_Readback readback), readback) CF_DISPATCH_SHIM_VOID(apply_canvas, (CF_Canvas canvas_handle, bool clear), canvas_handle, clear) CF_DISPATCH_SHIM_VOID(apply_viewport, (int x, int y, int w, int h), x, y, w, h) @@ -649,3 +721,30 @@ void cf_draw_elements() #endif } } + +CF_DISPATCH_SHIM(CF_ComputeShader, make_compute_shader_from_bytecode, (CF_ShaderBytecode bytecode), bytecode) + +void cf_sdlgpu_destroy_compute_shader(CF_ComputeShader shader); +void cf_gles_destroy_compute_shader(CF_ComputeShader shader); +void cf_destroy_compute_shader(CF_ComputeShader shader) +{ + s_compute_shader_paths.remove(shader.id); +#ifdef CF_EMSCRIPTEN + cf_gles_destroy_compute_shader(shader); +#else + if (app->gfx_backend_type == CF_BACKEND_TYPE_GLES3) { + cf_gles_destroy_compute_shader(shader); + } else { + cf_sdlgpu_destroy_compute_shader(shader); + } +#endif +} +CF_DISPATCH_SHIM(CF_StorageBuffer, make_storage_buffer, (CF_StorageBufferParams params), params) +CF_DISPATCH_SHIM_VOID(update_storage_buffer, (CF_StorageBuffer buffer, const void* data, int size), buffer, data, size) +CF_DISPATCH_SHIM_VOID(destroy_storage_buffer, (CF_StorageBuffer buffer), buffer) +CF_DISPATCH_SHIM_VOID(dispatch_compute, (CF_ComputeShader shader, CF_Material material, CF_ComputeDispatch dispatch), shader, material, dispatch) + +CF_DISPATCH_SHIM(void*, create_draw_sampler, (CF_Filter filter), filter) +CF_DISPATCH_SHIM_VOID(destroy_draw_sampler, (void* sampler), sampler) +CF_DISPATCH_SHIM_VOID(set_sampler_override, (void* sampler), sampler) +CF_DISPATCH_SHIM_VOID(gpu_sync, (), ) diff --git a/src/cute_graphics_gles.cpp b/src/cute_graphics_gles.cpp index d0a4b1179..721e18786 100644 --- a/src/cute_graphics_gles.cpp +++ b/src/cute_graphics_gles.cpp @@ -31,10 +31,9 @@ static inline GLenum s_wrap(CF_Filter f) static inline GLenum s_wrap(CF_MipFilter m, bool has_mips) { - if (!has_mips) return GL_NEAREST; // min filter without mips switch (m) { default: - case CF_MIP_FILTER_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; - case CF_MIP_FILTER_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; + case CF_MIP_FILTER_NEAREST: return has_mips ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST; + case CF_MIP_FILTER_LINEAR: return has_mips ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR; } } @@ -233,6 +232,8 @@ struct CF_GL_Shader int num_texture_bindings; CF_GL_TextureBinding* texture_bindings; + int num_vs_texture_bindings; + CF_GL_TextureBinding* vs_texture_bindings; CF_GL_ShaderInfo vs; CF_GL_ShaderInfo fs; }; @@ -348,6 +349,8 @@ static struct CF_GL_Mesh* mesh; CF_GL_Canvas* canvas; uint64_t enabled_vertex_attrib_mask; + CF_Filter filter_override; + bool has_filter_override; } g_ctx = { }; static inline void s_poll_error(const char* file, int line) @@ -487,7 +490,9 @@ static inline void s_apply_state() target->viewport.w != current->viewport.w || target->viewport.h != current->viewport.h ) { - glViewport(target->viewport.x, target->viewport.y, target->viewport.w, target->viewport.h); + // Flip viewport vertically since OpenGL puts (0, 0) at the bottom left. + CF_GL_Canvas* canvas = g_ctx.canvas; + glViewport(target->viewport.x, canvas->h - target->viewport.y - target->viewport.h, target->viewport.w, target->viewport.h); } if (target->scissor_enabled != current->scissor_enabled) { @@ -504,9 +509,9 @@ static inline void s_apply_state() target->scissor.w != current->scissor.w || target->scissor.h != current->scissor.h) ) { - // Flip the scissor rect vertically since OpenGL's puts (0, 0) at the bottom left. + // Flip the scissor rect vertically since OpenGL puts (0, 0) at the bottom left. CF_GL_Canvas* canvas = g_ctx.canvas; - glScissor(target->scissor.x, target->scissor.y, target->scissor.w, target->scissor.h); + glScissor(target->scissor.x, canvas->h - target->scissor.y - target->scissor.h, target->scissor.w, target->scissor.h); } if (target->stencil_reference != current->stencil_reference) { @@ -528,10 +533,10 @@ static inline void s_apply_state() target->blend_constants.a != current->blend_constants.a ) { glBlendColor( - current->blend_constants.r, - current->blend_constants.g, - current->blend_constants.b, - current->blend_constants.a + target->blend_constants.r, + target->blend_constants.g, + target->blend_constants.b, + target->blend_constants.a ); } @@ -616,8 +621,8 @@ static inline GLuint s_compile_shader(GLenum stage, const char* src, int src_len static inline GLuint s_make_program(CF_ShaderBytecode vs_bytecode, CF_ShaderBytecode fs_bytecode) { - GLuint vs = s_compile_shader(GL_VERTEX_SHADER, vs_bytecode.glsl300_src, vs_bytecode.glsl300_src_size); - GLuint fs = s_compile_shader(GL_FRAGMENT_SHADER, fs_bytecode.glsl300_src, fs_bytecode.glsl300_src_size); + GLuint vs = s_compile_shader(GL_VERTEX_SHADER, vs_bytecode.glsl300_src, (int)vs_bytecode.glsl300_src_size); + GLuint fs = s_compile_shader(GL_FRAGMENT_SHADER, fs_bytecode.glsl300_src, (int)fs_bytecode.glsl300_src_size); GLuint program = glCreateProgram(); glAttachShader(program, vs); @@ -683,7 +688,8 @@ void cf_gles_attach(SDL_Window* window) bool cf_gles_supports_msaa(int sample_count) { - CF_ASSERT(!"This is only implemented on SDL_Gpu backends (Metal/DX11/DX12/Vulkan)."); + // GLES backend does not support MSAA. + CF_UNUSED(sample_count); return false; } @@ -692,6 +698,11 @@ void cf_gles_flush() glFlush(); } +void cf_gles_gpu_sync() +{ + glFinish(); +} + void cf_gles_set_vsync(bool true_turn_on_vsync) { SDL_GL_SetSwapInterval(true_turn_on_vsync ? 1 : 0); @@ -801,7 +812,7 @@ static inline void s_apply_sampler_params(CF_GL_Texture* t, const CF_TexturePara s_load_format_caps(); CF_GL_PixelFormatInfo* info = s_find_pixel_format_info(p.pixel_format); uint32_t caps = info ? info->caps : 0; - t->has_mips = p.generate_mipmaps || p.mip_count > 1; + t->has_mips = p.allocate_mipmaps || p.mip_count > 1; GLenum min_filter = s_wrap(p.mip_filter, t->has_mips); GLenum mag_filter = s_wrap(p.filter); if (!(caps & CF_GL_FMT_CAP_LINEAR)) { @@ -918,7 +929,7 @@ CF_Texture cf_gles_make_texture(CF_TextureParams params) t->id = slot->handle; t->active_slot = slot_index; s_apply_sampler_params(t, params); - if (params.generate_mipmaps) { + if (params.allocate_mipmaps) { glBindTexture(GL_TEXTURE_2D, t->id); glGenerateMipmap(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); @@ -1083,7 +1094,7 @@ CF_Texture cf_gles_canvas_get_target(CF_Canvas ch) CF_Texture cf_gles_canvas_get_depth_stencil_target(CF_Canvas) { - // TODO: Implement + // GLES uses renderbuffers for depth/stencil, which are not sampleable as textures. return CF_Texture{}; } @@ -1318,6 +1329,7 @@ CF_Shader cf_gles_make_shader_from_bytecode(CF_ShaderBytecode vertex_bytecode, C CF_GL_Shader* shader = (CF_GL_Shader*)CF_CALLOC(sizeof(CF_GL_Shader)); shader->program = program; + // FS texture bindings. shader->texture_bindings = (CF_GL_TextureBinding*)CF_ALLOC(sizeof(CF_GL_TextureBinding) * fragment_bytecode.shader_info.num_images); for (int image_index = 0; image_index < fragment_bytecode.shader_info.num_images; ++image_index) { // GLES 3.00 and WebGL do not support explicit binding (i.e: layout(binding = x)) @@ -1331,6 +1343,18 @@ CF_Shader cf_gles_make_shader_from_bytecode(CF_ShaderBytecode vertex_bytecode, C ++shader->num_texture_bindings; } + // VS texture bindings. + shader->vs_texture_bindings = (CF_GL_TextureBinding*)CF_ALLOC(sizeof(CF_GL_TextureBinding) * vertex_bytecode.shader_info.num_images); + for (int image_index = 0; image_index < vertex_bytecode.shader_info.num_images; ++image_index) { + const char* image_name = vertex_bytecode.shader_info.image_names[image_index]; + GLint location = glGetUniformLocation(program, image_name); + if (location < 0) { continue; } + + shader->vs_texture_bindings[shader->num_vs_texture_bindings].location = location; + shader->vs_texture_bindings[shader->num_vs_texture_bindings].name = cf_sintern(image_name); + ++shader->num_vs_texture_bindings; + } + GLuint binding_point = 0; s_build_uniforms(program, &shader->vs, &vertex_bytecode.shader_info, &binding_point); s_build_uniforms(program, &shader->fs, &fragment_bytecode.shader_info, &binding_point); @@ -1352,6 +1376,7 @@ void cf_gles_destroy_shader_internal(CF_Shader shader_handle) CF_FREE(shader->vs.uniform_members); CF_FREE(shader->fs.uniform_members); CF_FREE(shader->texture_bindings); + CF_FREE(shader->vs_texture_bindings); glDeleteProgram(shader->program); shader->~CF_GL_Shader(); CF_FREE(shader); @@ -1585,7 +1610,6 @@ void cf_gles_apply_shader(CF_Shader shader_handle, CF_Material material_handle) // Textures (FS). GLuint texture_unit = 0; for (int texture_index = 0; texture_index < material->fs.textures.count(); ++texture_index) { - const char* name = material->fs.textures[texture_index].name; CF_MaterialTex material_tex = material->fs.textures[texture_index]; CF_GL_Texture* texture = (CF_GL_Texture*)(uintptr_t)material_tex.handle.id; for (int image_index = 0; image_index < shader->num_texture_bindings; ++image_index) { @@ -1593,12 +1617,40 @@ void cf_gles_apply_shader(CF_Shader shader_handle, CF_Material material_handle) if (binding->name == material_tex.name) { glActiveTexture(GL_TEXTURE0 + texture_unit); glBindTexture(GL_TEXTURE_2D, texture->id); + if (g_ctx.has_filter_override) { + GLenum gl_filter = (g_ctx.filter_override == CF_FILTER_NEAREST) ? GL_NEAREST : GL_LINEAR; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter); + } + glUniform1i(binding->location, texture_unit); + ++texture_unit; + break; + } + } + } + + // Textures (VS). + for (int texture_index = 0; texture_index < material->vs.textures.count(); ++texture_index) { + CF_MaterialTex material_tex = material->vs.textures[texture_index]; + CF_GL_Texture* texture = (CF_GL_Texture*)(uintptr_t)material_tex.handle.id; + for (int image_index = 0; image_index < shader->num_vs_texture_bindings; ++image_index) { + const CF_GL_TextureBinding* binding = &shader->vs_texture_bindings[image_index]; + if (binding->name == material_tex.name) { + glActiveTexture(GL_TEXTURE0 + texture_unit); + glBindTexture(GL_TEXTURE_2D, texture->id); + if (g_ctx.has_filter_override) { + GLenum gl_filter = (g_ctx.filter_override == CF_FILTER_NEAREST) ? GL_NEAREST : GL_LINEAR; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter); + } glUniform1i(binding->location, texture_unit); ++texture_unit; break; } } } + + g_ctx.has_filter_override = false; CF_POLL_OPENGL_ERROR(); CF_GL_Mesh* mesh = g_ctx.mesh; @@ -1650,6 +1702,13 @@ void cf_gles_draw_elements() s_set_slot_fence(texture->ring.slots[texture->active_slot]); } } + for (int texture_index = 0; texture_index < material->vs.textures.count(); ++texture_index) { + CF_GL_Texture* texture = (CF_GL_Texture*)(uintptr_t)material->vs.textures[texture_index].handle.id; + if (!texture) continue; + if (texture->active_slot >= 0 && texture->active_slot < texture->ring.count) { + s_set_slot_fence(texture->ring.slots[texture->active_slot]); + } + } CF_POLL_OPENGL_ERROR(); ++app->draw_call_count; @@ -1676,3 +1735,98 @@ void cf_gles_apply_blend_constants(float r, float g, float b, float a) { g_ctx.target_state.blend_constants = { r, g, b, a }; } + +// For GLES, samplers are just stored filter values since GLES sets filter mode directly on textures. +void* cf_gles_create_draw_sampler(CF_Filter filter) +{ + // Store the filter value as a pointer (we only need NEAREST=0 or LINEAR=1). + return (void*)(uintptr_t)filter; +} + +void cf_gles_destroy_draw_sampler(void* sampler) +{ + // Nothing to do for GLES - filter values don't need cleanup. + CF_UNUSED(sampler); +} + +void cf_gles_set_sampler_override(void* sampler) +{ + if (sampler) { + g_ctx.filter_override = (CF_Filter)(uintptr_t)sampler; + g_ctx.has_filter_override = true; + } else { + g_ctx.has_filter_override = false; + } +} + +//-------------------------------------------------------------------------------------------------- +// Compute stubs (not supported on GLES3). + +CF_ComputeShader cf_gles_make_compute_shader_from_bytecode(CF_ShaderBytecode bytecode) +{ + CF_UNUSED(bytecode); + CF_ComputeShader result = { 0 }; + return result; +} + +void cf_gles_destroy_compute_shader(CF_ComputeShader shader) +{ + CF_UNUSED(shader); +} + +CF_StorageBuffer cf_gles_make_storage_buffer(CF_StorageBufferParams params) +{ + CF_UNUSED(params); + CF_StorageBuffer result = { 0 }; + return result; +} + +void cf_gles_update_storage_buffer(CF_StorageBuffer buffer, const void* data, int size) +{ + CF_UNUSED(buffer); + CF_UNUSED(data); + CF_UNUSED(size); +} + +void cf_gles_destroy_storage_buffer(CF_StorageBuffer buffer) +{ + CF_UNUSED(buffer); +} + +void cf_gles_dispatch_compute(CF_ComputeShader shader, CF_Material material, CF_ComputeDispatch dispatch) +{ + CF_UNUSED(shader); + CF_UNUSED(material); + CF_UNUSED(dispatch); +} + +CF_Readback cf_gles_canvas_readback(CF_Canvas canvas) +{ + CF_UNUSED(canvas); + return { 0 }; +} + +bool cf_gles_readback_ready(CF_Readback readback) +{ + CF_UNUSED(readback); + return false; +} + +int cf_gles_readback_data(CF_Readback readback, void* data, int size) +{ + CF_UNUSED(readback); + CF_UNUSED(data); + CF_UNUSED(size); + return 0; +} + +int cf_gles_readback_size(CF_Readback readback) +{ + CF_UNUSED(readback); + return 0; +} + +void cf_gles_destroy_readback(CF_Readback readback) +{ + CF_UNUSED(readback); +} diff --git a/src/cute_graphics_sdlgpu.cpp b/src/cute_graphics_sdlgpu.cpp index 4ef09e481..73c6fce1e 100644 --- a/src/cute_graphics_sdlgpu.cpp +++ b/src/cute_graphics_sdlgpu.cpp @@ -47,12 +47,45 @@ struct CF_TextureInternal SDL_GPUTextureSamplerBinding binding; }; +struct CF_ReadbackInternal +{ + SDL_GPUTransferBuffer* transfer_buffer; + SDL_GPUFence* fence; + int w, h; + int size; // w * h * texel_size + bool ready; +}; + +// Content-based pipeline cache key. All fields that affect pipeline creation. +// Zeroed before filling so memcmp is safe even with padding. +struct CF_PipelineKey +{ + // Canvas state. + SDL_GPUTextureFormat color_format; + SDL_GPUTextureFormat depth_format; + bool has_depth_stencil; + int sample_count; + + // Material render state. + CF_RenderState render_state; + + // Vertex layout (matched attributes in shader input order). + int vertex_stride; + int instance_stride; + int attribute_count; + struct + { + int location; + CF_VertexFormat format; + int offset; + bool per_instance; + } attrs[CF_MAX_SHADER_INPUTS]; +}; + struct CF_Pipeline { - int sample_count = 0; - CF_MaterialInternal* material = NULL; - SDL_GPUGraphicsPipeline* pip = NULL; - CF_MeshInternal* mesh = NULL; + CF_PipelineKey key; + SDL_GPUGraphicsPipeline* pip; }; struct CF_ShaderInternal @@ -69,7 +102,10 @@ struct CF_ShaderInternal int fs_block_sizes[CF_MAX_UNIFORM_BLOCK_COUNT]; Cute::Array fs_uniform_block_members[CF_MAX_UNIFORM_BLOCK_COUNT]; Cute::Array vs_uniform_block_members[CF_MAX_UNIFORM_BLOCK_COUNT]; - Cute::Array image_names; + Cute::Array vs_image_names; + Cute::Array vs_image_binding_slots; + Cute::Array fs_image_names; + Cute::Array fs_image_binding_slots; Cute::Array pip_cache; CF_INLINE int get_input_index(const char* name) @@ -125,8 +161,18 @@ static struct bool skip_drawing; int msaa_sample_count; CF_CanvasInternal* canvas; + SDL_GPUSampler* sampler_override; + SDL_GPURenderPass* active_pass; } g_ctx = { }; +static inline void s_end_active_pass() { + if (g_ctx.active_pass) { + SDL_EndGPURenderPass(g_ctx.active_pass); + g_ctx.active_pass = NULL; + if (g_ctx.canvas) g_ctx.canvas->pass = NULL; + } +} + CF_INLINE SDL_GPUTextureCreateInfo SDL_GPUTextureCreateInfoDefaults(int w, int h) { SDL_GPUTextureCreateInfo createInfo; @@ -156,7 +202,7 @@ CF_INLINE SDL_GPUSamplerCreateInfo SDL_GPUSamplerCreateInfoDefaults() samplerInfo.enable_anisotropy = false; samplerInfo.max_anisotropy = 1.0f; samplerInfo.enable_compare = false; - samplerInfo.compare_op = SDL_GPU_COMPAREOP_ALWAYS; + samplerInfo.compare_op = SDL_GPU_COMPAREOP_NEVER; samplerInfo.min_lod = 0.0f; samplerInfo.max_lod = FLT_MAX; return samplerInfo; @@ -487,7 +533,7 @@ static inline CF_Texture s_make_texture(CF_TextureParams params, CF_SampleCount tex_info.usage = sample_count == CF_SAMPLE_COUNT_1 ? params.usage : (params.usage & ~(SDL_GPU_TEXTUREUSAGE_SAMPLER)); tex_info.sample_count = (SDL_GPUSampleCount)sample_count; - if (params.generate_mipmaps) { + if (params.allocate_mipmaps) { tex_info.num_levels = params.mip_count > 0 ? (Uint32)params.mip_count : (Uint32)(1 + (int)CF_FLOORF(CF_LOG2F((float)cf_max(params.width, params.height)))); @@ -551,8 +597,16 @@ static inline SDL_GPUShader* s_load_shader_bytecode(CF_ShaderInternal* shader_in // Load reflection info const CF_ShaderInfo* shader_info = &bytecode.shader_info; - for (int i = 0; i < shader_info->num_images; ++i) { - shader_internal->image_names.add(sintern(shader_info->image_names[i])); + if (vs) { + for (int i = 0; i < shader_info->num_images; ++i) { + shader_internal->vs_image_names.add(sintern(shader_info->image_names[i])); + shader_internal->vs_image_binding_slots.add(shader_info->image_binding_slots ? shader_info->image_binding_slots[i] : i); + } + } else { + for (int i = 0; i < shader_info->num_images; ++i) { + shader_internal->fs_image_names.add(sintern(shader_info->image_names[i])); + shader_internal->fs_image_binding_slots.add(shader_info->image_binding_slots ? shader_info->image_binding_slots[i] : i); + } } if (stage == CF_SHADER_STAGE_VERTEX) { @@ -635,11 +689,58 @@ static inline SDL_GPUShader* s_load_shader_bytecode(CF_ShaderInternal* shader_in spirvInfo.name = "shader.shd"; spirvInfo.props = SDL_CreateProperties(); sdl_shader = (SDL_GPUShader*)SDL_ShaderCross_CompileGraphicsShaderFromSPIRV(g_ctx.device, &spirvInfo, &metadata); + SDL_DestroyProperties(spirvInfo.props); } CF_ASSERT(sdl_shader); return sdl_shader; } +#ifdef _WIN32 +#include +#include "d3d12_layout.gen.h" + +// Suppress D3D12 debug warning #820 (CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE). +// We want to suppress this because CF is designed where the clear value can be set by the +// user at pretty much anytime, so our design conflicts with D3D12's expectation, but, you know, +// fuck you Microsoft, we do what we want. +// +// SDL3 GPU doesn't expose the native ID3D12Device* publicly, so we reach into +// private struct layouts using offsets computed at build time by gen_d3d12_layout. This is +// another case of someone tryna cramp our style. So yeah, fuck you SDL, we do what we want. + +static void s_silence_d3d12_clear_value_warning(SDL_GPUDevice* gpu_device) +{ + if (SDL_strcmp(SDL_GetGPUDeviceDriver(gpu_device), "direct3d12") != 0) return; + + // SDL_GPUDevice (SDL_sysgpu.h): N function pointer slots, then driverData, then backend. + void** device_slots = (void**)gpu_device; + void* driver_data = device_slots[SDL_GPU_DEVICE_FN_SLOT_COUNT]; + + // Sanity: next slot should be the backend string. + const char* backend = (const char*)device_slots[SDL_GPU_DEVICE_FN_SLOT_COUNT + 1]; + if (SDL_strcmp(backend, "direct3d12") != 0) return; + + // Sanity: D3D12Renderer's first field is a back-pointer to SDL_GPUDevice. + void** renderer_slots = (void**)driver_data; + if (renderer_slots[0] != gpu_device) return; + + // Access ID3D12Device* at build-time-computed byte offset. + ID3D12Device* d3d_device = *(ID3D12Device**)((char*)driver_data + D3D12_RENDERER_DEVICE_BYTE_OFFSET); + + ID3D12InfoQueue* info_queue = NULL; + if (SUCCEEDED(d3d_device->QueryInterface(IID_PPV_ARGS(&info_queue)))) { + D3D12_MESSAGE_ID hide[] = { + D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, + }; + D3D12_INFO_QUEUE_FILTER filter = {}; + filter.DenyList.NumIDs = _countof(hide); + filter.DenyList.pIDList = hide; + info_queue->AddStorageFilterEntries(&filter); + info_queue->Release(); + } +} +#endif + CF_Result cf_sdlgpu_init(const char* device_name, bool debug, CF_BackendType* backend_type) { if(!SDL_ShaderCross_Init()) { @@ -650,6 +751,12 @@ CF_Result cf_sdlgpu_init(const char* device_name, bool debug, CF_BackendType* ba return cf_result_error("Failed to create GPU Device."); } +#ifdef _WIN32 + if (debug) { + s_silence_d3d12_clear_value_warning(g_ctx.device); + } +#endif + *backend_type = s_query_backend(); return cf_result_success(); @@ -657,6 +764,11 @@ CF_Result cf_sdlgpu_init(const char* device_name, bool debug, CF_BackendType* ba void cf_sdlgpu_cleanup() { + if (g_ctx.cmd) { + SDL_SubmitGPUCommandBuffer(g_ctx.cmd); + g_ctx.cmd = NULL; + } + SDL_WaitForGPUIdle(g_ctx.device); SDL_ReleaseWindowFromGPUDevice(g_ctx.device, g_ctx.window); SDL_DestroyGPUDevice(g_ctx.device); SDL_ShaderCross_Quit(); @@ -688,8 +800,7 @@ void cf_sdlgpu_attach(SDL_Window* window) bool cf_sdlgpu_supports_msaa(int sample_count) { SDL_GPUTextureFormat fmt = SDL_GetGPUSwapchainTextureFormat(g_ctx.device, g_ctx.window); - SDL_GPUSampleCount msaa = (SDL_GPUSampleCount)cf_clamp((g_ctx.msaa_sample_count >> 1), 0, 3); - return SDL_GPUTextureSupportsSampleCount(g_ctx.device, fmt, msaa); + return SDL_GPUTextureSupportsSampleCount(g_ctx.device, fmt, (SDL_GPUSampleCount)sample_count); } void cf_sdlgpu_flush() @@ -701,6 +812,17 @@ void cf_sdlgpu_flush() } } +void cf_sdlgpu_gpu_sync() +{ + s_end_active_pass(); + if (g_ctx.cmd) { + SDL_GPUFence *fence = SDL_SubmitGPUCommandBufferAndAcquireFence(g_ctx.cmd); + SDL_WaitForGPUFences(g_ctx.device, true, &fence, 1); + SDL_ReleaseGPUFence(g_ctx.device, fence); + g_ctx.cmd = SDL_AcquireGPUCommandBuffer(g_ctx.device); + } +} + void cf_sdlgpu_set_vsync(bool vsync) { SDL_SetGPUSwapchainParameters(g_ctx.device, g_ctx.window, SDL_GPU_SWAPCHAINCOMPOSITION_SDR, vsync ? SDL_GPU_PRESENTMODE_VSYNC : SDL_GPU_PRESENTMODE_IMMEDIATE); @@ -719,6 +841,7 @@ void cf_sdlgpu_begin_frame() void cf_sdlgpu_blit_canvas(CF_Canvas canvas) { + s_end_active_pass(); // Try to acquire a swapchain texture if (g_ctx.swapchain_tex == NULL && !g_ctx.skip_drawing) { if ( @@ -757,6 +880,7 @@ void cf_sdlgpu_blit_canvas(CF_Canvas canvas) void cf_sdlgpu_end_frame() { + s_end_active_pass(); SDL_SubmitGPUCommandBuffer(g_ctx.cmd); g_ctx.cmd = NULL; g_ctx.canvas = NULL; @@ -809,6 +933,7 @@ void cf_sdlgpu_destroy_texture(CF_Texture texture_handle) void cf_sdlgpu_texture_update(CF_Texture texture_handle, void* data, int size) { + s_end_active_pass(); CF_TextureInternal* tex = (CF_TextureInternal*)texture_handle.id; // Copy bytes over to the driver. @@ -831,8 +956,9 @@ void cf_sdlgpu_texture_update(CF_Texture texture_handle, void* data, int size) SDL_GPUTextureTransferInfo src; src.transfer_buffer = buf; src.offset = 0; - src.pixels_per_row = tex->w; - src.rows_per_layer = tex->h; + // SDL_GPU: 0 means tightly packed, inferred from the texture region dimensions. + src.pixels_per_row = 0; + src.rows_per_layer = 0; SDL_GPUTextureRegion dst = SDL_GPUTextureRegionDefaults(tex, tex->w, tex->h); SDL_UploadToGPUTexture(pass, &src, &dst, true); SDL_EndGPUCopyPass(pass); @@ -842,6 +968,7 @@ void cf_sdlgpu_texture_update(CF_Texture texture_handle, void* data, int size) void cf_sdlgpu_texture_update_mip(CF_Texture texture_handle, void* data, int size, int mip_level) { + s_end_active_pass(); CF_TextureInternal* tex = (CF_TextureInternal*)texture_handle.id; // Create a temporary transfer buffer if needed. @@ -870,8 +997,9 @@ void cf_sdlgpu_texture_update_mip(CF_Texture texture_handle, void* data, int siz SDL_GPUTextureTransferInfo src; src.transfer_buffer = buf; src.offset = 0; - src.pixels_per_row = w; - src.rows_per_layer = h; + // SDL_GPU: 0 means tightly packed, inferred from the texture region dimensions. + src.pixels_per_row = 0; + src.rows_per_layer = 0; SDL_GPUTextureRegion dst = SDL_GPUTextureRegionDefaults(tex, w, h); dst.mip_level = (Uint32)mip_level; SDL_UploadToGPUTexture(pass, &src, &dst, true); @@ -882,6 +1010,7 @@ void cf_sdlgpu_texture_update_mip(CF_Texture texture_handle, void* data, int siz void cf_sdlgpu_generate_mipmaps(CF_Texture texture_handle) { + s_end_active_pass(); CF_TextureInternal* tex = (CF_TextureInternal*)texture_handle.id; SDL_GPUCommandBuffer* cmd = g_ctx.cmd ? g_ctx.cmd : SDL_AcquireGPUCommandBuffer(g_ctx.device); SDL_GenerateMipmapsForGPUTexture(cmd, tex->tex); @@ -971,6 +1100,7 @@ void cf_sdlgpu_canvas_get_size(CF_Canvas canvas_handle, int* w, int* h) void cf_sdlgpu_clear_canvas(CF_Canvas canvas_handle) { + s_end_active_pass(); CF_CanvasInternal* canvas = (CF_CanvasInternal*)canvas_handle.id; SDL_GPUCommandBuffer* cmd = g_ctx.cmd ? g_ctx.cmd : SDL_AcquireGPUCommandBuffer(g_ctx.device); @@ -1019,6 +1149,118 @@ CF_Texture cf_sdlgpu_canvas_get_depth_stencil_target(CF_Canvas canvas_handle) return canvas->cf_depth_stencil; } +CF_Readback cf_sdlgpu_canvas_readback(CF_Canvas canvas_handle) +{ + CF_CanvasInternal* canvas = (CF_CanvasInternal*)canvas_handle.id; + if (!canvas) return { 0 }; + + s_end_active_pass(); + + // Pick source texture: resolve_texture for MSAA canvases, texture for non-MSAA. + SDL_GPUTexture* src_texture = canvas->resolve_texture ? canvas->resolve_texture : canvas->texture; + CF_TextureInternal* tex_internal = canvas->resolve_texture + ? (CF_TextureInternal*)canvas->cf_resolve_texture.id + : (CF_TextureInternal*)canvas->cf_texture.id; + + int texel_size = (int)SDL_GPUTextureFormatTexelBlockSize(tex_internal->format); + int total_size = canvas->w * canvas->h * texel_size; + + // Create a download transfer buffer. + SDL_GPUTransferBufferCreateInfo tbuf_info = { + .usage = SDL_GPU_TRANSFERBUFFERUSAGE_DOWNLOAD, + .size = (Uint32)total_size, + .props = 0, + }; + SDL_GPUTransferBuffer* transfer_buffer = SDL_CreateGPUTransferBuffer(g_ctx.device, &tbuf_info); + if (!transfer_buffer) return { 0 }; + + // Acquire a separate command buffer for the readback (not g_ctx.cmd). + SDL_GPUCommandBuffer* cmd = SDL_AcquireGPUCommandBuffer(g_ctx.device); + if (!cmd) { + SDL_ReleaseGPUTransferBuffer(g_ctx.device, transfer_buffer); + return { 0 }; + } + + // Begin copy pass, download texture, end copy pass. + SDL_GPUCopyPass* copy_pass = SDL_BeginGPUCopyPass(cmd); + SDL_GPUTextureTransferInfo dst = { }; + dst.transfer_buffer = transfer_buffer; + dst.offset = 0; + SDL_GPUTextureRegion src = { }; + src.texture = src_texture; + src.w = (Uint32)canvas->w; + src.h = (Uint32)canvas->h; + src.d = 1; + SDL_DownloadFromGPUTexture(copy_pass, &src, &dst); + SDL_EndGPUCopyPass(copy_pass); + + // Submit and acquire fence. + SDL_GPUFence* fence = SDL_SubmitGPUCommandBufferAndAcquireFence(cmd); + if (!fence) { + SDL_ReleaseGPUTransferBuffer(g_ctx.device, transfer_buffer); + return { 0 }; + } + + // Allocate internal struct. + CF_ReadbackInternal* rb = (CF_ReadbackInternal*)CF_CALLOC(sizeof(CF_ReadbackInternal)); + rb->transfer_buffer = transfer_buffer; + rb->fence = fence; + rb->w = canvas->w; + rb->h = canvas->h; + rb->size = total_size; + rb->ready = false; + + CF_Readback result; + result.id = (uint64_t)rb; + return result; +} + +bool cf_sdlgpu_readback_ready(CF_Readback readback) +{ + CF_ReadbackInternal* rb = (CF_ReadbackInternal*)readback.id; + if (!rb) return false; + if (rb->ready) return true; + rb->ready = SDL_QueryGPUFence(g_ctx.device, rb->fence); + return rb->ready; +} + +int cf_sdlgpu_readback_data(CF_Readback readback, void* data, int size) +{ + CF_ReadbackInternal* rb = (CF_ReadbackInternal*)readback.id; + if (!rb || !data || size <= 0) return 0; + if (!rb->ready) return 0; + + void* mapped = SDL_MapGPUTransferBuffer(g_ctx.device, rb->transfer_buffer, false); + if (!mapped) return 0; + + int bytes = size < rb->size ? size : rb->size; + CF_MEMCPY(data, mapped, bytes); + SDL_UnmapGPUTransferBuffer(g_ctx.device, rb->transfer_buffer); + return bytes; +} + +int cf_sdlgpu_readback_size(CF_Readback readback) +{ + CF_ReadbackInternal* rb = (CF_ReadbackInternal*)readback.id; + if (!rb) return 0; + return rb->size; +} + +void cf_sdlgpu_destroy_readback(CF_Readback readback) +{ + CF_ReadbackInternal* rb = (CF_ReadbackInternal*)readback.id; + if (!rb) return; + + // If not ready, block until the GPU finishes. + if (!rb->ready) { + SDL_WaitForGPUFences(g_ctx.device, true, &rb->fence, 1); + } + + SDL_ReleaseGPUFence(g_ctx.device, rb->fence); + SDL_ReleaseGPUTransferBuffer(g_ctx.device, rb->transfer_buffer); + CF_FREE(rb); +} + CF_Mesh cf_sdlgpu_make_mesh(int vertex_buffer_size, const CF_VertexAttribute* attributes, int attribute_count, int vertex_stride) { CF_MeshInternal* mesh = (CF_MeshInternal*)CF_CALLOC(sizeof(CF_MeshInternal)); @@ -1107,6 +1349,8 @@ void cf_sdlgpu_destroy_mesh(CF_Mesh mesh_handle) static inline void s_update_buffer(CF_Buffer* buffer, int element_count, void* data, int size, SDL_GPUBufferUsageFlags flags) { + s_end_active_pass(); + // Resize buffer if necessary. if (size > buffer->size) { SDL_ReleaseGPUBuffer(g_ctx.device, buffer->buffer); @@ -1173,6 +1417,10 @@ void cf_sdlgpu_apply_canvas(CF_Canvas canvas_handle, bool clear) { CF_CanvasInternal* canvas = (CF_CanvasInternal*)canvas_handle.id; CF_ASSERT(canvas); + // End any active pass if switching canvases or requesting a clear. + if (g_ctx.active_pass && (canvas != g_ctx.canvas || clear)) { + s_end_active_pass(); + } g_ctx.canvas = canvas; g_ctx.canvas->clear = clear; } @@ -1302,13 +1550,11 @@ static inline SDL_GPUGraphicsPipeline* s_build_pipeline(CF_ShaderInternal* shade pip_info.target_info.color_target_descriptions = &color_info; pip_info.vertex_shader = shader->vs; pip_info.fragment_shader = shader->fs; - const bool has_depth_stencil_texture = g_ctx.canvas->depth_stencil != NULL; - const bool depth_test_requested = state->depth_write_enabled || state->depth_compare != CF_COMPARE_FUNCTION_ALWAYS; - CF_TextureInternal* depth_texture = has_depth_stencil_texture ? (CF_TextureInternal*)g_ctx.canvas->cf_depth_stencil.id : NULL; - const bool depth_test_enabled = (depth_texture != NULL) && depth_test_requested; - const bool stencil_capable = s_texture_supports_stencil(depth_texture); - const bool stencil_test_enabled = stencil_capable && state->stencil.enabled; - if (depth_texture && (depth_test_enabled || stencil_test_enabled)) { + // Always declare depth-stencil target if the canvas has one, regardless of whether + // the material enables depth/stencil testing. This ensures all pipelines are compatible + // with the render pass (which always includes the depth-stencil attachment). + CF_TextureInternal* depth_texture = g_ctx.canvas->depth_stencil ? (CF_TextureInternal*)g_ctx.canvas->cf_depth_stencil.id : NULL; + if (depth_texture) { pip_info.target_info.depth_stencil_format = depth_texture->format; pip_info.target_info.has_depth_stencil_target = true; } @@ -1349,7 +1595,8 @@ static inline SDL_GPUGraphicsPipeline* s_build_pipeline(CF_ShaderInternal* shade vertex_buffer_descriptions_count++; } if (has_instance_data) { - vertex_buffer_descriptions[vertex_buffer_descriptions_count].slot = 1; + int instance_slot = has_vertex_data ? 1 : 0; + vertex_buffer_descriptions[vertex_buffer_descriptions_count].slot = instance_slot; vertex_buffer_descriptions[vertex_buffer_descriptions_count].pitch = mesh->instances.stride; vertex_buffer_descriptions[vertex_buffer_descriptions_count].input_rate = SDL_GPU_VERTEXINPUTRATE_INSTANCE; vertex_buffer_descriptions[vertex_buffer_descriptions_count].instance_step_rate = 0; @@ -1369,6 +1616,10 @@ static inline SDL_GPUGraphicsPipeline* s_build_pipeline(CF_ShaderInternal* shade pip_info.multisample_state.sample_count = (SDL_GPUSampleCount)g_ctx.canvas->sample_count; pip_info.multisample_state.sample_mask = 0; + bool depth_test_requested = state->depth_write_enabled || state->depth_compare != CF_COMPARE_FUNCTION_ALWAYS; + bool depth_test_enabled = (depth_texture != NULL) && depth_test_requested; + bool stencil_capable = depth_texture ? s_texture_supports_stencil(depth_texture) : false; + bool stencil_test_enabled = stencil_capable && state->stencil.enabled; pip_info.depth_stencil_state.enable_depth_test = depth_test_enabled; pip_info.depth_stencil_state.enable_depth_write = (depth_texture != NULL) && state->depth_write_enabled; pip_info.depth_stencil_state.compare_op = s_wrap(state->depth_compare); @@ -1389,6 +1640,45 @@ static inline SDL_GPUGraphicsPipeline* s_build_pipeline(CF_ShaderInternal* shade return pip; } +static CF_PipelineKey s_make_pipeline_key(CF_RenderState* state, CF_MeshInternal* mesh, CF_ShaderInternal* shader) +{ + CF_PipelineKey key; + CF_MEMSET(&key, 0, sizeof(key)); + + // Canvas state. + CF_TextureInternal* tex = (CF_TextureInternal*)g_ctx.canvas->cf_texture.id; + key.color_format = tex->format; + key.sample_count = (int)g_ctx.canvas->sample_count; + CF_TextureInternal* depth_texture = g_ctx.canvas->depth_stencil ? (CF_TextureInternal*)g_ctx.canvas->cf_depth_stencil.id : NULL; + if (depth_texture) { + key.has_depth_stencil = true; + key.depth_format = depth_texture->format; + } + + // Material render state (copied field-by-field to avoid padding issues, but + // CF_RenderState is fully zeroed by cf_render_state_defaults, so direct copy is safe). + key.render_state = *state; + + // Vertex layout. + bool has_vertex_data = mesh->vertices.buffer != NULL; + key.vertex_stride = has_vertex_data ? mesh->vertices.stride : 0; + key.instance_stride = mesh->instances.buffer ? mesh->instances.stride : 0; + int attr_count = 0; + for (int i = 0; i < mesh->attribute_count && attr_count < CF_MAX_SHADER_INPUTS; ++i) { + int idx = shader->get_input_index(mesh->attributes[i].name); + if (idx >= 0) { + key.attrs[attr_count].location = shader->input_locations[idx]; + key.attrs[attr_count].format = mesh->attributes[i].format; + key.attrs[attr_count].offset = mesh->attributes[i].offset; + key.attrs[attr_count].per_instance = mesh->attributes[i].per_instance; + attr_count++; + } + } + key.attribute_count = attr_count; + + return key; +} + void cf_sdlgpu_apply_shader(CF_Shader shader_handle, CF_Material material_handle) { CF_ASSERT(g_ctx.canvas); @@ -1398,82 +1688,81 @@ void cf_sdlgpu_apply_shader(CF_Shader shader_handle, CF_Material material_handle CF_ShaderInternal* shader = (CF_ShaderInternal*)shader_handle.id; CF_RenderState* state = &material->state; - // Cache the pipeline to avoid create/release each frame. - // ...Build a new one if the material marks itself as dirty. + // Content-based pipeline cache lookup. + CF_PipelineKey key = s_make_pipeline_key(state, mesh, shader); SDL_GPUGraphicsPipeline* pip = NULL; - bool found = false; for (int i = 0; i < shader->pip_cache.count(); ++i) { - CF_Pipeline pip_cache = shader->pip_cache[i]; - if (pip_cache.material == material && - pip_cache.mesh == mesh && - g_ctx.canvas->sample_count == (CF_SampleCount)pip_cache.sample_count) { - found = true; - if (material->dirty) { - material->dirty = false; - pip = s_build_pipeline(shader, state, mesh); - if (pip_cache.pip) { - SDL_ReleaseGPUGraphicsPipeline(g_ctx.device, pip_cache.pip); - } - shader->pip_cache[i].pip = pip; - } else { - pip = pip_cache.pip; - } + if (CF_MEMCMP(&key, &shader->pip_cache[i].key, sizeof(key)) == 0) { + pip = shader->pip_cache[i].pip; + break; } } - if (!found) { + if (!pip) { pip = s_build_pipeline(shader, state, mesh); - shader->pip_cache.add({ (SDL_GPUSampleCount)g_ctx.canvas->sample_count, material, pip, mesh }); - material->dirty = false; + CF_Pipeline entry; + entry.key = key; + entry.pip = pip; + shader->pip_cache.add(entry); } CF_ASSERT(pip); SDL_GPUCommandBuffer* cmd = g_ctx.cmd; CF_ASSERT(cmd); - SDL_GPUColorTargetInfo pass_color_info; - CF_MEMSET(&pass_color_info, 0, sizeof(pass_color_info)); - pass_color_info.texture = g_ctx.canvas->texture; - pass_color_info.clear_color = { app->clear_color.r, app->clear_color.g, app->clear_color.b, app->clear_color.a }; - pass_color_info.load_op = g_ctx.canvas->clear ? SDL_GPU_LOADOP_CLEAR : SDL_GPU_LOADOP_LOAD; - pass_color_info.cycle = g_ctx.canvas->clear ? true : false; - if (g_ctx.canvas->sample_count == CF_SAMPLE_COUNT_1) { - pass_color_info.store_op = SDL_GPU_STOREOP_STORE; - } else { - pass_color_info.store_op = SDL_GPU_STOREOP_RESOLVE_AND_STORE; - pass_color_info.resolve_texture = g_ctx.canvas->resolve_texture; - } - - const bool has_depth_stencil_texture = g_ctx.canvas->depth_stencil != NULL; - CF_TextureInternal* depth_texture = has_depth_stencil_texture ? (CF_TextureInternal*)g_ctx.canvas->cf_depth_stencil.id : NULL; - const bool depth_test_requested = state->depth_write_enabled || state->depth_compare != CF_COMPARE_FUNCTION_ALWAYS; - const bool depth_test_enabled = (depth_texture != NULL) && depth_test_requested; - const bool stencil_capable = s_texture_supports_stencil(depth_texture); - const bool stencil_test_enabled = stencil_capable && state->stencil.enabled; - const bool use_depth_stencil_target = depth_texture && (depth_test_enabled || stencil_test_enabled); - - SDL_GPUDepthStencilTargetInfo pass_depth_stencil_info; - CF_MEMSET(&pass_depth_stencil_info, 0, sizeof(pass_depth_stencil_info)); - pass_depth_stencil_info.texture = use_depth_stencil_target ? g_ctx.canvas->depth_stencil : NULL; - if (use_depth_stencil_target) { - pass_depth_stencil_info.clear_depth = app->clear_depth; - pass_depth_stencil_info.clear_stencil = app->clear_stencil; - pass_depth_stencil_info.load_op = g_ctx.canvas->clear ? SDL_GPU_LOADOP_CLEAR : SDL_GPU_LOADOP_LOAD; - pass_depth_stencil_info.store_op = SDL_GPU_STOREOP_STORE; - pass_depth_stencil_info.stencil_load_op = g_ctx.canvas->clear ? SDL_GPU_LOADOP_CLEAR : SDL_GPU_LOADOP_LOAD; - pass_depth_stencil_info.stencil_store_op = SDL_GPU_STOREOP_STORE; - pass_depth_stencil_info.cycle = pass_color_info.cycle; - } - SDL_GPUDepthStencilTargetInfo* depth_stencil_ptr = use_depth_stencil_target ? &pass_depth_stencil_info : NULL; - SDL_GPURenderPass* pass = SDL_BeginGPURenderPass(cmd, &pass_color_info, 1, depth_stencil_ptr); - CF_ASSERT(pass); - g_ctx.canvas->pass = pass; + // Begin a new render pass if needed, or reuse the active one. + SDL_GPURenderPass* pass = g_ctx.active_pass; + if (!pass) { + SDL_GPUColorTargetInfo pass_color_info; + CF_MEMSET(&pass_color_info, 0, sizeof(pass_color_info)); + pass_color_info.texture = g_ctx.canvas->texture; + pass_color_info.clear_color = { app->clear_color.r, app->clear_color.g, app->clear_color.b, app->clear_color.a }; + pass_color_info.load_op = g_ctx.canvas->clear ? SDL_GPU_LOADOP_CLEAR : SDL_GPU_LOADOP_LOAD; + pass_color_info.cycle = g_ctx.canvas->clear ? true : false; + if (g_ctx.canvas->sample_count == CF_SAMPLE_COUNT_1) { + pass_color_info.store_op = SDL_GPU_STOREOP_STORE; + } else { + pass_color_info.store_op = SDL_GPU_STOREOP_RESOLVE_AND_STORE; + pass_color_info.resolve_texture = g_ctx.canvas->resolve_texture; + } + + // Always include depth-stencil if the canvas has one. + SDL_GPUDepthStencilTargetInfo pass_depth_stencil_info; + CF_MEMSET(&pass_depth_stencil_info, 0, sizeof(pass_depth_stencil_info)); + SDL_GPUDepthStencilTargetInfo* depth_stencil_ptr = NULL; + if (g_ctx.canvas->depth_stencil) { + pass_depth_stencil_info.texture = g_ctx.canvas->depth_stencil; + pass_depth_stencil_info.clear_depth = app->clear_depth; + pass_depth_stencil_info.clear_stencil = app->clear_stencil; + pass_depth_stencil_info.load_op = g_ctx.canvas->clear ? SDL_GPU_LOADOP_CLEAR : SDL_GPU_LOADOP_LOAD; + pass_depth_stencil_info.store_op = SDL_GPU_STOREOP_STORE; + pass_depth_stencil_info.stencil_load_op = g_ctx.canvas->clear ? SDL_GPU_LOADOP_CLEAR : SDL_GPU_LOADOP_LOAD; + pass_depth_stencil_info.stencil_store_op = SDL_GPU_STOREOP_STORE; + pass_depth_stencil_info.cycle = pass_color_info.cycle; + depth_stencil_ptr = &pass_depth_stencil_info; + } + + pass = SDL_BeginGPURenderPass(cmd, &pass_color_info, 1, depth_stencil_ptr); + CF_ASSERT(pass); + g_ctx.active_pass = pass; + g_ctx.canvas->pass = pass; + g_ctx.canvas->clear = false; + } + SDL_BindGPUGraphicsPipeline(pass, pip); SDL_GPUBufferBinding bind[2]; - bind[0].buffer = mesh->vertices.buffer; - bind[0].offset = 0; - bind[1].buffer = mesh->instances.buffer; - bind[1].offset = 0; - SDL_BindGPUVertexBuffers(pass, 0, bind, mesh->instances.buffer ? 2 : 1); + bool has_vertex_data = mesh->vertices.buffer != NULL; + bool has_instance_data = mesh->instances.buffer != NULL; + if (has_vertex_data && has_instance_data) { + bind[0].buffer = mesh->vertices.buffer; bind[0].offset = 0; + bind[1].buffer = mesh->instances.buffer; bind[1].offset = 0; + SDL_BindGPUVertexBuffers(pass, 0, bind, 2); + } else if (has_instance_data) { + bind[0].buffer = mesh->instances.buffer; bind[0].offset = 0; + SDL_BindGPUVertexBuffers(pass, 0, bind, 1); + } else { + bind[0].buffer = mesh->vertices.buffer; bind[0].offset = 0; + SDL_BindGPUVertexBuffers(pass, 0, bind, 1); + } if (mesh->indices.buffer) { SDL_GPUBufferBinding index_bind = { @@ -1482,33 +1771,61 @@ void cf_sdlgpu_apply_shader(CF_Shader shader_handle, CF_Material material_handle }; SDL_BindGPUIndexBuffer(pass, &index_bind, mesh->indices.stride == 2 ? SDL_GPU_INDEXELEMENTSIZE_16BIT : SDL_GPU_INDEXELEMENTSIZE_32BIT); } - // @TODO Storage/compute. - - // Bind images to all their respective slots. - int sampler_count = shader->image_names.count(); - SDL_GPUTextureSamplerBinding* sampler_bindings = SDL_stack_alloc(SDL_GPUTextureSamplerBinding, sampler_count); - int found_image_count = 0; - for (int i = 0; found_image_count < sampler_count && i < material->fs.textures.count(); ++i) { - const char* image_name = material->fs.textures[i].name; - for (int j = 0; j < shader->image_names.size(); ++j) { - if (shader->image_names[j] == image_name) { - sampler_bindings[j].sampler = ((CF_TextureInternal*)material->fs.textures[i].handle.id)->sampler; - sampler_bindings[j].texture = ((CF_TextureInternal*)material->fs.textures[i].handle.id)->tex; - found_image_count++; + + // Bind VS images to their respective slots. + int vs_sampler_count = shader->vs_image_names.count(); + if (vs_sampler_count > 0) { + SDL_GPUTextureSamplerBinding* vs_sampler_bindings = SDL_stack_alloc(SDL_GPUTextureSamplerBinding, vs_sampler_count); + int found_vs_image_count = 0; + for (int i = 0; found_vs_image_count < vs_sampler_count && i < material->vs.textures.count(); ++i) { + const char* image_name = material->vs.textures[i].name; + for (int j = 0; j < shader->vs_image_names.size(); ++j) { + if (shader->vs_image_names[j] == image_name) { + if (g_ctx.sampler_override) { + vs_sampler_bindings[j].sampler = g_ctx.sampler_override; + } else { + vs_sampler_bindings[j].sampler = ((CF_TextureInternal*)material->vs.textures[i].handle.id)->sampler; + } + vs_sampler_bindings[j].texture = ((CF_TextureInternal*)material->vs.textures[i].handle.id)->tex; + found_vs_image_count++; + } } } + CF_ASSERT(found_vs_image_count == vs_sampler_count); + SDL_BindGPUVertexSamplers(pass, 0, vs_sampler_bindings, (Uint32)found_vs_image_count); } - CF_ASSERT(found_image_count == sampler_count); - SDL_BindGPUFragmentSamplers(pass, 0, sampler_bindings, (Uint32)found_image_count); + + // Bind FS images to their respective slots. + int fs_sampler_count = shader->fs_image_names.count(); + if (fs_sampler_count > 0) { + SDL_GPUTextureSamplerBinding* fs_sampler_bindings = SDL_stack_alloc(SDL_GPUTextureSamplerBinding, fs_sampler_count); + int found_fs_image_count = 0; + for (int i = 0; found_fs_image_count < fs_sampler_count && i < material->fs.textures.count(); ++i) { + const char* image_name = material->fs.textures[i].name; + for (int j = 0; j < shader->fs_image_names.size(); ++j) { + if (shader->fs_image_names[j] == image_name) { + if (g_ctx.sampler_override) { + fs_sampler_bindings[j].sampler = g_ctx.sampler_override; + } else { + fs_sampler_bindings[j].sampler = ((CF_TextureInternal*)material->fs.textures[i].handle.id)->sampler; + } + fs_sampler_bindings[j].texture = ((CF_TextureInternal*)material->fs.textures[i].handle.id)->tex; + found_fs_image_count++; + } + } + } + CF_ASSERT(found_fs_image_count == fs_sampler_count); + SDL_BindGPUFragmentSamplers(pass, 0, fs_sampler_bindings, (Uint32)found_fs_image_count); + } + + // Clear sampler override after use. + g_ctx.sampler_override = NULL; // Copy over uniform data. s_copy_uniforms(cmd, &material->block_arena, shader, &material->vs, true); s_copy_uniforms(cmd, &material->block_arena, shader, &material->fs, false); SDL_SetGPUStencilReference(pass, state->stencil.reference); - - // Prevent the same canvas from clearing itself more than once. - g_ctx.canvas->clear = false; } void cf_sdlgpu_draw_elements() @@ -1528,8 +1845,371 @@ void cf_sdlgpu_draw_elements() } } app->draw_call_count++; +} + +void* cf_sdlgpu_create_draw_sampler(CF_Filter filter) +{ + SDL_GPUSamplerCreateInfo sampler_info = SDL_GPUSamplerCreateInfoDefaults(); + sampler_info.min_filter = s_wrap(filter); + sampler_info.mag_filter = s_wrap(filter); + SDL_GPUSampler* sampler = SDL_CreateGPUSampler(g_ctx.device, &sampler_info); + return (void*)sampler; +} + +void cf_sdlgpu_destroy_draw_sampler(void* sampler) +{ + if (sampler) { + SDL_ReleaseGPUSampler(g_ctx.device, (SDL_GPUSampler*)sampler); + } +} + +void cf_sdlgpu_set_sampler_override(void* sampler) +{ + g_ctx.sampler_override = (SDL_GPUSampler*)sampler; +} + +//-------------------------------------------------------------------------------------------------- +// Compute shaders. + +struct CF_ComputeShaderInternal +{ + SDL_GPUComputePipeline* pipeline; + + // Reflection data for name-matching uniforms from material. + int cs_uniform_block_count; + int cs_block_sizes[CF_MAX_UNIFORM_BLOCK_COUNT]; + Cute::Array cs_uniform_block_members[CF_MAX_UNIFORM_BLOCK_COUNT]; + + // Sampler names + binding slots for name-matching textures from material. + Cute::Array cs_sampler_names; + Cute::Array cs_sampler_binding_slots; + + // Resource counts (from SPIRV reflection, needed for pipeline creation). + int num_samplers; + int num_readonly_storage_textures; + int num_readonly_storage_buffers; + int num_readwrite_storage_textures; + int num_readwrite_storage_buffers; + int num_uniform_buffers; + + // Hot-reload. + const char* path; // NULL if not file-based. + + CF_INLINE int cs_index(const char* name, int block_index) + { + for (int i = 0; i < cs_uniform_block_members[block_index].size(); ++i) { + if (cs_uniform_block_members[block_index][i].name == name) return i; + } + return -1; + } +}; + +struct CF_StorageBufferInternal +{ + SDL_GPUBuffer* buffer; + SDL_GPUTransferBuffer* transfer_buffer; + int size; +}; + +CF_ComputeShader cf_sdlgpu_make_compute_shader_from_bytecode(CF_ShaderBytecode bytecode) +{ + CF_ComputeShaderInternal* cs = CF_NEW(CF_ComputeShaderInternal); + CF_MEMSET(cs, 0, sizeof(*cs)); + + const CF_ShaderInfo* info = &bytecode.shader_info; + + // Load sampler reflection data. + for (int i = 0; i < info->num_images; ++i) { + cs->cs_sampler_names.add(sintern(info->image_names[i])); + cs->cs_sampler_binding_slots.add(info->image_binding_slots ? info->image_binding_slots[i] : i); + } + + // Load uniform block reflection data. + cs->cs_uniform_block_count = info->num_uniforms; + const CF_ShaderUniformMemberInfo* member_infos = info->uniform_members; + for (int i = 0; i < info->num_uniforms; ++i) { + const CF_ShaderUniformInfo* block_info = &info->uniforms[i]; + int block_index = block_info->block_index; + cs->cs_block_sizes[block_index] = block_info->block_size; + + const char* block_name = sintern(block_info->block_name); + for (int j = 0; j < block_info->num_members; ++j) { + const CF_ShaderUniformMemberInfo* member_info = &member_infos[j]; + CF_UniformBlockMember block_member; + block_member.name = sintern(member_info->name); + block_member.block_name = block_name; + block_member.type = s_uniform_type(member_info->type); + CF_ASSERT(block_member.type != CF_UNIFORM_TYPE_UNKNOWN); + block_member.array_element_count = member_info->array_length; + block_member.size = s_uniform_size(block_member.type) * member_info->array_length; + block_member.offset = member_info->offset; + cs->cs_uniform_block_members[block_index].add(block_member); + } + member_infos += block_info->num_members; + } + + // Store resource counts. + cs->num_samplers = info->num_samplers; + cs->num_readonly_storage_textures = info->num_storage_textures; + cs->num_readonly_storage_buffers = info->num_storage_buffers; + cs->num_readwrite_storage_textures = info->num_readwrite_storage_textures; + cs->num_readwrite_storage_buffers = info->num_readwrite_storage_buffers; + cs->num_uniform_buffers = info->num_uniforms; + + // Create the compute pipeline. + if (SDL_GetGPUShaderFormats(g_ctx.device) == SDL_GPU_SHADERFORMAT_SPIRV) { + SDL_GPUComputePipelineCreateInfo pip_info = {}; + pip_info.code = bytecode.content; + pip_info.code_size = bytecode.size; + pip_info.entrypoint = "main"; + pip_info.format = SDL_GPU_SHADERFORMAT_SPIRV; + pip_info.num_samplers = (Uint32)cs->num_samplers; + pip_info.num_readonly_storage_textures = (Uint32)cs->num_readonly_storage_textures; + pip_info.num_readonly_storage_buffers = (Uint32)cs->num_readonly_storage_buffers; + pip_info.num_readwrite_storage_textures = (Uint32)cs->num_readwrite_storage_textures; + pip_info.num_readwrite_storage_buffers = (Uint32)cs->num_readwrite_storage_buffers; + pip_info.num_uniform_buffers = (Uint32)cs->num_uniform_buffers; + pip_info.threadcount_x = 0; + pip_info.threadcount_y = 0; + pip_info.threadcount_z = 0; + cs->pipeline = SDL_CreateGPUComputePipeline(g_ctx.device, &pip_info); + } else { + SDL_ShaderCross_ComputePipelineMetadata metadata = {}; + metadata.num_samplers = (Uint32)cs->num_samplers; + metadata.num_readonly_storage_textures = (Uint32)cs->num_readonly_storage_textures; + metadata.num_readonly_storage_buffers = (Uint32)cs->num_readonly_storage_buffers; + metadata.num_readwrite_storage_textures = (Uint32)cs->num_readwrite_storage_textures; + metadata.num_readwrite_storage_buffers = (Uint32)cs->num_readwrite_storage_buffers; + metadata.num_uniform_buffers = (Uint32)cs->num_uniform_buffers; + metadata.threadcount_x = 0; + metadata.threadcount_y = 0; + metadata.threadcount_z = 0; + + SDL_ShaderCross_SPIRV_Info spirvInfo; + CF_MEMSET(&spirvInfo, 0, sizeof(spirvInfo)); + spirvInfo.bytecode = bytecode.content; + spirvInfo.bytecode_size = bytecode.size; + spirvInfo.entrypoint = "main"; + spirvInfo.shader_stage = SDL_SHADERCROSS_SHADERSTAGE_COMPUTE; + spirvInfo.enable_debug = false; + spirvInfo.name = "compute.glsl"; + spirvInfo.props = SDL_CreateProperties(); + cs->pipeline = SDL_ShaderCross_CompileComputePipelineFromSPIRV(g_ctx.device, &spirvInfo, &metadata); + SDL_DestroyProperties(spirvInfo.props); + } + CF_ASSERT(cs->pipeline); + + CF_ComputeShader result; + result.id = (uint64_t)cs; + return result; +} + +void cf_sdlgpu_destroy_compute_shader(CF_ComputeShader shader) +{ + CF_ComputeShaderInternal* cs = (CF_ComputeShaderInternal*)shader.id; + if (!cs) return; + SDL_ReleaseGPUComputePipeline(g_ctx.device, cs->pipeline); + CF_FREE(cs); +} + +CF_StorageBuffer cf_sdlgpu_make_storage_buffer(CF_StorageBufferParams params) +{ + CF_StorageBufferInternal* sb = CF_NEW(CF_StorageBufferInternal); + CF_MEMSET(sb, 0, sizeof(*sb)); + sb->size = params.size; + + SDL_GPUBufferUsageFlags usage = 0; + if (params.compute_readable) usage |= SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ; + if (params.compute_writable) usage |= SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE; + if (params.graphics_readable) usage |= SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ; + + SDL_GPUBufferCreateInfo buf_info = {}; + buf_info.usage = usage; + buf_info.size = (Uint32)params.size; + sb->buffer = SDL_CreateGPUBuffer(g_ctx.device, &buf_info); + + SDL_GPUTransferBufferCreateInfo tbuf_info = {}; + tbuf_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + tbuf_info.size = (Uint32)params.size; + sb->transfer_buffer = SDL_CreateGPUTransferBuffer(g_ctx.device, &tbuf_info); + + CF_StorageBuffer result; + result.id = (uint64_t)sb; + return result; +} + +void cf_sdlgpu_update_storage_buffer(CF_StorageBuffer buffer, const void* data, int size) +{ + CF_StorageBufferInternal* sb = (CF_StorageBufferInternal*)buffer.id; + s_end_active_pass(); + + // Resize if necessary. + if (size > sb->size) { + SDL_ReleaseGPUBuffer(g_ctx.device, sb->buffer); + SDL_ReleaseGPUTransferBuffer(g_ctx.device, sb->transfer_buffer); + + int new_size = size * 2; + sb->size = new_size; + + // Infer usage flags from the old buffer -- just re-create with same flags. + // For simplicity, use READ since we're uploading. + SDL_GPUBufferCreateInfo buf_info = {}; + buf_info.usage = SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ; + buf_info.size = (Uint32)new_size; + sb->buffer = SDL_CreateGPUBuffer(g_ctx.device, &buf_info); + + SDL_GPUTransferBufferCreateInfo tbuf_info = {}; + tbuf_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + tbuf_info.size = (Uint32)new_size; + sb->transfer_buffer = SDL_CreateGPUTransferBuffer(g_ctx.device, &tbuf_info); + } + + // Map, copy, unmap. + void* p = SDL_MapGPUTransferBuffer(g_ctx.device, sb->transfer_buffer, true); + CF_MEMCPY(p, data, size); + SDL_UnmapGPUTransferBuffer(g_ctx.device, sb->transfer_buffer); + + // Upload via copy pass. + SDL_GPUCommandBuffer* cmd = g_ctx.cmd ? g_ctx.cmd : SDL_AcquireGPUCommandBuffer(g_ctx.device); + SDL_GPUCopyPass* pass = SDL_BeginGPUCopyPass(cmd); + SDL_GPUTransferBufferLocation location; + location.offset = 0; + location.transfer_buffer = sb->transfer_buffer; + SDL_GPUBufferRegion region; + region.buffer = sb->buffer; + region.offset = 0; + region.size = size; + SDL_UploadToGPUBuffer(pass, &location, ®ion, true); + SDL_EndGPUCopyPass(pass); + if (!g_ctx.cmd) SDL_SubmitGPUCommandBuffer(cmd); +} + +void cf_sdlgpu_destroy_storage_buffer(CF_StorageBuffer buffer) +{ + CF_StorageBufferInternal* sb = (CF_StorageBufferInternal*)buffer.id; + if (!sb) return; + SDL_ReleaseGPUBuffer(g_ctx.device, sb->buffer); + SDL_ReleaseGPUTransferBuffer(g_ctx.device, sb->transfer_buffer); + CF_FREE(sb); +} + +void cf_sdlgpu_dispatch_compute(CF_ComputeShader shader, CF_Material material_handle, CF_ComputeDispatch dispatch) +{ + CF_ComputeShaderInternal* cs = (CF_ComputeShaderInternal*)shader.id; + CF_MaterialInternal* material = (CF_MaterialInternal*)material_handle.id; + + s_end_active_pass(); + + SDL_GPUCommandBuffer* cmd = g_ctx.cmd; + CF_ASSERT(cmd); + + // Build RW texture bindings for pass creation. + SDL_GPUStorageTextureReadWriteBinding* rw_tex_bindings = NULL; + if (dispatch.rw_texture_count > 0) { + rw_tex_bindings = SDL_stack_alloc(SDL_GPUStorageTextureReadWriteBinding, dispatch.rw_texture_count); + for (int i = 0; i < dispatch.rw_texture_count; ++i) { + CF_MEMSET(rw_tex_bindings + i, 0, sizeof(SDL_GPUStorageTextureReadWriteBinding)); + CF_TextureInternal* tex = (CF_TextureInternal*)dispatch.rw_textures[i].id; + rw_tex_bindings[i].texture = tex->tex; + } + } + + // Build RW buffer bindings for pass creation. + SDL_GPUStorageBufferReadWriteBinding* rw_buf_bindings = NULL; + if (dispatch.rw_buffer_count > 0) { + rw_buf_bindings = SDL_stack_alloc(SDL_GPUStorageBufferReadWriteBinding, dispatch.rw_buffer_count); + for (int i = 0; i < dispatch.rw_buffer_count; ++i) { + CF_MEMSET(rw_buf_bindings + i, 0, sizeof(SDL_GPUStorageBufferReadWriteBinding)); + CF_StorageBufferInternal* sb = (CF_StorageBufferInternal*)dispatch.rw_buffers[i].id; + rw_buf_bindings[i].buffer = sb->buffer; + } + } + + // Begin compute pass with RW resources. + SDL_GPUComputePass* pass = SDL_BeginGPUComputePass( + cmd, + rw_tex_bindings, + (Uint32)dispatch.rw_texture_count, + rw_buf_bindings, + (Uint32)dispatch.rw_buffer_count + ); + CF_ASSERT(pass); + + // Bind the pipeline. + SDL_BindGPUComputePipeline(pass, cs->pipeline); + + // Bind sampled textures from material (name-matched against cs_sampler_names). + int sampler_count = cs->cs_sampler_names.count(); + if (sampler_count > 0) { + SDL_GPUTextureSamplerBinding* sampler_bindings = SDL_stack_alloc(SDL_GPUTextureSamplerBinding, sampler_count); + int found_count = 0; + for (int i = 0; found_count < sampler_count && i < material->cs.textures.count(); ++i) { + const char* image_name = material->cs.textures[i].name; + for (int j = 0; j < cs->cs_sampler_names.size(); ++j) { + if (cs->cs_sampler_names[j] == image_name) { + sampler_bindings[j].sampler = ((CF_TextureInternal*)material->cs.textures[i].handle.id)->sampler; + sampler_bindings[j].texture = ((CF_TextureInternal*)material->cs.textures[i].handle.id)->tex; + found_count++; + } + } + } + CF_ASSERT(found_count == sampler_count); + SDL_BindGPUComputeSamplers(pass, 0, sampler_bindings, (Uint32)found_count); + } + + // Bind readonly storage textures. + if (dispatch.ro_texture_count > 0) { + SDL_GPUTexture** ro_textures = SDL_stack_alloc(SDL_GPUTexture*, dispatch.ro_texture_count); + for (int i = 0; i < dispatch.ro_texture_count; ++i) { + CF_TextureInternal* tex = (CF_TextureInternal*)dispatch.ro_textures[i].id; + ro_textures[i] = tex->tex; + } + SDL_BindGPUComputeStorageTextures(pass, 0, ro_textures, (Uint32)dispatch.ro_texture_count); + } + + // Bind readonly storage buffers. + if (dispatch.ro_buffer_count > 0) { + SDL_GPUBuffer** ro_buffers = SDL_stack_alloc(SDL_GPUBuffer*, dispatch.ro_buffer_count); + for (int i = 0; i < dispatch.ro_buffer_count; ++i) { + CF_StorageBufferInternal* sb = (CF_StorageBufferInternal*)dispatch.ro_buffers[i].id; + ro_buffers[i] = sb->buffer; + } + SDL_BindGPUComputeStorageBuffers(pass, 0, ro_buffers, (Uint32)dispatch.ro_buffer_count); + } + + // Push uniforms from material (name-matched against cs_uniform_block_members). + { + void* ub_ptrs[CF_MAX_UNIFORM_BLOCK_COUNT] = { }; + int ub_sizes[CF_MAX_UNIFORM_BLOCK_COUNT] = { }; + for (int block_index = 0; block_index < cs->cs_uniform_block_count; ++block_index) { + for (int i = 0; i < material->cs.uniforms.count(); ++i) { + CF_Uniform uniform = material->cs.uniforms[i]; + int idx = cs->cs_index(uniform.name, block_index); + if (idx >= 0) { + if (!ub_ptrs[block_index]) { + int size = cs->cs_block_sizes[block_index]; + void* block = cf_arena_alloc(&material->block_arena, size); + CF_MEMSET(block, 0, size); + ub_ptrs[block_index] = block; + ub_sizes[block_index] = size; + } + int offset = cs->cs_uniform_block_members[block_index][idx].offset; + void* dst = (void*)(((uintptr_t)ub_ptrs[block_index]) + offset); + CF_MEMCPY(dst, uniform.data, uniform.size); + } + } + } + for (int i = 0; i < CF_MAX_UNIFORM_BLOCK_COUNT; ++i) { + if (ub_ptrs[i]) { + SDL_PushGPUComputeUniformData(cmd, i, ub_ptrs[i], (uint32_t)ub_sizes[i]); + } + } + cf_arena_reset(&material->block_arena); + } - SDL_EndGPURenderPass(g_ctx.canvas->pass); + // Dispatch. + SDL_DispatchGPUCompute(pass, (Uint32)dispatch.group_count_x, (Uint32)dispatch.group_count_y, (Uint32)dispatch.group_count_z); + SDL_EndGPUComputePass(pass); } #endif diff --git a/src/cute_hashtable.cpp b/src/cute_hashtable.cpp deleted file mode 100644 index 1b941f136..000000000 --- a/src/cute_hashtable.cpp +++ /dev/null @@ -1,415 +0,0 @@ -/* - Cute Framework - Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ - - This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info -*/ - -#include -#include -#include -#include -#include - -#include - -using namespace Cute; - -// Original implementation by Mattias Gustavsson -// https://github.com/mattiasgustavsson/libs/blob/main/hashtable.h - -// Prime table sizes are used to help security of the table at the expense of the % operator, -// as opposed to the speed << operator, for lookups. -// By using prime table sizes we ensure all bits of each hash are utilized. This helps mitigate -// DoS attacks on the table lookup function. -static int s_primes[] = { - 31, 67, 127, 257, 509, 1021, 2053, 4099, 8191, 16381, 32771, 65537, 131071, - 262147, 524287, 1048573, 2097143, 4194301, 8388617, 16777213, 33554467, - 67108859, 134217757, 268435459, 536870909, 1073741827, 2147483647 -}; - -static CF_INLINE int s_next_prime(int a) -{ - int i = 0; - while (s_primes[i] <= a) ++i; - return s_primes[i]; -} - -static CF_INLINE void* s_get_item(const CF_Hhdr* table, int index) -{ - uint8_t* items = (uint8_t*)table->items_data; - return items + index * table->item_size; -} - -static CF_INLINE void* s_get_key(const CF_Hhdr* table, int index) -{ - uint8_t* keys = (uint8_t*)table->items_key; - return keys + index * table->key_size; -} - -void* cf_hashtable_make_impl(int key_size, int item_size, int capacity) -{ - CF_ASSERT(capacity); - - CF_Hhdr* table = (CF_Hhdr*)CF_CALLOC(sizeof(CF_Hhdr) + (capacity + 1) * item_size); - table->cookie = CF_HCOOKIE; - table->key_size = key_size; - table->item_size = item_size; - - // Space is made for a zero'd out "hidden item" to represent failed lookups. - // This is critical to support return-by-value polymorphism in the C macro API for `hget` and `hfind`. - // We also "pass" in values to `hadd` through this space. - table->hidden_item = (void*)((uintptr_t)(table + 1)); - table->items_data = (void*)((uintptr_t)(table + 1) + item_size); - table->slot_capacity = s_next_prime(capacity); - table->slots = (CF_Hslot*)CF_CALLOC(table->slot_capacity * sizeof(CF_Hslot)); - for (int i = 0; i < table->slot_capacity; ++i) { - table->slots[i].item_index = -1; - } - table->item_capacity = capacity; - table->items_key = CF_ALLOC(capacity * key_size); - table->items_slot_index = (int*)CF_ALLOC(capacity * sizeof(*table->items_slot_index)); - table->temp_key = CF_ALLOC(key_size); - table->temp_item = CF_ALLOC(item_size); - - return s_get_item(table, 0); -} - -void cf_hashtable_free_impl(CF_Hhdr* table) -{ - if (!table) return; - CF_FREE(table->slots); - CF_FREE(table->items_key); - CF_FREE(table->items_slot_index); - CF_FREE(table->temp_key); - CF_FREE(table->temp_item); - CF_FREE(table); -} - -static CF_INLINE int s_keys_equal(const CF_Hhdr* table, const void* a, const void* b) -{ - return !CF_MEMCMP(a, b, table->key_size); -} - -static int s_find_slot(const CF_Hhdr *table, uint32_t hash, const void* key) -{ - uint32_t slot_capacity = (uint32_t)table->slot_capacity; - int base_slot = (int)(hash % slot_capacity); - int base_count = table->slots[base_slot].base_count; - int slot = base_slot; - - while (base_count > 0) { - if (table->slots[slot].item_index >= 0) { - uint32_t slot_hash = table->slots[slot].key_hash; - int slot_base = (int)(slot_hash % slot_capacity); - if (slot_base == base_slot) { - CF_ASSERT(base_count > 0); - --base_count; - const void* slot_key = s_get_key(table, table->slots[slot].item_index); - if (slot_hash == hash && s_keys_equal(table, slot_key, key)) { - return slot; - } - } - } - slot = (int)((slot + 1) % table->slot_capacity); - } - - return -1; -} - -static void s_expand_slots(CF_Hhdr* table) -{ - int old_capacity = table->slot_capacity; - CF_Hslot* old_slots = table->slots; - - table->slot_capacity = s_next_prime(table->slot_capacity); - uint32_t slot_capacity = (uint32_t)table->slot_capacity; - - table->slots = (CF_Hslot*)CF_CALLOC(table->slot_capacity * sizeof(CF_Hslot)); - CF_ASSERT(table->slots); - for (int i = 0; i < table->slot_capacity; ++i) { - table->slots[i].item_index = -1; - } - - for (int i = 0; i < old_capacity; ++i) { - if (old_slots[i].item_index >= 0) { - uint32_t hash = old_slots[i].key_hash; - int base_slot = (int)(hash % slot_capacity); - int slot = base_slot; - while (table->slots[slot].item_index >= 0) { - slot = (slot + 1) % slot_capacity; - } - table->slots[slot].key_hash = hash; - int item_index = old_slots[i].item_index; - table->slots[slot].item_index = item_index; - table->items_slot_index[item_index] = slot; - ++table->slots[base_slot].base_count; - } - } - - CF_FREE(old_slots); -} - -static CF_Hhdr* s_expand_items(CF_Hhdr* table) -{ - int capacity = table->item_capacity * 2; - table = (CF_Hhdr*)CF_REALLOC(table, sizeof(CF_Hhdr) + (capacity + 1) * table->item_size); - table->item_capacity = capacity; - table->hidden_item = (void*)((uintptr_t)(table + 1)); - table->items_data = (void*)((uintptr_t)(table + 1) + table->item_size); - table->items_key = CF_REALLOC(table->items_key, capacity * table->key_size); - table->items_slot_index = (int*)CF_REALLOC(table->items_slot_index, capacity * sizeof(*table->items_slot_index)); - return table; -} - -void* cf_hashtable_insert_impl2(CF_Hhdr* table, const void* key, const void* item) -{ - uint32_t hash = (uint32_t)fnv1a(key, table->key_size); - int slot = s_find_slot(table, hash, key); - if (slot >= 0) { - int item_index = table->slots[slot].item_index; - void* item_dst = s_get_item(table, item_index); - if (item) { - CF_MEMCPY(item_dst, item, table->item_size); - } else { - CF_MEMSET(item_dst, 0, table->item_size); - } - return s_get_item(table, 0); - } - - if (table->count >= (table->slot_capacity - table->slot_capacity / 3)) { - s_expand_slots(table); - } - - uint32_t slot_capacity = (uint32_t)table->slot_capacity; - int base_slot = (int)(hash % slot_capacity); - int base_count = table->slots[base_slot].base_count; - slot = base_slot; - int first_free = slot; - while (base_count) { - if (table->slots[slot].item_index < 0 && table->slots[first_free].item_index >= 0) first_free = slot; - uint32_t slot_hash = table->slots[slot].key_hash; - int slot_base = (int)(slot_hash % slot_capacity); - if (slot_base == base_slot) { - --base_count; - } - slot = (slot + 1) % table->slot_capacity; - } - - slot = first_free; - while (table->slots[slot].item_index >= 0) { - slot = (slot + 1) % slot_capacity; - } - - if (table->count >= table->item_capacity) { - table = s_expand_items(table); - - // Update the "hidden item" pointer, as it was invalidated by the item array expansion - // since the hidden item is at index -1. - item = (void*)((uintptr_t)(table + 1)); - } - - CF_ASSERT(table->count < table->item_capacity); - CF_ASSERT(table->slots[slot].item_index < 0 && (hash % slot_capacity) == (uint32_t)base_slot); - table->slots[slot].key_hash = hash; - table->slots[slot].item_index = table->count; - ++table->slots[base_slot].base_count; - - void* item_dst = s_get_item(table, table->count); - void* key_dst = s_get_key(table, table->count); - if (item) { - CF_MEMCPY(item_dst, item, table->item_size); - } else { - CF_MEMSET(item_dst, 0, table->item_size); - } - CF_MEMCPY(key_dst, key, table->key_size); - table->items_slot_index[table->count] = slot; - table->return_index = table->count++; - - return s_get_item(table, 0); -} - -void* cf_hashtable_insert_impl3(CF_Hhdr* table, const void* key) -{ - return cf_hashtable_insert_impl2(table, key, table->hidden_item); -} - -void* cf_hashtable_insert_impl(CF_Hhdr* table, uint64_t key) -{ - return cf_hashtable_insert_impl2(table, &key, table->hidden_item); -} - -void cf_hashtable_remove_impl2(CF_Hhdr* table, const void* key) -{ - uint32_t hash = (uint32_t)fnv1a(key, table->key_size); - int slot = s_find_slot(table, hash, key); - CF_ASSERT(slot >= 0); - - int base_slot = (int)(hash % (uint32_t)table->slot_capacity); - int index = table->slots[slot].item_index; - int last_index = table->count - 1; - --table->slots[base_slot].base_count; - table->slots[slot].item_index = -1; - - if (index != last_index) { - void* dst_key = s_get_key(table, index); - void* src_key = s_get_key(table, last_index); - CF_MEMCPY(dst_key, src_key, (size_t)table->key_size); - void* dst_item = s_get_item(table, index); - void* src_item = s_get_item(table, last_index); - CF_MEMCPY(dst_item, src_item, (size_t)table->item_size); - table->items_slot_index[index] = table->items_slot_index[last_index]; - table->slots[table->items_slot_index[last_index]].item_index = index; - } - --table->count; -} - -void cf_hashtable_remove_impl(CF_Hhdr* table, uint64_t key) -{ - cf_hashtable_remove_impl2(table, &key); -} - -void cf_hashtable_clear_impl(CF_Hhdr* table) -{ - table->count = 0; - for (int i = 0; i < table->slot_capacity; ++i) { - table->slots[i].base_count = 0; - table->slots[i].item_index = -1; - } -} - -int cf_hashtable_find_impl2(const CF_Hhdr* table, const void* key) -{ - int slot = s_find_slot(table, (uint32_t)fnv1a(key, table->key_size), key); - if (slot < 0) { - // We will be "returning" a zero'd out item through `hget` with this - // hidden item. - CF_MEMSET(table->hidden_item, 0, table->item_size); - - ((CF_Hhdr *)table)->return_index = -1; - return -1; - } - ((CF_Hhdr*)table)->return_index = table->slots[slot].item_index; - return table->return_index; -} - -int cf_hashtable_find_impl(const CF_Hhdr* table, uint64_t key) -{ - return cf_hashtable_find_impl2(table, &key); -} - -bool cf_hashtable_has_impl(CF_Hhdr* table, uint64_t key) -{ - return cf_hashtable_find_impl(table, key) < 0 ? false : true; -} - -int cf_hashtable_count_impl(const CF_Hhdr* table) -{ - return table->count; -} - -void* cf_hashtable_items_impl(const CF_Hhdr* table) -{ - return table->items_data; -} - -void* cf_hashtable_keys_impl(const CF_Hhdr* table) -{ - return table->items_key; -} - -void cf_hashtable_swap_impl(CF_Hhdr* table, int index_a, int index_b) -{ - if (index_a < 0 || index_a >= table->count || index_b < 0 || index_b >= table->count) return; - - int slot_a = table->items_slot_index[index_a]; - int slot_b = table->items_slot_index[index_b]; - - table->items_slot_index[index_a] = slot_b; - table->items_slot_index[index_b] = slot_a; - - void* key_a = s_get_key(table, index_a); - void* key_b = s_get_key(table, index_b); - CF_MEMCPY(table->temp_key, key_a, table->key_size); - CF_MEMCPY(key_a, key_b, table->key_size); - CF_MEMCPY(key_b, table->temp_key, table->key_size); - - void* item_a = s_get_item(table, index_a); - void* item_b = s_get_item(table, index_b); - CF_MEMCPY(table->temp_item, item_a, table->item_size); - CF_MEMCPY(item_a, item_b, table->item_size); - CF_MEMCPY(item_b, table->temp_item, table->item_size); - - table->slots[slot_a].item_index = index_b; - table->slots[slot_b].item_index = index_a; -} - -static void s_sort(CF_Hhdr* table, int offset, int count) -{ - if (count <= 1) return; - - auto key = [=](int index) { return *(uint64_t*)s_get_key(table, offset + index); }; - auto swap = [=](int ia, int ib) { cf_hashtable_swap_impl(table, offset + ia, offset + ib); }; - - uint64_t pivot_key = key(count - 1); - int lo = 0; - for (int hi = 0; hi < count - 1; ++hi) { - uint64_t hi_key = key(hi); - if (hi_key < pivot_key) { - swap(lo, hi); - lo++; - } - } - - swap(count - 1, lo); - - s_sort(table, 0, lo); - s_sort(table, lo + 1, count - 1 - lo); -} - -void* cf_hashtable_sort_impl(CF_Hhdr* table) -{ - s_sort(table, 0, table->count); - return s_get_item(table, 0); -} - -static void s_ssort(CF_Hhdr* table, int offset, int count, bool ignore_case) -{ - if (count <= 1) return; - - auto key = [=](int index) { return (const char*)s_get_key(table, offset + index); }; - auto swap = [=](int ia, int ib) { cf_hashtable_swap_impl(table, offset + ia, offset + ib); }; - - const char* pivot_key = key(count - 1); - int lo = 0; - for (int hi = 0; hi < count - 1; ++hi) { - const char* hi_key = key(hi); - bool pred; - if (ignore_case) { - pred = sicmp(hi_key, pivot_key) < 0 ? true : false; - } else { - pred = scmp(hi_key, pivot_key) < 0 ? true : false; - } - if (pred) { - swap(lo, hi); - lo++; - } - } - - swap(count - 1, lo); - - s_sort(table, 0, lo); - s_sort(table, lo + 1, count - 1 - lo); -} - -void* cf_hashtable_ssort_impl(CF_Hhdr* table) -{ - s_ssort(table, 0, table->count, false); - return s_get_item(table, 0); -} - -void* cf_hashtable_sisort_impl(CF_Hhdr* table) -{ - s_ssort(table, 0, table->count, true); - return s_get_item(table, 0); -} diff --git a/src/cute_https.cpp b/src/cute_https.cpp index 35f21f4a2..cc33664f4 100644 --- a/src/cute_https.cpp +++ b/src/cute_https.cpp @@ -48,7 +48,7 @@ typedef struct CF_Response bool deflate = false; int content_length = 0; bool trailers = false; - Map headers; + Map headers; String parse; String content; } CF_Response; @@ -309,13 +309,6 @@ static void s_decode(CF_Coroutine co) if (response->trailers) { // Read in any trailing headers. s_headers(co, response); - } else { - s_get_line(co, response); - if (response->parse.len() != 0) { - // End of response doesn't have expected final empty-line CRLF. - response->ok = false; - return; - } } } @@ -339,27 +332,26 @@ static void s_https_process(CF_Coroutine co) // Send of the HTTP request. String s; + s = String::fmt( + "%s %s HTTP/1.1\r\n" + "Host: %s\r\n" + "Connection: close\r\n" + "TE: trailers, deflate, gzip\r\n", + request->content ? "POST" : "GET", request->uri, request->host + ); + for (int i = 0; i < request->headers.count(); ++i) { + s.fmt_append("%s: %s\r\n", request->headers[i].name, request->headers[i].value); + } + + if (request->content) { + s.fmt_append("Content-Length: %d\r\n", request->content_length); + } + + s.append("\r\n"); + if (request->content) { - s = String::fmt( - "POST %s HTTP/1.1\r\n" - "Host: %s\r\n" - "Connection: close\r\n" - "TE: trailers, deflate, gzip\r\n" - "Content-Length: %d\r\n" - "\r\n", - request->uri, request->host, request->content_length - ); const char* content = (const char*)request->content; - s.append(content, content + request->content_length); - } else { - s = String::fmt( - "GET %s HTTP/1.1\r\n" - "Host: %s\r\n" - "Connection: close\r\n" - "TE: trailers, deflate, gzip\r\n" - "\r\n", - request->uri, request->host - ); + s.append(content, content + request->content_length); } if (tls_send(request->connection, s.c_str(), s.len()) < 0) { @@ -395,6 +387,8 @@ static void s_https_process(CF_Coroutine co) request->result = CF_HTTPS_RESULT_FAILED; destroy_coroutine(decoder); return; + } else if (coroutine_state(decoder) == CF_COROUTINE_STATE_DEAD) { + break; } } coroutine_yield(co); @@ -500,7 +494,7 @@ int cf_https_response_headers_count(CF_HttpsResponse response_handle) return response->headers.count(); } -htbl const CF_HttpsHeader* cf_https_response_headers(CF_HttpsResponse response_handle) +dyna const CF_HttpsHeader* cf_https_response_headers(CF_HttpsResponse response_handle) { CF_Response* response = (CF_Response*)response_handle.id; return response->headers.items(); diff --git a/src/cute_image.cpp b/src/cute_image.cpp index da5c63284..287dcf6db 100644 --- a/src/cute_image.cpp +++ b/src/cute_image.cpp @@ -73,6 +73,26 @@ void cf_image_free_indexed(CF_ImageIndexed* img) CF_FREE(img->pix); } +CF_Result cf_image_save_png(const char* virtual_path, CF_Image* img) +{ + void* data = NULL; + int size = 0; + CF_Result r = cf_image_save_png_to_memory(img, &data, &size); + if (cf_is_error(r)) return r; + r = cf_fs_write_entire_buffer_to_file(virtual_path, data, (size_t)size); + CF_FREE(data); + return r; +} + +CF_Result cf_image_save_png_to_memory(CF_Image* img, void** out_data, int* out_size) +{ + cp_saved_png_t saved = cp_save_png_to_memory((cp_image_t*)img); + if (!saved.data) return cf_result_error("Failed to save PNG to memory."); + *out_data = saved.data; + *out_size = saved.size; + return cf_result_success(); +} + CF_Image cf_image_depallete(CF_ImageIndexed* indexed_img) { cp_image_t cp_img = cp_depallete_indexed_image((cp_indexed_image_t*)indexed_img); diff --git a/src/cute_input.cpp b/src/cute_input.cpp index 1eb61e230..7228a6d74 100644 --- a/src/cute_input.cpp +++ b/src/cute_input.cpp @@ -370,7 +370,7 @@ void cf_input_text_add_utf8(const char* text) { while (*text) { int cp; - text = cf_decode_UTF8(text, &cp); + text = cf_string_decode_UTF8(text, &cp); app->input_text.add((int)cp); } } @@ -476,6 +476,7 @@ void cf_begin_frame_input() app->window_state.moved = false; app->window_state.restored = false; app->window_state.resized = false; + app->dpi_scale_was_changed = false; cf_joypad_update(); // Update key durations to simulate "press and hold" style for `key_repeating`. @@ -553,6 +554,11 @@ void cf_pump_input_msgs() app->window_state.has_keyboard_focus = false; break; + case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED: + app->dpi_scale = SDL_GetWindowDisplayScale(app->window); + app->dpi_scale_was_changed = true; + break; + case SDL_EVENT_KEY_DOWN: { if (event.key.repeat) continue; diff --git a/src/cute_math.cpp b/src/cute_math.cpp index e7e3c94ef..43755dd1f 100644 --- a/src/cute_math.cpp +++ b/src/cute_math.cpp @@ -16,8 +16,6 @@ CF_STATIC_ASSERT(CF_POLY_MAX_VERTS == C2_MAX_POLYGON_VERTS, "Must be equal."); CF_STATIC_ASSERT(sizeof(CF_V2) == sizeof(c2v), "Must be equal."); -CF_STATIC_ASSERT(sizeof(CF_SinCos) == sizeof(c2r), "Must be equal."); -CF_STATIC_ASSERT(sizeof(CF_Transform) == sizeof(c2x), "Must be equal."); CF_STATIC_ASSERT(sizeof(CF_M2x2) == sizeof(c2m), "Must be equal."); CF_STATIC_ASSERT(sizeof(CF_Halfspace) == sizeof(c2h), "Must be equal."); CF_STATIC_ASSERT(sizeof(CF_Ray) == sizeof(c2Ray), "Must be equal."); @@ -198,24 +196,24 @@ bool cf_capsule_to_capsule(CF_Capsule A, CF_Capsule B) return !!c2CapsuletoCapsule(*(c2Capsule*)&A, *(c2Capsule*)&B); } -bool cf_circle_to_poly(CF_Circle A, const CF_Poly* B, const CF_Transform* bx) +bool cf_circle_to_poly(CF_Circle A, const CF_Poly* B) { - return !!c2CircletoPoly(*(c2Circle*)&A, (c2Poly*)B, (c2x*)bx); + return !!c2CircletoPoly(*(c2Circle*)&A, (c2Poly*)B); } -bool cf_aabb_to_poly(CF_Aabb A, const CF_Poly* B, const CF_Transform* bx) +bool cf_aabb_to_poly(CF_Aabb A, const CF_Poly* B) { - return !!c2AABBtoPoly(*(c2AABB*)&A, (c2Poly*)B, (c2x*)bx); + return !!c2AABBtoPoly(*(c2AABB*)&A, (c2Poly*)B); } -bool cf_capsule_to_poly(CF_Capsule A, const CF_Poly* B, const CF_Transform* bx) +bool cf_capsule_to_poly(CF_Capsule A, const CF_Poly* B) { - return !!c2CapsuletoPoly(*(c2Capsule*)&A, (c2Poly*)B, (c2x*)bx); + return !!c2CapsuletoPoly(*(c2Capsule*)&A, (c2Poly*)B); } -bool cf_poly_to_poly(const CF_Poly* A, const CF_Transform* ax, const CF_Poly* B, const CF_Transform* bx) +bool cf_poly_to_poly(const CF_Poly* A, const CF_Poly* B) { - return !!c2PolytoPoly((c2Poly*)A, (c2x*)ax, (c2Poly*)B, (c2x*)bx); + return !!c2PolytoPoly((c2Poly*)A, (c2Poly*)B); } CF_Raycast cf_ray_to_circle(CF_Ray A, CF_Circle B) @@ -248,11 +246,11 @@ CF_Raycast cf_ray_to_capsule(CF_Ray A, CF_Capsule B) return result; } -CF_Raycast cf_ray_to_poly(CF_Ray A, const CF_Poly* B, const CF_Transform* bx_ptr) +CF_Raycast cf_ray_to_poly(CF_Ray A, const CF_Poly* B) { CF_Raycast result; c2Raycast cast; - result.hit = !!c2RaytoPoly(*(c2Ray*)&A, (c2Poly*)B, (c2x*)bx_ptr, (c2Raycast*)&cast); + result.hit = !!c2RaytoPoly(*(c2Ray*)&A, (c2Poly*)B, (c2Raycast*)&cast); result.n = *(v2*)&cast.n; result.t = cast.t; return result; @@ -299,61 +297,61 @@ CF_Manifold cf_capsule_to_capsule_manifold(CF_Capsule A, CF_Capsule B) return *(CF_Manifold*)&m; } -CF_Manifold cf_circle_to_poly_manifold(CF_Circle A, const CF_Poly* B, const CF_Transform* bx) +CF_Manifold cf_circle_to_poly_manifold(CF_Circle A, const CF_Poly* B) { c2Manifold m; - c2CircletoPolyManifold(*(c2Circle*)&A, (c2Poly*)B, (c2x*)bx, &m); + c2CircletoPolyManifold(*(c2Circle*)&A, (c2Poly*)B, &m); return *(CF_Manifold*)&m; } -CF_Manifold cf_aabb_to_poly_manifold(CF_Aabb A, const CF_Poly* B, const CF_Transform* bx) +CF_Manifold cf_aabb_to_poly_manifold(CF_Aabb A, const CF_Poly* B) { c2Manifold m; - c2AABBtoPolyManifold(*(c2AABB*)&A, (c2Poly*)B, (c2x*)bx, &m); + c2AABBtoPolyManifold(*(c2AABB*)&A, (c2Poly*)B, &m); return *(CF_Manifold*)&m; } -CF_Manifold cf_capsule_to_poly_manifold(CF_Capsule A, const CF_Poly* B, const CF_Transform* bx) +CF_Manifold cf_capsule_to_poly_manifold(CF_Capsule A, const CF_Poly* B) { c2Manifold m; - c2CapsuletoPolyManifold(*(c2Capsule*)&A, (c2Poly*)B, (c2x*)bx, &m); + c2CapsuletoPolyManifold(*(c2Capsule*)&A, (c2Poly*)B, &m); return *(CF_Manifold*)&m; } -CF_Manifold cf_poly_to_poly_manifold(const CF_Poly* A, const CF_Transform* ax, const CF_Poly* B, const CF_Transform* bx) +CF_Manifold cf_poly_to_poly_manifold(const CF_Poly* A, const CF_Poly* B) { c2Manifold m; - c2PolytoPolyManifold((c2Poly*)A, (c2x*)ax, (c2Poly*)B, (c2x*)bx, &m); + c2PolytoPolyManifold((c2Poly*)A, (c2Poly*)B, &m); return *(CF_Manifold*)&m; } -float cf_gjk(const void* A, CF_ShapeType typeA, const CF_Transform* ax_ptr, const void* B, CF_ShapeType typeB, const CF_Transform* bx_ptr, CF_V2* outA, CF_V2* outB, bool use_radius, int* iterations, CF_GjkCache* cache) +float cf_gjk(const void* A, CF_ShapeType typeA, const void* B, CF_ShapeType typeB, CF_V2* outA, CF_V2* outB, bool use_radius, int* iterations, CF_GjkCache* cache) { - return c2GJK(A, (C2_TYPE)typeA, (c2x*)ax_ptr, B, (C2_TYPE)typeB, (c2x*)bx_ptr, (c2v*)outA, (c2v*)outB, (int)use_radius, iterations, (c2GJKCache*)cache); + return c2GJK(A, (C2_TYPE)typeA, B, (C2_TYPE)typeB, (c2v*)outA, (c2v*)outB, (int)use_radius, iterations, (c2GJKCache*)cache); } -CF_ToiResult cf_toi(const void* A, CF_ShapeType typeA, const CF_Transform* ax_ptr, CF_V2 vA, const void* B, CF_ShapeType typeB, const CF_Transform* bx_ptr, CF_V2 vB, int use_radius) +CF_ToiResult cf_toi(const void* A, CF_ShapeType typeA, CF_V2 vA, const void* B, CF_ShapeType typeB, CF_V2 vB, int use_radius) { CF_ToiResult result; - c2TOIResult c2result = c2TOI(A, (C2_TYPE)typeA, (c2x*)ax_ptr, *(c2v*)&vA, B, (C2_TYPE)typeB, (c2x*)bx_ptr, *(c2v*)&vB, use_radius); + c2TOIResult c2result = c2TOI(A, (C2_TYPE)typeA, *(c2v*)&vA, B, (C2_TYPE)typeB, *(c2v*)&vB, use_radius); result = *(CF_ToiResult*)&c2result; return result; } -int cf_collided(const void* A, const CF_Transform* ax, CF_ShapeType typeA, const void* B, const CF_Transform* bx, CF_ShapeType typeB) +int cf_collided(const void* A, CF_ShapeType typeA, const void* B, CF_ShapeType typeB) { - return c2Collided(A, (c2x*)ax, (C2_TYPE)typeA, B, (c2x*)bx, (C2_TYPE)typeB); + return c2Collided(A, (C2_TYPE)typeA, B, (C2_TYPE)typeB); } -void cf_collide(const void* A, const CF_Transform* ax, CF_ShapeType typeA, const void* B, const CF_Transform* bx, CF_ShapeType typeB, CF_Manifold* m) +void cf_collide(const void* A, CF_ShapeType typeA, const void* B, CF_ShapeType typeB, CF_Manifold* m) { - c2Collide(A, (c2x*)ax, (C2_TYPE)typeA, B, (c2x*)bx, (C2_TYPE)typeB, (c2Manifold*)m); + c2Collide(A, (C2_TYPE)typeA, B, (C2_TYPE)typeB, (c2Manifold*)m); } -bool cf_cast_ray(CF_Ray A, const void* B, const CF_Transform* bx, CF_ShapeType typeB, CF_Raycast* out) +bool cf_cast_ray(CF_Ray A, const void* B, CF_ShapeType typeB, CF_Raycast* out) { c2Raycast cast; - out->hit = !!c2CastRay(*(c2Ray*)&A, B, (c2x*)bx, (C2_TYPE)typeB, (c2Raycast*)&cast); + out->hit = !!c2CastRay(*(c2Ray*)&A, B, (C2_TYPE)typeB, (c2Raycast*)&cast); out->n = *(v2*)&cast.n; out->t = cast.t; return out->hit; diff --git a/src/cute_png_cache.cpp b/src/cute_png_cache.cpp deleted file mode 100644 index 3f9dd0532..000000000 --- a/src/cute_png_cache.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/* - Cute Framework - Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ - - This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info -*/ - -#include -#include -#include -#include - -#include -#include -#include - -struct CF_PngCache -{ - htbl void** id_to_pixels = NULL; - dyna CF_Animation** animations = NULL; - htbl CF_Animation*** animation_tables = NULL; - htbl CF_Png* pngs = NULL; - uint64_t id_gen = CF_PNG_ID_RANGE_LO; -}; - -CF_GLOBAL static CF_PngCache* cache; - -void cf_png_cache_get_pixels(uint64_t image_id, void* buffer, int bytes_to_fill) -{ - void* pixels = hget(cache->id_to_pixels, image_id); - if (!pixels) { - CF_DEBUG_PRINTF("png cache -- unable to find id %lld.", (long long int)image_id); - CF_MEMSET(buffer, 0, bytes_to_fill); - } else { - CF_MEMCPY(buffer, pixels, bytes_to_fill); - } -} - -void cf_make_png_cache() -{ - cache = CF_NEW(CF_PngCache); -} - -void cf_destroy_png_cache() -{ - for (int i = 0; i < hsize(cache->animations); ++i) { - afree(cache->animations[i]->frames); - CF_FREE(cache->animations[i]); - } - hfree(cache->animations); - - for (int i = 0; i < hsize(cache->animation_tables); ++i) { - hfree(cache->animation_tables[i]); - } - hfree(cache->animation_tables); - - int i = 0; - while (hsize(cache->pngs)) { - CF_Png png = cache->pngs[i++]; - cf_png_cache_unload(png); - } - hfree(cache->pngs); - hfree(cache->id_to_pixels); - - CF_FREE(cache); - cache = NULL; -} - -CF_Result cf_png_cache_load(const char* png_path, CF_Png* png) -{ - CF_Image img; - CF_Result err = cf_image_load_png(png_path, &img); - if (cf_is_error(err)) return err; - cf_image_premultiply(&img); - CF_Png entry; - entry.path = sintern(png_path); - entry.id = cache->id_gen++; - entry.pix = img.pix; - entry.w = img.w; - entry.h = img.h; - hadd(cache->id_to_pixels, entry.id, img.pix); - hadd(cache->pngs, entry.id, entry); - if (png) *png = entry; - return cf_result_success(); -} - -CF_Result cf_png_cache_load_from_memory(const char* png_path, const void* memory, size_t size, CF_Png* png) -{ - CF_Image img; - CF_Result err = cf_image_load_png_from_memory(memory, (int)size, &img); - if (cf_is_error(err)) return err; - CF_Png entry; - entry.path = sintern(png_path); - entry.id = cache->id_gen++; - entry.pix = img.pix; - entry.w = img.w; - entry.h = img.h; - hadd(cache->id_to_pixels, entry.id, img.pix); - hadd(cache->pngs, entry.id, entry); - if (png) *png = entry; - return cf_result_success(); -} - -void cf_png_cache_unload(CF_Png png) -{ - CF_Image img; - img.pix = png.pix; - img.w = png.w; - img.h = png.h; - cf_image_free(&img); - hdel(cache->id_to_pixels, png.id); - hdel(cache->pngs, png.id); -} - -const CF_Animation* cf_make_png_cache_animation(const char* name, const CF_Png* pngs, int pngs_count, const float* delays, int delays_count) -{ - CF_ASSERT(pngs_count == delays_count); - name = sintern(name); - - // If already made, just return the old animation. - CF_Animation* animation = NULL; - if (cache->animations) { - animation = hget(cache->animations, name); - if (animation) return animation; - } - - // Otherwise allocate a new animation. - animation = (CF_Animation*)CF_ALLOC(sizeof(CF_Animation)); - CF_MEMSET(animation, 0, sizeof(CF_Animation)); - animation->name = name; - hadd(cache->animations, name, animation); - - for (int i = 0; i < pngs_count; ++i) { - CF_Frame frame; - frame.id = pngs[i].id; - frame.delay = delays[i]; - cf_animation_add_frame(animation, frame); - } - - return animation; -} - -const CF_Animation* cf_png_cache_get_animation(const char* name) -{ - return hget(cache->animations, sintern(name)); -} - -const CF_Animation** cf_make_png_cache_animation_table(const char* sprite_name, const CF_Animation* const* animations, int animations_count) -{ - sprite_name = sintern(sprite_name); - - // If already made, just return the old table. - CF_Animation** table = NULL; - if (cache->animation_tables) { - table = hget(cache->animation_tables, sprite_name); - if (table) return (const CF_Animation**)table; - } - - // Otherwise allocate a new table entry. - for (int i = 0; i < animations_count; ++i) { - hadd(table, animations[i]->name, (CF_Animation*)animations[i]); - } - hset(cache->animation_tables, sprite_name, table); - - return (const CF_Animation**)table; -} - -const CF_Animation** cf_png_cache_get_animation_table(const char* sprite_name) -{ - return (const CF_Animation**)hget(cache->animation_tables, sintern(sprite_name)); -} - -CF_Sprite cf_make_png_cache_sprite(const char* sprite_name, const CF_Animation** table) -{ - sprite_name = sintern(sprite_name); - CF_Sprite sprite = cf_sprite_defaults(); - sprite.name = sprite_name; - - if (table) { - CF_Png png = hget(cache->pngs, table[0]->frames[0].id); - CF_ASSERT(png.path); - sprite.w = png.w; - sprite.h = png.h; - sprite.animations = table; - cf_sprite_play(&sprite, sprite.animations[0]->name); - } else { - CF_Png entry; - CF_Result result = cf_png_cache_load(sprite_name, &entry); - if (cf_is_error(result)) { - return cf_sprite_defaults(); - } else { - sprite.w = entry.w; - sprite.h = entry.h; - sprite.easy_sprite_id = entry.id; - } - } - - return sprite; -} - -namespace Cute -{ - -const CF_Animation* make_png_cache_animation(const char* name, const Array& pngs, const Array& delays) -{ - return cf_make_png_cache_animation(name, pngs.data(), pngs.count(), delays.data(), delays.count()); -} -const CF_Animation** make_png_cache_animation_table(const char* sprite_name, const Array& animations) -{ - return cf_make_png_cache_animation_table(sprite_name, animations.data(), animations.count()); -} - -} diff --git a/src/cute_sprite.cpp b/src/cute_sprite.cpp index e6fe0e5eb..f3f508ce7 100644 --- a/src/cute_sprite.cpp +++ b/src/cute_sprite.cpp @@ -8,12 +8,27 @@ #include "cute_sprite.h" #include "cute_draw.h" #include "cute_image.h" +#include "cute_file_system.h" + +#include #include #include #include #include +CF_Sprite cf_sprite_defaults() +{ + CF_Sprite sprite = { 0 }; + sprite.id = CF_SPRITE_ID_INVALID; + sprite.scale = cf_v2(1, 1); + sprite.opacity = 1.0f; + sprite.play_speed_multiplier = 1.0f; + sprite.transform = cf_make_transform(); + sprite.loop = true; + return sprite; +} + static CF_Sprite s_insert(CF_Image img) { uint64_t id = app->easy_sprite_id_gen++; @@ -30,6 +45,19 @@ static CF_Sprite s_insert(CF_Image img) CF_Sprite cf_make_easy_sprite_from_png(const char* png_path, CF_Result* result_out) { + png_path = sintern(png_path); + uint64_t* cached_id = app->easy_sprite_id_cache.try_find(png_path); + if (cached_id) { + CF_Image* img = app->easy_sprites.try_find(*cached_id); + if (img) { + CF_Sprite sprite = cf_sprite_defaults(); + sprite.name = png_path; + sprite.w = img->w; + sprite.h = img->h; + sprite.easy_sprite_id = *cached_id; + return sprite; + } + } CF_Image img; CF_Result result = cf_image_load_png(png_path, &img); if (cf_is_error(result)) { @@ -37,7 +65,10 @@ CF_Sprite cf_make_easy_sprite_from_png(const char* png_path, CF_Result* result_o return cf_sprite_defaults(); } cf_image_premultiply(&img); - return s_insert(img); + CF_Sprite sprite = s_insert(img); + sprite.name = png_path; + app->easy_sprite_id_cache.insert(png_path, sprite.easy_sprite_id); + return sprite; } CF_Sprite cf_make_easy_sprite_from_pixels(const CF_Pixel* pixels, int w, int h) @@ -94,11 +125,59 @@ void cf_sprite_unload(const char* aseprite_path) cf_aseprite_cache_unload(aseprite_path); } -CF_Sprite cf_sprite_reload(const CF_Sprite* sprite) +void cf_sprite_reload(CF_Sprite* sprite) { - const char* name = sprite->name; - cf_aseprite_cache_unload(name); - return cf_make_sprite(name); + if (sprite->id == CF_SPRITE_ID_INVALID) return; + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + if (!asset->ase) return; // External assets can't be reloaded from disk. + + const char* path = asset->path; + + // Reload the ase file from disk. + size_t sz = 0; + void* data = cf_fs_read_entire_file_to_memory(path, &sz); + if (!data) return; + + ase_t* new_ase = cute_aseprite_load_from_memory(data, (int)sz, NULL); + CF_FREE(data); + if (!new_ase) return; + + // Premultiply alpha on new frames. + for (int i = 0; i < new_ase->frame_count; ++i) { + ase_color_t* pix = new_ase->frames[i].pixels; + for (int y = 0; y < new_ase->h; ++y) { + for (int x = 0; x < new_ase->w; ++x) { + float a = pix[y * new_ase->w + x].a / 255.0f; + float r = pix[y * new_ase->w + x].r / 255.0f; + float g = pix[y * new_ase->w + x].g / 255.0f; + float b = pix[y * new_ase->w + x].b / 255.0f; + r *= a; g *= a; b *= a; + pix[y * new_ase->w + x].r = (uint8_t)(r * 255.0f); + pix[y * new_ase->w + x].g = (uint8_t)(g * 255.0f); + pix[y * new_ase->w + x].b = (uint8_t)(b * 255.0f); + } + } + } + + // Reuse frame IDs for overlapping frames; allocate new ones for extras. + cf_aseprite_cache_reload_asset(sprite->id, new_ase); + + // Refresh the sprite from the updated asset. + asset = cf_sprite_get_asset(sprite->id); + sprite->w = asset->w; + sprite->h = asset->h; + sprite->name = asset->path; + + // Replay the current animation (or fall back to first). + const char* anim_name = sprite->animation_name; + if (anim_name && map_get(asset->animations, sintern(anim_name))) { + cf_sprite_play(sprite, anim_name); + } else { + CF_Animation** first_anim = map_items(asset->animations); + if (map_size(asset->animations) > 0) { + cf_sprite_play(sprite, (*first_anim)->name); + } + } } void cf_easy_sprite_unload(CF_Sprite *sprite) @@ -108,4 +187,316 @@ void cf_easy_sprite_unload(CF_Sprite *sprite) cf_image_free(img); app->easy_sprites.remove(sprite->easy_sprite_id); } + app->easy_sprite_id_cache.remove(sprite->name); +} + +//-------------------------------------------------------------------------------------------------- +// Sprite animation functions. + +// Helper: get the current CF_Animation* for a sprite via asset. +// animation_name is already interned (set by cf_sprite_play from anim->name). +static const CF_Animation* s_get_animation(const CF_Sprite* sprite) +{ + if (sprite->id != CF_SPRITE_ID_INVALID && sprite->animation_name) { + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + return map_get(asset->animations, sprite->animation_name); + } + return NULL; +} + +// Cache _image_id, _pivot, _center_patch from the current animation state. +static void s_cache_sprite_frame(CF_Sprite* sprite) +{ + if (sprite->id == CF_SPRITE_ID_INVALID) return; + const CF_Animation* anim = s_get_animation(sprite); + if (!anim) return; + int fi = sprite->frame_index; + if (fi >= asize(anim->frames)) fi = asize(anim->frames) - 1; + sprite->_image_id = anim->frames[fi].id; + + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + int global_frame = fi + anim->frame_offset; + sprite->_pivot = asset->pivots ? asset->pivots[global_frame] : cf_v2(0, 0); + CF_Aabb zero_aabb = { 0 }; + sprite->_center_patch = asset->center_patches ? asset->center_patches[global_frame] : zero_aabb; +} + +void cf_sprite_play(CF_Sprite* sprite, const char* animation) +{ + CF_ASSERT(sprite); + if (sprite->id == CF_SPRITE_ID_INVALID) return; + const char* name = sintern(animation); + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + const CF_Animation* anim = map_get(asset->animations, name); + CF_ASSERT(anim); + sprite->animation_name = anim->name; + cf_sprite_reset(sprite); + s_cache_sprite_frame(sprite); +} + +void cf_sprite_reset(CF_Sprite* sprite) +{ + CF_ASSERT(sprite); + sprite->paused = false; + sprite->finished = false; + sprite->frame_index = 0; + sprite->loop_count = 0; + sprite->t = 0; + const CF_Animation* anim = s_get_animation(sprite); + if (anim) sprite->play_direction = anim->play_direction; +} + +bool cf_sprite_is_playing(CF_Sprite* sprite, const char* animation) +{ + CF_ASSERT(sprite); + if (!sprite->animation_name) return false; + return sprite->animation_name == sintern(animation); +} + +int cf_sprite_frame_count(const CF_Sprite* sprite) +{ + CF_ASSERT(sprite); + const CF_Animation* anim = s_get_animation(sprite); + if (!anim) return 0; + return asize(anim->frames); +} + +int cf_sprite_current_global_frame(const CF_Sprite* sprite) +{ + CF_ASSERT(sprite); + const CF_Animation* anim = s_get_animation(sprite); + return sprite->frame_index + (anim ? anim->frame_offset : 0); +} + +void cf_sprite_set_frame(CF_Sprite* sprite, int frame) +{ + CF_ASSERT(sprite); + const CF_Animation* anim = s_get_animation(sprite); + if (!anim) return; + int frame_count = asize(anim->frames); + CF_ASSERT(frame >= 0 && frame < frame_count); + sprite->frame_index = frame; + sprite->t = 0; + s_cache_sprite_frame(sprite); +} + +float cf_sprite_frame_delay(CF_Sprite* sprite) +{ + CF_ASSERT(sprite); + const CF_Animation* anim = s_get_animation(sprite); + if (!anim) return 0; + return anim->frames[sprite->frame_index].delay; +} + +float cf_sprite_animation_delay(CF_Sprite* sprite) +{ + CF_ASSERT(sprite); + const CF_Animation* anim = s_get_animation(sprite); + if (!anim) return 0; + int count = asize(anim->frames); + float delay = 0; + for (int i = 0; i < count; ++i) { + delay += anim->frames[i].delay; + } + return delay; +} + +float cf_sprite_animation_interpolant(CF_Sprite* sprite) +{ + CF_ASSERT(sprite); + const CF_Animation* anim = s_get_animation(sprite); + if (!anim) return 0; + float total = cf_sprite_animation_delay(sprite); + if (total <= 0) return 0; + int frame_count = asize(anim->frames); + + // Sum delays of completed frames based on play direction. + float elapsed = sprite->t; + if (sprite->play_direction == CF_PLAY_DIRECTION_BACKWARDS) { + for (int i = frame_count - 1; i > sprite->frame_index; --i) { + elapsed += anim->frames[i].delay; + } + } else if (sprite->play_direction == CF_PLAY_DIRECTION_PINGPONG) { + if (sprite->loop_count % 2) { + // Backward half: measure progress through backward pass. + for (int i = frame_count - 1; i > sprite->frame_index; --i) { + elapsed += anim->frames[i].delay; + } + } else { + // Forward half: measure progress through forward pass. + for (int i = 0; i < sprite->frame_index; ++i) { + elapsed += anim->frames[i].delay; + } + } + } else { + for (int i = 0; i < sprite->frame_index; ++i) { + elapsed += anim->frames[i].delay; + } + } + + return cf_clamp(elapsed / total, 0.0f, 1.0f); +} + +bool cf_sprite_will_finish(CF_Sprite* sprite) +{ + CF_ASSERT(sprite); + const CF_Animation* anim = s_get_animation(sprite); + if (!anim) return false; + float dt = CF_DELTA_TIME * sprite->play_speed_multiplier; + int frame_count = asize(anim->frames); + + if (sprite->play_direction == CF_PLAY_DIRECTION_FORWARDS) { + if (sprite->frame_index == frame_count - 1) { + return sprite->t + dt >= anim->frames[sprite->frame_index].delay; + } + } else if (sprite->play_direction == CF_PLAY_DIRECTION_BACKWARDS) { + if (sprite->frame_index == 0) { + return sprite->t + dt >= anim->frames[0].delay; + } + } else { // PINGPONG + // Finishes when the backward half completes (full cycle). + if (sprite->loop_count % 2 && sprite->frame_index == 0) { + return sprite->t + dt >= anim->frames[0].delay; + } + } + return false; +} + +CF_Aabb cf_sprite_get_slice(CF_Sprite* sprite, const char* name) +{ + CF_ASSERT(sprite); + CF_Aabb not_found = { 0 }; + if (sprite->id == CF_SPRITE_ID_INVALID) return not_found; + name = sintern(name); + + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + const CF_Animation* anim = s_get_animation(sprite); + int frame = sprite->frame_index + (anim ? anim->frame_offset : 0); + for (int i = 0; i < asize(asset->slices); ++i) { + if (asset->slices[i].name == name && asset->slices[i].frame_index >= frame) { + return asset->slices[i].box; + } + } + return not_found; +} + +void cf_sprite_update(CF_Sprite* sprite) +{ + CF_ASSERT(sprite); + if (sprite->paused) return; + const CF_Animation* anim = s_get_animation(sprite); + if (!anim) return; + + sprite->t += CF_DELTA_TIME * sprite->play_speed_multiplier; + int frame_count = asize(anim->frames); + CF_PlayDirection direction = sprite->play_direction; + if (direction == CF_PLAY_DIRECTION_FORWARDS) { + if (sprite->t >= anim->frames[sprite->frame_index].delay) { + sprite->frame_index++; + sprite->t = 0; + if (sprite->frame_index == frame_count) { + if (sprite->loop) { + sprite->loop_count++; + sprite->frame_index = 0; + } else { + sprite->frame_index--; + sprite->finished = true; + } + } + } + } else if (direction == CF_PLAY_DIRECTION_BACKWARDS) { + if (sprite->t >= anim->frames[sprite->frame_index].delay) { + sprite->frame_index--; + sprite->t = 0; + if (sprite->frame_index < 0) { + if (sprite->loop) { + sprite->loop_count++; + sprite->frame_index = frame_count - 1; + } else { + sprite->frame_index++; + sprite->finished = true; + } + } + } + } else if (direction == CF_PLAY_DIRECTION_PINGPONG) { + if (sprite->t >= anim->frames[sprite->frame_index].delay) { + sprite->t = 0; + if (sprite->loop_count % 2) { + sprite->frame_index--; + if (sprite->frame_index < 0) { + if (sprite->loop) { + sprite->loop_count++; + sprite->frame_index++; + } else { + sprite->frame_index = 0; + sprite->finished = true; + } + } + } else { + sprite->frame_index++; + if (sprite->frame_index == frame_count) { + sprite->loop_count++; + sprite->frame_index--; + } + } + } + } + + s_cache_sprite_frame(sprite); +} + +int cf_sprite_animation_count(const CF_Sprite* sprite) +{ + CF_ASSERT(sprite); + if (sprite->id == CF_SPRITE_ID_INVALID) return 0; + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + return map_size(asset->animations); +} + +const char* cf_sprite_animation_name_at(const CF_Sprite* sprite, int index) +{ + CF_ASSERT(sprite); + if (sprite->id == CF_SPRITE_ID_INVALID) return NULL; + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + if (index < 0 || index >= map_size(asset->animations)) return NULL; + CF_Animation** anim_vals = map_items(asset->animations); + return anim_vals[index]->name; +} + +CF_V2 cf_sprite_pivot(const CF_Sprite* sprite) +{ + CF_ASSERT(sprite); + return sprite->_pivot; +} + +int cf_sprite_add_blend(const char* path, const char** layer_names, int layer_count) +{ + // Blends require aseprite layer data -- PNG sprites have no layers. + if (!ssuffix(path, ".ase") && !ssuffix(path, ".aseprite")) { + char buf[1024]; + snprintf(buf, sizeof buf, + "cf_sprite_add_blend: \"%s\" is not an .ase/.aseprite file.\n" + "Layer blends only work with aseprite sprites, not PNG sprites.", + path); + cf_message_box(CF_MESSAGE_BOX_TYPE_ERROR, "ERROR", buf); + return -1; + } + + // Ensure asset is loaded. + CF_Sprite tmp = cf_sprite_defaults(); + cf_aseprite_cache_load(path, &tmp); + + // Build mask and add blend. + ase_t* ase = NULL; + cf_aseprite_cache_load_ase(path, &ase); + uint64_t mask = cf_aseprite_layer_mask(ase, layer_names, layer_count); + return cf_aseprite_cache_add_blend(path, mask); +} + +int cf_sprite_blend_count(const CF_Sprite* sprite) +{ + CF_ASSERT(sprite); + if (sprite->id == CF_SPRITE_ID_INVALID) return 1; + CF_SpriteAsset* asset = cf_sprite_get_asset(sprite->id); + return asset->blend_count; } diff --git a/src/cute_string.cpp b/src/cute_string.cpp deleted file mode 100644 index 33179f6d6..000000000 --- a/src/cute_string.cpp +++ /dev/null @@ -1,600 +0,0 @@ -/* - Cute Framework - Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ - - This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info -*/ - -#include "cute_string.h" -#include "cute_multithreading.h" -#include "cute_array.h" -#include "cute_math.h" - -#include - -#include -#include - -using namespace Cute; - -char* cf_sfit(char* a, int n) -{ - cf_array_fit(a, n + 1); - if (scount(a) == 0) apush(a, 0); - return a; -} - -char* cf_sset(char* a, const char* b) -{ - CF_ACANARY(a); - if (!b) return NULL; - int bsize = (int)(b ? CF_STRLEN(b) : 0) + 1; - if (!bsize) { - sclear(a); - return a; - } else if (acap(a) < bsize) { - a = (char*)cf_agrow(a, bsize, 1); - } - CF_MEMCPY((void*)a, b, bsize); - alen(a) = bsize; - return a; -} - -char* cf_sfmt(char* s, const char* fmt, ...) -{ - CF_ACANARY(s); - va_list args; - va_start(args, fmt); - int n = 1+vsnprintf(s, scap(s), fmt, args); - va_end(args); - if (n > scap(s)) { - sfit(s, n); - va_start(args, fmt); - n = 1+vsnprintf(s, scap(s), fmt, args); - va_end(args); - } - alen(s) = n; - return s; -} - -char* cf_sfmt_append(char* s, const char* fmt, ...) -{ - CF_ACANARY(s); - va_list args; - va_start(args, fmt); - int nul = !!s; - int capacity = scap(s) - scount(s); - int n = 1+vsnprintf(s + slen(s), capacity, fmt, args); - va_end(args); - if (n > capacity) { - afit(s, n + scount(s)); - va_start(args, fmt); - int new_capacity = scap(s) - scount(s); - n = 1+vsnprintf(s + slen(s), new_capacity, fmt, args); - CF_ASSERT(n <= new_capacity); - va_end(args); - } - alen(s) += n-nul; - return s; -} - -char* cf_svfmt(char* s, const char* fmt, va_list args) -{ - CF_ACANARY(s); - va_list copy_args; - va_copy(copy_args, args); - int n = 1+vsnprintf(s, scap(s), fmt, args); - if (n > scap(s)) { - sfit(s, n); - n = 1+vsnprintf(s, scap(s), fmt, copy_args); - va_end(copy_args); - } - alen(s) = n; - return s; -} - -char* cf_svfmt_append(char* s, const char* fmt, va_list args) -{ - CF_ACANARY(s); - va_list copy_args; - va_copy(copy_args, args); - int nul = !!s; - int capacity = scap(s) - scount(s); - int n = 1+vsnprintf(s + slen(s), capacity, fmt, copy_args); - va_end(copy_args); - if (n > capacity) { - afit(s, n + scount(s)); - int new_capacity = scap(s) - scount(s); - n = 1+vsnprintf(s + slen(s), new_capacity, fmt, args); - CF_ASSERT(n <= new_capacity); - } - alen(s) += n-nul; - return s; -} - -bool cf_sprefix(char* s, const char* prefix) -{ - CF_ACANARY(s); - if (!s) return false; - int prefix_len = (int)(prefix ? CF_STRLEN(prefix) : 0); - bool a = slen(s) >= prefix_len; - bool b = !CF_MEMCMP(s, prefix, prefix_len); - return a && b; -} - -bool cf_ssuffix(char* s, const char* suffix) -{ - CF_ACANARY(s); - if (!s) return false; - int suffix_len = (int)(suffix ? CF_STRLEN(suffix) : 0); - bool a = slen(s) >= suffix_len; - bool b = !CF_MEMCMP(s + slen(s) - suffix_len, suffix, suffix_len); - return a && b; -} - -void cf_stoupper(char* s) -{ - CF_ACANARY(s); - if (!s) return; - for (int i = 0; i < slen(s); ++i) { - s[i] = toupper(s[i]); - } -} - -void cf_stolower(char* s) -{ - CF_ACANARY(s); - if (!s) return; - for (int i = 0; i < slen(s); ++i) { - s[i] = tolower(s[i]); - } -} - -char* cf_sappend(char* a, const char* b) -{ - CF_ACANARY(a); - int blen = (int)CF_STRLEN(b); - if (blen <= 0) return a; - sfit(a, slen(a) + blen + 1); - CF_MEMCPY(a + slen(a), b, blen); - alen(a) += blen; - a[slen(a)] = 0; - return a; -} - -char* cf_sappend_range(char* a, const char* b, const char* b_end) -{ - CF_ACANARY(a); - int blen = (int)(b_end - b); - if (blen <= 0) return a; - sfit(a, slen(a) + blen + 1); - CF_MEMCPY(a + slen(a), b, blen); - alen(a) += blen; - a[slen(a)] = 0; - return a; -} - -char* cf_strim(char* s) -{ - CF_ACANARY(s); - int len = slen(s); - if (len <= 0) return s; - char* start = s; - char* end = s + len - 1; - while (start <= end && isspace((unsigned char)*start)) start++; - while (end >= start && isspace((unsigned char)*end)) end--; - int new_len = (int)(end >= start ? (end - start + 1) : 0); - if (new_len > 0) { - CF_MEMMOVE(s, start, (size_t)new_len); - } - s[new_len] = 0; - alen(s) = new_len + 1; - return s; -} - -char* cf_sltrim(char* s) -{ - CF_ACANARY(s); - int len = slen(s); - if (len <= 0) return s; - char* start = s; - char* end = s + len - 1; - while (start <= end && isspace((unsigned char)*start)) start++; - int new_len = (int)(end >= start ? (end - start + 1) : 0); - if (new_len > 0) { - CF_MEMMOVE(s, start, (size_t)new_len); - } - s[new_len] = 0; - alen(s) = new_len + 1; - return s; -} - -char* cf_srtrim(char* s) -{ - CF_ACANARY(s); - int len = slen(s); - if (len <= 0) return s; - char* end = s + len - 1; - while (end >= s && isspace((unsigned char)*end)) { - --end; - } - int new_len = (int)(end - s + 1); - if (new_len < 0) new_len = 0; - s[new_len] = 0; - alen(s) = new_len + 1; - return s; -} - -char* cf_slpad(char* s, char pad, int count) -{ - CF_ACANARY(s); - int cap = scap(s) - scount(s); - if (cap < count) { - sfit(s, scount(s) + cap); - } - CF_MEMMOVE(s + count, s, scount(s)); - CF_MEMSET(s, pad, count); - alen(s) += count; - return s; -} - -char* cf_srpad(char* s, char pad, int count) -{ - CF_ACANARY(s); - int cap = scap(s) - scount(s); - if (cap < count) { - sfit(s, scount(s) + cap); - } - CF_MEMSET(s + slen(s), pad, count); - alen(s) += count; - s[slen(s)] = 0; - return s; -} - -char* cf_ssplit_once(char* s, char split_c) -{ - CF_ACANARY(s); - char* start = s; - char* end = s + slen(s) - 1; - while (start < end) { - if (*start == split_c) { - break; - } - ++start; - } - int len = (int)(start - s); - if (len + 1 == slen(s)) return NULL; - char* split = NULL; - sfit(split, len + 1); - alen(split) = len + 1; - CF_MEMCPY(split, s, len); - split[len] = 0; - int new_len = slen(s) - len - 1; - CF_MEMMOVE(s, s + len + 1, new_len); - alen(s) = new_len + 1; - s[new_len] = 0; - return split; -} - -char** cf_ssplit(const char* s, char split_c) -{ - char* copy = NULL; - char** result = NULL; - char* split = NULL; - sset(copy, s); - while ((split = ssplit_once(copy, split_c))) { - apush(result, split); - } - apush(result, copy); - return result; -} - -int cf_sfirst_index_of(const char* s, char c) -{ - if (!s) return -1; - const char* p = CF_STRCHR(s, c); - if (!p) return -1; - return ((int)(uintptr_t)(p - s)); -} - -int cf_slast_index_of(const char* s, char c) -{ - if (!s) return -1; - const char* p = CF_STRRCHR(s, c); - if (!p) return -1; - return ((int)(uintptr_t)(p - s)); -} - -static uint64_t s_stoint(const char* s) -{ - char* end; - uint64_t result = CF_STRTOLL(s, &end, 10); - CF_ASSERT(end == s + CF_STRLEN(s)); - return result; -} - -int cf_stoint(const char* s) -{ - return (int)s_stoint(s); -} - -uint64_t cf_stouint(const char* s) -{ - return s_stoint(s); -} - -static double s_stod(const char* s) -{ - char* end; - double result = CF_STRTOD(s, &end); - CF_ASSERT(end == s + CF_STRLEN(s)); - return result; -} - -float cf_stofloat(const char* s) -{ - return (float)s_stod(s); -} - -double cf_stodouble(const char* s) -{ - return s_stod(s); -} - -uint64_t cf_stohex(const char* s) -{ - if (!CF_STRNCMP(s, "#", 1)) s += 1; - if (!CF_STRNCMP(s, "0x", 2)) s += 2; - int len = (int)CF_STRLEN(s); - if (len != 6 && len != 8) return 0; - char* end; - uint64_t result = CF_STRTOLL(s, &end, 16); - CF_ASSERT(end == s + len); - return len == 6 ? ((result << 8) | 0xFF) : result; -} - -char* cf_sreplace(char* s, const char* replace_me, const char* with_me) -{ - CF_ACANARY(s); - if (!s) return NULL; - size_t replace_len = CF_STRLEN(replace_me); - size_t with_len = CF_STRLEN(with_me); - char* find; - char* search = s; - while ((find = sfind(search, replace_me))) { - int find_offset = (int)(find - s); - if (replace_len > with_len) { - int remaining = scount(s) - find_offset - (int)replace_len; - int diff = (int)(replace_len - with_len); - CF_MEMCPY(find, with_me, with_len); - CF_MEMMOVE(find + with_len, find + replace_len, remaining); - alen(s) -= diff; - } else { - int remaining = scount(s) - find_offset - (int)replace_len; - int diff = (int)(with_len - replace_len); - sfit(s, scount(s) + diff); - find = s + find_offset; - CF_MEMMOVE(find + with_len, find + replace_len, remaining); - CF_MEMCPY(find, with_me, with_len); - alen(s) += diff; - } - search = find + with_len; - } - return s; -} - -char* cf_sdedup(char* s, int ch) -{ - CF_ACANARY(s); - if (!s) return NULL; - int len = (int)CF_STRLEN(s); - int i = 0, j = 1; - bool dup = false; - while (j < len) { - if (s[i] == ch && s[j] == ch) { - dup = true; - ++j; - } else { - ++i; - if (dup) { - s[i] = s[j]; - } - ++j; - } - } - s[i + 1] = 0; - alen(s) = i + 1; - return s; -} - -char* cf_serase(char* s, int index, int count) -{ - CF_ACANARY(s); - if (!s) return NULL; - if (index < 0) { - count += index; - index = 0; - if (count <= 0) return s; - } - if (index >= slen(s)) return s; - if (index + count >= slen(s)) { - alen(s) = index + 1; - s[index] = 0; - return s; - } else { - int remaining = scount(s) - (count + index); - CF_MEMMOVE(s + index, s + count + index, remaining); - alen(s) -= count; - } - return s; -} - -char* cf_spop(char* s) -{ - CF_ACANARY(s); - if (s && slen(s)) s[--alen(s) - 1] = 0; - return s; -} - -char* cf_spopn(char* s, int n) -{ - CF_ACANARY(s); - if (!s || n < 0) return s; - while (scount(s) > 1 && n--) { - alen(s)--; - } - s[slen(s)] = 0; - return s; -} - -using intern_t = cf_intern_t; - -struct intern_table_t -{ - htbl intern_t** interns; - CF_Arena arena; - CF_ReadWriteLock lock; - - CF_INLINE void read_lock() { cf_read_lock(&lock); } - CF_INLINE void read_unlock() { cf_read_unlock(&lock); } - CF_INLINE void write_lock() { cf_write_lock(&lock); } - CF_INLINE void write_unlock() { cf_write_unlock(&lock); } -}; - -static intern_table_t* g_intern_table; - -static intern_table_t* s_inst() -{ - // Locklessly get/instantiate a global instance. - intern_table_t* inst = (intern_table_t*)cf_atomic_ptr_get((void**)&g_intern_table); - if (!inst) { - // Create a new instance of the table. - inst = (intern_table_t*)CF_ALLOC(sizeof(intern_table_t)); - CF_MEMSET(inst, 0, sizeof(*inst)); - inst->interns = NULL; - inst->arena.alignment = 8; - inst->arena.block_size = CF_MB; - inst->lock = cf_make_rw_lock(); - - // Try and set the global pointer. If this fails it means another thread - // has raced us and completed first, so then just destroy ours and use theirs. - CF_Result result = cf_atomic_ptr_cas((void**)&g_intern_table, NULL, inst); - if (is_error(result)) { - cf_destroy_rw_lock(&inst->lock); - CF_FREE(inst); - inst = (intern_table_t*)cf_atomic_ptr_get((void**)&g_intern_table); - CF_ASSERT(inst); - } - } - return inst; -} - -const char* cf_sintern(const char* s) -{ - return s ? cf_sintern_range(s, s + CF_STRLEN(s)) : NULL; -} - -const char* cf_sintern_range(const char* start, const char* end) -{ - intern_table_t* table = s_inst(); - intern_t* intern = NULL; - int len = (int)(end - start); - uint64_t hash = fnv1a(start, len); - - // Fast-path, fetch already intern'd strings and return them as-is. - // Uses a mere read-lock, which is just a couple atomic read operations - // and supports *many* simultaneous lockless readers. - if (table->interns) { - table->read_lock(); - intern = hget(table->interns, hash); - table->read_unlock(); - if (intern) { - // This loop is highly unlikely to ever actually run more than one iteration. - intern_t* i = intern; - while (i) { - if (len == i->len && !CF_STRNCMP(i->string, start, len)) { - return i->string; - } - i = i->next; - } - } - } - - // String is not yet interned, create a new allocation for it. - // We write-lock the data structure, this will wait for all readers to flush. - CF_ASSERT(!intern || intern->cookie == CF_INTERN_COOKIE); - intern_t* list = intern; - table->write_lock(); - intern = (intern_t*)arena_alloc(&table->arena, sizeof(intern_t) + len + 1); - hset(table->interns, hash, intern); - intern->cookie = CF_INTERN_COOKIE; - intern->len = len; - intern->string = (char*)(intern + 1); - CF_MEMCPY((char*)intern->string, start, len); - ((char*)intern->string)[len] = 0; - intern->next = list; - table->write_unlock(); - - // Return a copy of the string as a stable pointer. - return intern->string; -} - -void cf_sinuke_intern_table(void) -{ - intern_table_t* table = s_inst(); - table->write_lock(); - cf_atomic_ptr_set((void**)&g_intern_table, NULL); - table->write_unlock(); - arena_reset(&table->arena); - hfree(table->interns); - cf_destroy_rw_lock(&table->lock); - CF_FREE(table); -} - -// All invalid characters are encoded as the "replacement character" 0xFFFD for both -// UTF8 and UTF16 functions. - -char* cf_string_append_UTF8_impl(char *s, int codepoint) -{ - CF_ACANARY(s); - if (codepoint > 0x10FFFF) codepoint = 0xFFFD; -#define CF_EMIT(X, Y, Z) spush(s, X | ((codepoint >> Y) & Z)) - if (codepoint < 0x80) { CF_EMIT(0x00,0,0x7F); } - else if (codepoint < 0x800) { CF_EMIT(0xC0,6,0x1F); CF_EMIT(0x80, 0, 0x3F); } - else if (codepoint < 0x10000) { CF_EMIT(0xE0,12,0xF); CF_EMIT(0x80, 6, 0x3F); CF_EMIT(0x80, 0, 0x3F); } - else { CF_EMIT(0xF0,18,0x7); CF_EMIT(0x80, 12, 0x3F); CF_EMIT(0x80, 6, 0x3F); CF_EMIT(0x80, 0, 0x3F); } - return s; -} - -const char* cf_decode_UTF8(const char* s, int* codepoint) -{ - unsigned char c = *s++; - int extra = 0; - int min = 0; - *codepoint = 0; - if (c >= 0xF0) { *codepoint = c & 0x07; extra = 3; min = 0x10000; } - else if (c >= 0xE0) { *codepoint = c & 0x0F; extra = 2; min = 0x800; } - else if (c >= 0xC0) { *codepoint = c & 0x1F; extra = 1; min = 0x80; } - else if (c >= 0x80) { *codepoint = 0xFFFD; } - else *codepoint = c; - while (extra--) { - c = *s++; - if ((c & 0xC0) != 0x80) { *codepoint = 0xFFFD; } - if (*codepoint != 0xFFFD) { *codepoint = ((*codepoint) << 6) | (c & 0x3F); } - } - if (*codepoint < min) *codepoint = 0xFFFD; - return s; -} - -const uint16_t* cf_decode_UTF16(const uint16_t* s, int* codepoint) -{ - int W1 = *s++; - if (W1 < 0xD800 || W1 > 0xDFFF) { - *codepoint = W1; - } else if (W1 > 0xD800 && W1 < 0xDBFF) { - int W2 = *s++; - if (W2 > 0xDC00 && W2 < 0xDFFF) *codepoint = 0x10000 + (((W1 & 0x03FF) << 10) | (W2 & 0x03FF)); - else *codepoint = 0xFFFD; - } else *codepoint = 0xFFFD; - return s; -} diff --git a/src/cute_time.cpp b/src/cute_time.cpp index 7640bebe1..db36f2610 100644 --- a/src/cute_time.cpp +++ b/src/cute_time.cpp @@ -256,6 +256,16 @@ bool cf_is_paused() return pause_ticks > 0; } +void cf_pause() +{ + pause_ticks = INT64_MAX; +} + +void cf_unpause() +{ + pause_ticks = 0; +} + uint64_t cf_get_ticks() { s_init(); diff --git a/src/data/builtin_shaders_bytecode.h b/src/data/builtin_shaders_bytecode.h index 90596d699..6cb395554 100644 --- a/src/data/builtin_shaders_bytecode.h +++ b/src/data/builtin_shaders_bytecode.h @@ -5,419 +5,344 @@ #line 1 0 -layout(location = 0) in vec2 in_pos; -layout(location = 1) in vec2 in_posH; -layout(location = 2) in vec2 in_uv; - -layout(location = 3) in int in_n; -layout(location = 4) in vec4 in_ab; -layout(location = 5) in vec4 in_cd; -layout(location = 6) in vec4 in_ef; -layout(location = 7) in vec4 in_gh; -layout(location = 8) in vec4 in_col; -layout(location = 9) in float in_radius; -layout(location = 10) in float in_stroke; -layout(location = 11) in float in_aa; -layout(location = 12) in vec4 in_params; -layout(location = 13) in vec4 in_user_params; - -layout(location = 0) out vec2 v_pos; +layout(location = 0) in vec4 in_pos_uv; +layout(location = 1) in int in_n; +layout(location = 2) in vec4 in_ab; +layout(location = 3) in vec4 in_cd; +layout(location = 4) in vec4 in_ef; +layout(location = 5) in vec4 in_gh; +layout(location = 6) in vec4 in_col; +layout(location = 7) in vec4 in_shape; +layout(location = 8) in vec4 in_blend_posH; +layout(location = 9) in vec4 in_user; +layout(location = 10) in vec4 in_uv_bounds; + +layout(location = 0) out vec4 v_pos_uv; layout(location = 1) out int v_n; layout(location = 2) out vec4 v_ab; layout(location = 3) out vec4 v_cd; layout(location = 4) out vec4 v_ef; layout(location = 5) out vec4 v_gh; -layout(location = 6) out vec2 v_uv; -layout(location = 7) out vec4 v_col; -layout(location = 8) out float v_radius; -layout(location = 9) out float v_stroke; -layout(location = 10) out float v_aa; -layout(location = 11) out float v_type; -layout(location = 12) out float v_alpha; -layout(location = 13) out float v_fill; -layout(location = 14) out vec2 v_posH; -layout(location = 15) out vec4 v_user; +layout(location = 6) out vec4 v_col; +layout(location = 7) out vec4 v_shape; +layout(location = 8) out vec4 v_blend_posH; +layout(location = 9) out vec4 v_user; +layout(location = 10) out vec4 v_uv_bounds; void main() { - v_pos = in_pos; + v_pos_uv = in_pos_uv; v_n = in_n; v_ab = in_ab; v_cd = in_cd; v_ef = in_ef; v_gh = in_gh; - v_uv = in_uv; v_col = in_col; - v_radius = in_radius; - v_stroke = in_stroke * 0.5; - v_aa = in_aa; - v_type = in_params.r; - v_alpha = in_params.g; - v_fill = in_params.b; + v_shape = vec4(in_shape.x, in_shape.y * 0.5, in_shape.zw); + v_blend_posH = in_blend_posH; + v_user = in_user; + v_uv_bounds = in_uv_bounds; - - vec4 posH = vec4(in_posH, 0, 1); - gl_Position = posH; - v_posH = in_posH; - v_user = in_user_params; + gl_Position = vec4(in_blend_posH.zw, 0, 1); } */ #ifndef __EMSCRIPTEN__ -static const uint8_t s_draw_vs_bytecode_content[3176] = { - 0x03,0x02,0x23,0x07,0x00,0x03,0x01,0x00,0x0B,0x00,0x08,0x00,0x58,0x00,0x00,0x00, +static const uint8_t s_draw_vs_bytecode_content[2628] = { + 0x03,0x02,0x23,0x07,0x00,0x03,0x01,0x00,0x0B,0x00,0x08,0x00,0x4A,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0B,0x00,0x06,0x00, 0x01,0x00,0x00,0x00,0x47,0x4C,0x53,0x4C,0x2E,0x73,0x74,0x64,0x2E,0x34,0x35,0x30, 0x00,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0F,0x00,0x24,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, + 0x0F,0x00,0x1C,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, 0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, - 0x11,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x19,0x00,0x00,0x00, - 0x1A,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x1F,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x25,0x00,0x00,0x00, - 0x26,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x2D,0x00,0x00,0x00, - 0x2E,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x35,0x00,0x00,0x00, - 0x36,0x00,0x00,0x00,0x3B,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x45,0x00,0x00,0x00, - 0x4F,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x55,0x00,0x00,0x00,0x56,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x1C,0x00,0x00,0x00, + 0x1D,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x37,0x00,0x00,0x00, + 0x38,0x00,0x00,0x00,0x3A,0x00,0x00,0x00,0x3B,0x00,0x00,0x00,0x40,0x00,0x00,0x00, 0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xC2,0x01,0x00,0x00,0x04,0x00,0x09,0x00, 0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x68,0x61,0x64,0x69,0x6E,0x67,0x5F,0x6C, 0x61,0x6E,0x67,0x75,0x61,0x67,0x65,0x5F,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x00, 0x04,0x00,0x0A,0x00,0x47,0x4C,0x5F,0x47,0x4F,0x4F,0x47,0x4C,0x45,0x5F,0x63,0x70, 0x70,0x5F,0x73,0x74,0x79,0x6C,0x65,0x5F,0x6C,0x69,0x6E,0x65,0x5F,0x64,0x69,0x72, 0x65,0x63,0x74,0x69,0x76,0x65,0x00,0x00,0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00, - 0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x09,0x00,0x00,0x00, - 0x76,0x5F,0x70,0x6F,0x73,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x0B,0x00,0x00,0x00, - 0x69,0x6E,0x5F,0x70,0x6F,0x73,0x00,0x00,0x05,0x00,0x03,0x00,0x0F,0x00,0x00,0x00, - 0x76,0x5F,0x6E,0x00,0x05,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x69,0x6E,0x5F,0x6E, - 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x76,0x5F,0x61,0x62, - 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x69,0x6E,0x5F,0x61, - 0x62,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x76,0x5F,0x63,0x64, - 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x1A,0x00,0x00,0x00,0x69,0x6E,0x5F,0x63, - 0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x1C,0x00,0x00,0x00,0x76,0x5F,0x65,0x66, - 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x1D,0x00,0x00,0x00,0x69,0x6E,0x5F,0x65, - 0x66,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x1F,0x00,0x00,0x00,0x76,0x5F,0x67,0x68, - 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x69,0x6E,0x5F,0x67, - 0x68,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x22,0x00,0x00,0x00,0x76,0x5F,0x75,0x76, - 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x69,0x6E,0x5F,0x75, - 0x76,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x25,0x00,0x00,0x00,0x76,0x5F,0x63,0x6F, - 0x6C,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x69,0x6E,0x5F,0x63, - 0x6F,0x6C,0x00,0x00,0x05,0x00,0x05,0x00,0x29,0x00,0x00,0x00,0x76,0x5F,0x72,0x61, - 0x64,0x69,0x75,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x2B,0x00,0x00,0x00, - 0x69,0x6E,0x5F,0x72,0x61,0x64,0x69,0x75,0x73,0x00,0x00,0x00,0x05,0x00,0x05,0x00, - 0x2D,0x00,0x00,0x00,0x76,0x5F,0x73,0x74,0x72,0x6F,0x6B,0x65,0x00,0x00,0x00,0x00, - 0x05,0x00,0x05,0x00,0x2E,0x00,0x00,0x00,0x69,0x6E,0x5F,0x73,0x74,0x72,0x6F,0x6B, - 0x65,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x76,0x5F,0x61,0x61, - 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x69,0x6E,0x5F,0x61, - 0x61,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x35,0x00,0x00,0x00,0x76,0x5F,0x74,0x79, - 0x70,0x65,0x00,0x00,0x05,0x00,0x05,0x00,0x36,0x00,0x00,0x00,0x69,0x6E,0x5F,0x70, - 0x61,0x72,0x61,0x6D,0x73,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x3B,0x00,0x00,0x00, - 0x76,0x5F,0x61,0x6C,0x70,0x68,0x61,0x00,0x05,0x00,0x04,0x00,0x3F,0x00,0x00,0x00, - 0x76,0x5F,0x66,0x69,0x6C,0x6C,0x00,0x00,0x05,0x00,0x04,0x00,0x44,0x00,0x00,0x00, - 0x70,0x6F,0x73,0x48,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x45,0x00,0x00,0x00, - 0x69,0x6E,0x5F,0x70,0x6F,0x73,0x48,0x00,0x05,0x00,0x06,0x00,0x4D,0x00,0x00,0x00, - 0x67,0x6C,0x5F,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78,0x00,0x00,0x00,0x00, - 0x06,0x00,0x06,0x00,0x4D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x67,0x6C,0x5F,0x50, - 0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x00,0x06,0x00,0x07,0x00,0x4D,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x67,0x6C,0x5F,0x50,0x6F,0x69,0x6E,0x74,0x53,0x69,0x7A,0x65, - 0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x4D,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x67,0x6C,0x5F,0x43,0x6C,0x69,0x70,0x44,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x00, - 0x06,0x00,0x07,0x00,0x4D,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x67,0x6C,0x5F,0x43, - 0x75,0x6C,0x6C,0x44,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x00,0x05,0x00,0x03,0x00, - 0x4F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x53,0x00,0x00,0x00, - 0x76,0x5F,0x70,0x6F,0x73,0x48,0x00,0x00,0x05,0x00,0x04,0x00,0x55,0x00,0x00,0x00, - 0x76,0x5F,0x75,0x73,0x65,0x72,0x00,0x00,0x05,0x00,0x06,0x00,0x56,0x00,0x00,0x00, - 0x69,0x6E,0x5F,0x75,0x73,0x65,0x72,0x5F,0x70,0x61,0x72,0x61,0x6D,0x73,0x00,0x00, - 0x47,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0B,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x0F,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x15,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x1A,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x1C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x1D,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x1F,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x05,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x20,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x22,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x23,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x25,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x26,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x29,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x2B,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x2D,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x09,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x2E,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0A,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x32,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0A,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x33,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0B,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x35,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0B,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x36,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0C,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x3B,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0C,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x3F,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0D,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x45,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x47,0x00,0x03,0x00,0x4D,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x4D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x4D,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0B,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x4D,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x0B,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x4D,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x53,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x55,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x56,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x13,0x00,0x02,0x00, - 0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x0A,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x0A,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x0D,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x0E,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x0E,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00, - 0x13,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x16,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x16,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x16,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x16,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x08,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x0A,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x16,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x28,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x2A,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x2A,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x28,0x00,0x00,0x00,0x2D,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x2A,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x3B,0x00,0x04,0x00, - 0x28,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x2A,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x28,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x16,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x37,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, - 0x37,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x28,0x00,0x00,0x00,0x3B,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, - 0x37,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x28,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, - 0x37,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x43,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x0A,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x00,0x00,0x80,0x3F,0x1C,0x00,0x04,0x00, - 0x4C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x1E,0x00,0x06,0x00, - 0x4D,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x4C,0x00,0x00,0x00, - 0x4C,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x4E,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x4D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x4E,0x00,0x00,0x00,0x4F,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x0D,0x00,0x00,0x00,0x50,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x53,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x55,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x56,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x05,0x00,0x00,0x00, - 0x3B,0x00,0x04,0x00,0x43,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0B,0x00,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x09,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x0D,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x0F,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x13,0x00,0x00,0x00, - 0x18,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x15,0x00,0x00,0x00, - 0x18,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x1B,0x00,0x00,0x00, - 0x1A,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x19,0x00,0x00,0x00,0x1B,0x00,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x1D,0x00,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x1C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x13,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x1F,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x22,0x00,0x00,0x00, - 0x24,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x27,0x00,0x00,0x00, - 0x26,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x25,0x00,0x00,0x00,0x27,0x00,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x2C,0x00,0x00,0x00,0x2B,0x00,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x29,0x00,0x00,0x00,0x2C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0x85,0x00,0x05,0x00, - 0x06,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x30,0x00,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x2D,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x32,0x00,0x00,0x00,0x34,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x2A,0x00,0x00,0x00, - 0x39,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x3A,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x35,0x00,0x00,0x00,0x3A,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x2A,0x00,0x00,0x00, - 0x3D,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x3D,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x3B,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x2A,0x00,0x00,0x00, - 0x41,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x3F,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x46,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00, - 0x49,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, - 0x06,0x00,0x00,0x00,0x4A,0x00,0x00,0x00,0x46,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x50,0x00,0x07,0x00,0x13,0x00,0x00,0x00,0x4B,0x00,0x00,0x00,0x49,0x00,0x00,0x00, - 0x4A,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x44,0x00,0x00,0x00,0x4B,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x13,0x00,0x00,0x00, - 0x51,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x14,0x00,0x00,0x00, - 0x52,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x52,0x00,0x00,0x00,0x51,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x54,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x53,0x00,0x00,0x00, - 0x54,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x57,0x00,0x00,0x00, - 0x56,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x55,0x00,0x00,0x00,0x57,0x00,0x00,0x00, - 0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00, + 0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x09,0x00,0x00,0x00, + 0x76,0x5F,0x70,0x6F,0x73,0x5F,0x75,0x76,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0x0B,0x00,0x00,0x00,0x69,0x6E,0x5F,0x70,0x6F,0x73,0x5F,0x75,0x76,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x0F,0x00,0x00,0x00,0x76,0x5F,0x6E,0x00,0x05,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x69,0x6E,0x5F,0x6E,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x76,0x5F,0x61,0x62,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x14,0x00,0x00,0x00,0x69,0x6E,0x5F,0x61,0x62,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x16,0x00,0x00,0x00,0x76,0x5F,0x63,0x64,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x17,0x00,0x00,0x00,0x69,0x6E,0x5F,0x63,0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x19,0x00,0x00,0x00,0x76,0x5F,0x65,0x66,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x1A,0x00,0x00,0x00,0x69,0x6E,0x5F,0x65,0x66,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x1C,0x00,0x00,0x00,0x76,0x5F,0x67,0x68,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x1D,0x00,0x00,0x00,0x69,0x6E,0x5F,0x67,0x68,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x1F,0x00,0x00,0x00,0x76,0x5F,0x63,0x6F,0x6C,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x20,0x00,0x00,0x00,0x69,0x6E,0x5F,0x63,0x6F,0x6C,0x00,0x00,0x05,0x00,0x04,0x00, + 0x22,0x00,0x00,0x00,0x76,0x5F,0x73,0x68,0x61,0x70,0x65,0x00,0x05,0x00,0x05,0x00, + 0x23,0x00,0x00,0x00,0x69,0x6E,0x5F,0x73,0x68,0x61,0x70,0x65,0x00,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x34,0x00,0x00,0x00,0x76,0x5F,0x62,0x6C,0x65,0x6E,0x64,0x5F, + 0x70,0x6F,0x73,0x48,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x35,0x00,0x00,0x00, + 0x69,0x6E,0x5F,0x62,0x6C,0x65,0x6E,0x64,0x5F,0x70,0x6F,0x73,0x48,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x37,0x00,0x00,0x00,0x76,0x5F,0x75,0x73,0x65,0x72,0x00,0x00, + 0x05,0x00,0x04,0x00,0x38,0x00,0x00,0x00,0x69,0x6E,0x5F,0x75,0x73,0x65,0x72,0x00, + 0x05,0x00,0x05,0x00,0x3A,0x00,0x00,0x00,0x76,0x5F,0x75,0x76,0x5F,0x62,0x6F,0x75, + 0x6E,0x64,0x73,0x00,0x05,0x00,0x06,0x00,0x3B,0x00,0x00,0x00,0x69,0x6E,0x5F,0x75, + 0x76,0x5F,0x62,0x6F,0x75,0x6E,0x64,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x3E,0x00,0x00,0x00,0x67,0x6C,0x5F,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x67,0x6C,0x5F,0x50,0x6F,0x73,0x69,0x74,0x69,0x6F,0x6E,0x00,0x06,0x00,0x07,0x00, + 0x3E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6C,0x5F,0x50,0x6F,0x69,0x6E,0x74, + 0x53,0x69,0x7A,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x3E,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x67,0x6C,0x5F,0x43,0x6C,0x69,0x70,0x44,0x69,0x73,0x74,0x61, + 0x6E,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x3E,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x67,0x6C,0x5F,0x43,0x75,0x6C,0x6C,0x44,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x00, + 0x05,0x00,0x03,0x00,0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x09,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x0B,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x0F,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x14,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x16,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x17,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x19,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x1A,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x1C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x1D,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x1F,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x20,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x22,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x34,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x35,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x37,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x38,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x3A,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x3B,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x3E,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x3E,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x3E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x3E,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0B,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x3E,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x0B,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00, + 0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00, + 0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0A,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x00,0x00,0x00, + 0x0B,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x0D,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0E,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0E,0x00,0x00,0x00, + 0x0F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x00,0x00,0x00, + 0x14,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x19,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x00,0x00,0x00, + 0x1A,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x1C,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x00,0x00,0x00, + 0x1D,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x1F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x22,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x24,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x24,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x26,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x24,0x00,0x00,0x00, + 0x29,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x2C,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x17,0x00,0x04,0x00,0x2E,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x00,0x00,0x00, + 0x35,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x37,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x00,0x00,0x00, + 0x38,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x3A,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x00,0x00,0x00, + 0x3B,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1C,0x00,0x04,0x00,0x3D,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x1E,0x00,0x06,0x00,0x3E,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3D,0x00,0x00,0x00,0x3D,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x3F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3E,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x3F,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x0D,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x00,0x00,0x80,0x3F, + 0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x09,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x0D,0x00,0x00,0x00, + 0x12,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x0F,0x00,0x00,0x00, + 0x12,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x15,0x00,0x00,0x00, + 0x14,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x13,0x00,0x00,0x00,0x15,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x16,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x19,0x00,0x00,0x00,0x1B,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x1E,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x1C,0x00,0x00,0x00, + 0x1E,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x1F,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x27,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x26,0x00,0x00,0x00,0x2A,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x2B,0x00,0x00,0x00,0x2A,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x2D,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x2C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x2E,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x2F,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x31,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x2D,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x22,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x36,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x34,0x00,0x00,0x00, + 0x36,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x39,0x00,0x00,0x00, + 0x38,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x37,0x00,0x00,0x00,0x39,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x3B,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x3A,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x35,0x00,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x2E,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x42,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x06,0x00,0x00,0x00, + 0x46,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x47,0x00,0x00,0x00,0x43,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x50,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x46,0x00,0x00,0x00, + 0x47,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x45,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x41,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x49,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0xFD,0x00,0x01,0x00, + 0x38,0x00,0x01,0x00, }; #endif -static const char s_draw_vs_bytecode_glsl300_src[1350] = +static const char s_draw_vs_bytecode_glsl300_src[1079] = "#version 300 es\n" "\n" -"out vec2 v_pos;\n" -"layout(location = 0) in vec2 in_pos;\n" +"out vec4 v_pos_uv;\n" +"layout(location = 0) in vec4 in_pos_uv;\n" "flat out int v_n;\n" -"layout(location = 3) in int in_n;\n" +"layout(location = 1) in int in_n;\n" "out vec4 v_ab;\n" -"layout(location = 4) in vec4 in_ab;\n" +"layout(location = 2) in vec4 in_ab;\n" "out vec4 v_cd;\n" -"layout(location = 5) in vec4 in_cd;\n" +"layout(location = 3) in vec4 in_cd;\n" "out vec4 v_ef;\n" -"layout(location = 6) in vec4 in_ef;\n" +"layout(location = 4) in vec4 in_ef;\n" "out vec4 v_gh;\n" -"layout(location = 7) in vec4 in_gh;\n" -"out vec2 v_uv;\n" -"layout(location = 2) in vec2 in_uv;\n" +"layout(location = 5) in vec4 in_gh;\n" "out vec4 v_col;\n" -"layout(location = 8) in vec4 in_col;\n" -"out float v_radius;\n" -"layout(location = 9) in float in_radius;\n" -"out float v_stroke;\n" -"layout(location = 10) in float in_stroke;\n" -"out float v_aa;\n" -"layout(location = 11) in float in_aa;\n" -"out float v_type;\n" -"layout(location = 12) in vec4 in_params;\n" -"out float v_alpha;\n" -"out float v_fill;\n" -"layout(location = 1) in vec2 in_posH;\n" -"out vec2 v_posH;\n" +"layout(location = 6) in vec4 in_col;\n" +"out vec4 v_shape;\n" +"layout(location = 7) in vec4 in_shape;\n" +"out vec4 v_blend_posH;\n" +"layout(location = 8) in vec4 in_blend_posH;\n" "out vec4 v_user;\n" -"layout(location = 13) in vec4 in_user_params;\n" +"layout(location = 9) in vec4 in_user;\n" +"out vec4 v_uv_bounds;\n" +"layout(location = 10) in vec4 in_uv_bounds;\n" "\n" "void main()\n" "{\n" -" v_pos = in_pos;\n" +" v_pos_uv = in_pos_uv;\n" " v_n = in_n;\n" " v_ab = in_ab;\n" " v_cd = in_cd;\n" " v_ef = in_ef;\n" " v_gh = in_gh;\n" -" v_uv = in_uv;\n" " v_col = in_col;\n" -" v_radius = in_radius;\n" -" v_stroke = in_stroke * 0.5;\n" -" v_aa = in_aa;\n" -" v_type = in_params.x;\n" -" v_alpha = in_params.y;\n" -" v_fill = in_params.z;\n" -" vec4 posH = vec4(in_posH, 0.0, 1.0);\n" -" gl_Position = posH;\n" -" v_posH = in_posH;\n" -" v_user = in_user_params;\n" +" v_shape = vec4(in_shape.x, in_shape.y * 0.5, in_shape.zw);\n" +" v_blend_posH = in_blend_posH;\n" +" v_user = in_user;\n" +" v_uv_bounds = in_uv_bounds;\n" +" gl_Position = vec4(in_blend_posH.zw, 0.0, 1.0);\n" " gl_Position.z = 2.0 * gl_Position.z - gl_Position.w;\n" " gl_Position.y = -gl_Position.y;\n" "}\n" "\n" ""; #define s_draw_vs_bytecode_image_names NULL +#define s_draw_vs_bytecode_image_binding_slots NULL #define s_draw_vs_bytecode_uniforms NULL #define s_draw_vs_bytecode_uniform_members NULL -static CF_ShaderInputInfo s_draw_vs_bytecode_inputs[14] = { +static CF_ShaderInputInfo s_draw_vs_bytecode_inputs[11] = { { - .name = "in_pos", + .name = "in_pos_uv", .location = 0, - .format = CF_SHADER_INFO_TYPE_FLOAT2, + .format = CF_SHADER_INFO_TYPE_FLOAT4, }, { .name = "in_n", - .location = 3, + .location = 1, .format = CF_SHADER_INFO_TYPE_SINT, }, { .name = "in_ab", - .location = 4, + .location = 2, .format = CF_SHADER_INFO_TYPE_FLOAT4, }, { .name = "in_cd", - .location = 5, + .location = 3, .format = CF_SHADER_INFO_TYPE_FLOAT4, }, { .name = "in_ef", - .location = 6, + .location = 4, .format = CF_SHADER_INFO_TYPE_FLOAT4, }, { .name = "in_gh", - .location = 7, + .location = 5, .format = CF_SHADER_INFO_TYPE_FLOAT4, }, - { - .name = "in_uv", - .location = 2, - .format = CF_SHADER_INFO_TYPE_FLOAT2, - }, { .name = "in_col", - .location = 8, + .location = 6, .format = CF_SHADER_INFO_TYPE_FLOAT4, }, { - .name = "in_radius", - .location = 9, - .format = CF_SHADER_INFO_TYPE_FLOAT, - }, - { - .name = "in_stroke", - .location = 10, - .format = CF_SHADER_INFO_TYPE_FLOAT, - }, - { - .name = "in_aa", - .location = 11, - .format = CF_SHADER_INFO_TYPE_FLOAT, + .name = "in_shape", + .location = 7, + .format = CF_SHADER_INFO_TYPE_FLOAT4, }, { - .name = "in_params", - .location = 12, + .name = "in_blend_posH", + .location = 8, .format = CF_SHADER_INFO_TYPE_FLOAT4, }, { - .name = "in_posH", - .location = 1, - .format = CF_SHADER_INFO_TYPE_FLOAT2, + .name = "in_user", + .location = 9, + .format = CF_SHADER_INFO_TYPE_FLOAT4, }, { - .name = "in_user_params", - .location = 13, + .name = "in_uv_bounds", + .location = 10, .format = CF_SHADER_INFO_TYPE_FLOAT4, }, }; static const CF_ShaderBytecode s_draw_vs_bytecode = { #ifndef __EMSCRIPTEN__ .content = s_draw_vs_bytecode_content, - .size = 3176, + .size = 2628, #endif .glsl300_src = s_draw_vs_bytecode_glsl300_src, - .glsl300_src_size = 1349, + .glsl300_src_size = 1078, .shader_info = { .num_samplers = 0, .num_storage_textures = 0, .num_storage_buffers = 0, + .num_readwrite_storage_textures = 0, + .num_readwrite_storage_buffers = 0, .num_images = 0, .image_names = s_draw_vs_bytecode_image_names, + .image_binding_slots = s_draw_vs_bytecode_image_binding_slots, .num_uniforms = 0, .uniforms = s_draw_vs_bytecode_uniforms, .num_uniform_members = 0, .uniform_members = s_draw_vs_bytecode_uniform_members, - .num_inputs = 14, + .num_inputs = 11, .inputs = s_draw_vs_bytecode_inputs, }, }; @@ -426,22 +351,17 @@ static const CF_ShaderBytecode s_draw_vs_bytecode = { #line 1 0 -layout(location = 0) in vec2 v_pos; +layout(location = 0) in vec4 v_pos_uv; layout(location = 1) in flat int v_n; layout(location = 2) in vec4 v_ab; layout(location = 3) in vec4 v_cd; layout(location = 4) in vec4 v_ef; layout(location = 5) in vec4 v_gh; -layout(location = 6) in vec2 v_uv; -layout(location = 7) in vec4 v_col; -layout(location = 8) in float v_radius; -layout(location = 9) in float v_stroke; -layout(location = 10) in float v_aa; -layout(location = 11) in float v_type; -layout(location = 12) in float v_alpha; -layout(location = 13) in float v_fill; -layout(location = 14) in vec2 v_posH; -layout(location = 15) in vec4 v_user; +layout(location = 6) in vec4 v_col; +layout(location = 7) in vec4 v_shape; +layout(location = 8) in vec4 v_blend_posH; +layout(location = 9) in vec4 v_user; +layout(location = 10) in vec4 v_uv_bounds; layout(location = 0) out vec4 result; @@ -450,8 +370,26 @@ layout(set = 2, binding = 0) uniform sampler2D u_image; layout(set = 3, binding = 0) uniform uniform_block { vec2 u_texture_size; int u_alpha_discard; + int u_use_smooth_uv; }; + +vec2 pts[8]; + + + +vec2 v_pos; +vec2 v_uv; +vec2 v_posH; +float v_radius; +float v_stroke; +float v_aa; +float v_type; +float v_alpha; +float v_fill; +vec2 pos; +vec2 screen_uv; + #line 1 "blend.shd" @@ -516,7 +454,7 @@ vec4 softlight(vec4 base, vec4 blend) { return vec4(softlight(base.rgb, blend.rgb), base.a); } -#line 29 0 +#line 42 0 #line 1 "gamma.shd" vec4 gamma(vec4 c) @@ -528,7 +466,7 @@ vec4 de_gamma(vec4 c) { return vec4(pow(abs(c.rgb), vec3(2.2)), c.a); } -#line 30 0 +#line 43 0 #line 1 "smooth_uv.shd" vec2 smooth_uv(vec2 uv, vec2 texture_size) @@ -538,7 +476,7 @@ vec2 smooth_uv(vec2 uv, vec2 texture_size) pixel = seam + clamp( (pixel - seam) / fwidth(pixel), - 0.5, 0.5); return pixel / texture_size; } -#line 31 0 +#line 44 0 #line 1 "distance.shd" float safe_div(float a, float b) @@ -678,33 +616,53 @@ float distance_polygon(vec2 p, vec2[8] v, int N) return s * sqrt(d); } -#line 32 0 +#line 45 0 + +struct ShaderParams +{ + vec2 view_pos; + vec2 uv; + vec2 uv_min; + vec2 uv_max; + vec2 screen_uv; + vec4 attributes; +}; + #line 1 "shader_stub.shd" -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { return color; } -#line 33 0 - - -vec2 pts[8]; +#line 57 0 void main() { - bool is_sprite = v_type >= (0.0 / 255.0) && v_type < (0.5 / 255.0); - bool is_text = v_type > (0.5 / 255.0) && v_type < (1.5 / 255.0); - bool is_box = v_type > (1.5 / 255.0) && v_type < (2.5 / 255.0); - bool is_seg = v_type > (2.5 / 255.0) && v_type < (3.5 / 255.0); - bool is_tri = v_type > (3.5 / 255.0) && v_type < (4.5 / 255.0); - bool is_tri_sdf = v_type > (4.5 / 255.0) && v_type < (5.5 / 255.0); - bool is_poly = v_type > (5.5 / 255.0) && v_type < (6.5 / 255.0); + + v_pos = v_pos_uv.xy; + v_uv = v_pos_uv.zw; + v_radius = v_shape.x; + v_stroke = v_shape.y; + v_aa = v_shape.z; + v_type = v_shape.w; + v_alpha = v_blend_posH.x; + v_fill = v_blend_posH.y; + v_posH = v_blend_posH.zw; + + bool is_sprite = v_type >= 0.0 && v_type < 0.5; + bool is_text = v_type > 0.5 && v_type < 1.5; + bool is_box = v_type > 1.5 && v_type < 2.5; + bool is_seg = v_type > 2.5 && v_type < 3.5; + bool is_tri = v_type > 3.5 && v_type < 4.5; + bool is_tri_sdf = v_type > 4.5 && v_type < 5.5; + bool is_poly = v_type > 5.5 && v_type < 6.5; vec4 c = vec4(0); - c = ! (is_sprite && is_text) ? de_gamma(texture(u_image, smooth_uv(v_uv, u_texture_size))) : c; - c = is_sprite ? gamma(c) : c; - c = is_text ? v_col * c.a : c; + vec2 uv = u_use_smooth_uv == 0 ? smooth_uv(v_uv, u_texture_size) : v_uv; + vec4 tex_c = de_gamma(texture(u_image, uv)); + c = is_sprite ? gamma(tex_c) : c; + c = is_text ? v_col * tex_c.a : c; c = is_tri ? v_col : c; @@ -730,685 +688,737 @@ void main() c = (! is_sprite && ! is_text && ! is_tri) ? sdf(c, v_col, d - v_radius) : c; c *= v_alpha; - vec2 screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1); - c = shader(c, v_pos, screen_uv, v_user); + pos = v_pos; + screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1); + ShaderParams sp; + sp.view_pos = pos; + sp.uv = v_uv; + sp.uv_min = v_uv_bounds.xy; + sp.uv_max = v_uv_bounds.zw; + sp.screen_uv = screen_uv; + sp.attributes = v_user; + c = shader(c, sp); if (u_alpha_discard != 0 && c.a == 0) discard; result = c; } */ #ifndef __EMSCRIPTEN__ -static const uint8_t s_draw_fs_bytecode_content[20720] = { - 0x03,0x02,0x23,0x07,0x00,0x03,0x01,0x00,0x0B,0x00,0x08,0x00,0x6A,0x03,0x00,0x00, +static const uint8_t s_draw_fs_bytecode_content[21400] = { + 0x03,0x02,0x23,0x07,0x00,0x03,0x01,0x00,0x0B,0x00,0x08,0x00,0x81,0x03,0x00,0x00, 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0B,0x00,0x06,0x00, 0x01,0x00,0x00,0x00,0x47,0x4C,0x53,0x4C,0x2E,0x73,0x74,0x64,0x2E,0x34,0x35,0x30, 0x00,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0F,0x00,0x16,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, - 0x00,0x00,0x00,0x00,0xCE,0x00,0x00,0x00,0xDA,0x00,0x00,0x00,0x0B,0x01,0x00,0x00, - 0x0E,0x01,0x00,0x00,0x49,0x02,0x00,0x00,0x97,0x02,0x00,0x00,0xB7,0x02,0x00,0x00, - 0xC8,0x02,0x00,0x00,0xC9,0x02,0x00,0x00,0xCA,0x02,0x00,0x00,0x15,0x03,0x00,0x00, - 0x1E,0x03,0x00,0x00,0x27,0x03,0x00,0x00,0x3B,0x03,0x00,0x00,0x47,0x03,0x00,0x00, - 0x4C,0x03,0x00,0x00,0x52,0x03,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xC2,0x01,0x00,0x00, - 0x04,0x00,0x09,0x00,0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x68,0x61,0x64,0x69, - 0x6E,0x67,0x5F,0x6C,0x61,0x6E,0x67,0x75,0x61,0x67,0x65,0x5F,0x69,0x6E,0x63,0x6C, - 0x75,0x64,0x65,0x00,0x04,0x00,0x0A,0x00,0x47,0x4C,0x5F,0x47,0x4F,0x4F,0x47,0x4C, - 0x45,0x5F,0x63,0x70,0x70,0x5F,0x73,0x74,0x79,0x6C,0x65,0x5F,0x6C,0x69,0x6E,0x65, - 0x5F,0x64,0x69,0x72,0x65,0x63,0x74,0x69,0x76,0x65,0x00,0x00,0x05,0x00,0x04,0x00, - 0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, - 0x0B,0x00,0x00,0x00,0x67,0x61,0x6D,0x6D,0x61,0x28,0x76,0x66,0x34,0x3B,0x00,0x00, - 0x05,0x00,0x03,0x00,0x0A,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x0E,0x00,0x00,0x00,0x64,0x65,0x5F,0x67,0x61,0x6D,0x6D,0x61,0x28,0x76,0x66,0x34, - 0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x0D,0x00,0x00,0x00,0x63,0x00,0x00,0x00, - 0x05,0x00,0x07,0x00,0x15,0x00,0x00,0x00,0x73,0x6D,0x6F,0x6F,0x74,0x68,0x5F,0x75, - 0x76,0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x05,0x00,0x03,0x00, - 0x13,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x06,0x00,0x14,0x00,0x00,0x00, - 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00,0x00,0x00, - 0x05,0x00,0x06,0x00,0x1B,0x00,0x00,0x00,0x73,0x61,0x66,0x65,0x5F,0x64,0x69,0x76, - 0x28,0x66,0x31,0x3B,0x66,0x31,0x3B,0x00,0x05,0x00,0x03,0x00,0x19,0x00,0x00,0x00, - 0x61,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x1A,0x00,0x00,0x00,0x62,0x00,0x00,0x00, - 0x05,0x00,0x06,0x00,0x1F,0x00,0x00,0x00,0x73,0x61,0x66,0x65,0x5F,0x6C,0x65,0x6E, - 0x28,0x76,0x66,0x32,0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x1E,0x00,0x00,0x00, - 0x76,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x23,0x00,0x00,0x00,0x73,0x6B,0x65,0x77, - 0x28,0x76,0x66,0x32,0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x22,0x00,0x00,0x00, - 0x76,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x28,0x00,0x00,0x00,0x64,0x65,0x74,0x32, - 0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00, - 0x26,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x27,0x00,0x00,0x00, - 0x62,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x2C,0x00,0x00,0x00,0x73,0x64,0x66,0x5F, - 0x73,0x74,0x72,0x6F,0x6B,0x65,0x28,0x66,0x31,0x3B,0x00,0x00,0x05,0x00,0x03,0x00, - 0x2B,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x32,0x00,0x00,0x00, - 0x73,0x64,0x66,0x28,0x76,0x66,0x34,0x3B,0x76,0x66,0x34,0x3B,0x66,0x31,0x3B,0x00, - 0x05,0x00,0x03,0x00,0x2F,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x05,0x00,0x03,0x00, - 0x30,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x31,0x00,0x00,0x00, - 0x64,0x00,0x00,0x00,0x05,0x00,0x08,0x00,0x36,0x00,0x00,0x00,0x64,0x69,0x73,0x74, - 0x61,0x6E,0x63,0x65,0x5F,0x61,0x61,0x62,0x62,0x28,0x76,0x66,0x32,0x3B,0x76,0x66, - 0x32,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x34,0x00,0x00,0x00,0x70,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0x35,0x00,0x00,0x00,0x68,0x65,0x00,0x00,0x05,0x00,0x0A,0x00, - 0x3D,0x00,0x00,0x00,0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x5F,0x62,0x6F,0x78, - 0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x76,0x66,0x32, - 0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x39,0x00,0x00,0x00,0x70,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0x3A,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x03,0x00, - 0x3B,0x00,0x00,0x00,0x68,0x65,0x00,0x00,0x05,0x00,0x03,0x00,0x3C,0x00,0x00,0x00, - 0x75,0x00,0x00,0x00,0x05,0x00,0x0A,0x00,0x43,0x00,0x00,0x00,0x64,0x69,0x73,0x74, - 0x61,0x6E,0x63,0x65,0x5F,0x73,0x65,0x67,0x6D,0x65,0x6E,0x74,0x28,0x76,0x66,0x32, - 0x3B,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00, - 0x40,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x41,0x00,0x00,0x00, - 0x61,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x42,0x00,0x00,0x00,0x62,0x00,0x00,0x00, - 0x05,0x00,0x0B,0x00,0x49,0x00,0x00,0x00,0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65, - 0x5F,0x74,0x72,0x69,0x61,0x6E,0x67,0x6C,0x65,0x28,0x76,0x66,0x32,0x3B,0x76,0x66, - 0x32,0x3B,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x05,0x00,0x03,0x00, - 0x45,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x46,0x00,0x00,0x00, - 0x61,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x47,0x00,0x00,0x00,0x62,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0x48,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x0A,0x00, - 0x55,0x00,0x00,0x00,0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x5F,0x70,0x6F,0x6C, - 0x79,0x67,0x6F,0x6E,0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x5B,0x38,0x5D,0x3B, - 0x69,0x31,0x3B,0x00,0x05,0x00,0x03,0x00,0x52,0x00,0x00,0x00,0x70,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0x53,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x05,0x00,0x03,0x00, - 0x54,0x00,0x00,0x00,0x4E,0x00,0x00,0x00,0x05,0x00,0x08,0x00,0x5C,0x00,0x00,0x00, - 0x73,0x68,0x61,0x64,0x65,0x72,0x28,0x76,0x66,0x34,0x3B,0x76,0x66,0x32,0x3B,0x76, - 0x66,0x32,0x3B,0x76,0x66,0x34,0x3B,0x00,0x05,0x00,0x04,0x00,0x58,0x00,0x00,0x00, - 0x63,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x59,0x00,0x00,0x00, - 0x70,0x6F,0x73,0x00,0x05,0x00,0x05,0x00,0x5A,0x00,0x00,0x00,0x73,0x63,0x72,0x65, - 0x65,0x6E,0x5F,0x75,0x76,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x5B,0x00,0x00,0x00, - 0x70,0x61,0x72,0x61,0x6D,0x73,0x00,0x00,0x05,0x00,0x04,0x00,0x7C,0x00,0x00,0x00, - 0x70,0x69,0x78,0x65,0x6C,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x80,0x00,0x00,0x00, - 0x73,0x65,0x61,0x6D,0x00,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xA5,0x00,0x00,0x00, - 0x64,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0xCE,0x00,0x00,0x00,0x76,0x5F,0x73,0x74, - 0x72,0x6F,0x6B,0x65,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xD3,0x00,0x00,0x00, - 0x77,0x69,0x72,0x65,0x5F,0x64,0x00,0x00,0x05,0x00,0x04,0x00,0xD4,0x00,0x00,0x00, - 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0xD7,0x00,0x00,0x00, - 0x73,0x74,0x72,0x6F,0x6B,0x65,0x5F,0x61,0x61,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0xDA,0x00,0x00,0x00,0x76,0x5F,0x61,0x61,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0xE0,0x00,0x00,0x00,0x73,0x74,0x72,0x6F,0x6B,0x65,0x5F,0x6E,0x6F,0x5F,0x61,0x61, - 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xE8,0x00,0x00,0x00,0x66,0x69,0x6C,0x6C, - 0x5F,0x61,0x61,0x00,0x05,0x00,0x05,0x00,0xF0,0x00,0x00,0x00,0x66,0x69,0x6C,0x6C, - 0x5F,0x6E,0x6F,0x5F,0x61,0x61,0x00,0x00,0x05,0x00,0x04,0x00,0xFA,0x00,0x00,0x00, - 0x73,0x74,0x72,0x6F,0x6B,0x65,0x00,0x00,0x05,0x00,0x04,0x00,0x02,0x01,0x00,0x00, - 0x66,0x69,0x6C,0x6C,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x0B,0x01,0x00,0x00, - 0x72,0x65,0x73,0x75,0x6C,0x74,0x00,0x00,0x05,0x00,0x04,0x00,0x0E,0x01,0x00,0x00, - 0x76,0x5F,0x66,0x69,0x6C,0x6C,0x00,0x00,0x05,0x00,0x03,0x00,0x15,0x01,0x00,0x00, - 0x64,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x29,0x01,0x00,0x00,0x6D,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x2B,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x3C,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x3E,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0x43,0x01,0x00,0x00,0x6E,0x00,0x00,0x00,0x05,0x00,0x03,0x00, - 0x47,0x01,0x00,0x00,0x70,0x61,0x00,0x00,0x05,0x00,0x03,0x00,0x4B,0x01,0x00,0x00, - 0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x52,0x01,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x53,0x01,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x55,0x01,0x00,0x00,0x68,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x5D,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0x61,0x01,0x00,0x00,0x65,0x30,0x00,0x00,0x05,0x00,0x03,0x00, - 0x65,0x01,0x00,0x00,0x65,0x31,0x00,0x00,0x05,0x00,0x03,0x00,0x69,0x01,0x00,0x00, - 0x65,0x32,0x00,0x00,0x05,0x00,0x03,0x00,0x6D,0x01,0x00,0x00,0x76,0x30,0x00,0x00, - 0x05,0x00,0x03,0x00,0x71,0x01,0x00,0x00,0x76,0x31,0x00,0x00,0x05,0x00,0x03,0x00, - 0x75,0x01,0x00,0x00,0x76,0x32,0x00,0x00,0x05,0x00,0x03,0x00,0x79,0x01,0x00,0x00, - 0x70,0x71,0x30,0x00,0x05,0x00,0x04,0x00,0x82,0x01,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x83,0x01,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x88,0x01,0x00,0x00,0x70,0x71,0x31,0x00, - 0x05,0x00,0x04,0x00,0x91,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x92,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0x97,0x01,0x00,0x00,0x70,0x71,0x32,0x00,0x05,0x00,0x04,0x00, - 0xA0,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0xA1,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, - 0xA6,0x01,0x00,0x00,0x73,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xA7,0x01,0x00,0x00, - 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xA9,0x01,0x00,0x00, - 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xAC,0x01,0x00,0x00, - 0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xB1,0x01,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xB3,0x01,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xBC,0x01,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xBE,0x01,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xC8,0x01,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xCA,0x01,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xDA,0x01,0x00,0x00,0x64,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0xE5,0x01,0x00,0x00,0x73,0x00,0x00,0x00,0x05,0x00,0x03,0x00, - 0xE6,0x01,0x00,0x00,0x69,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xE7,0x01,0x00,0x00, - 0x6A,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xF3,0x01,0x00,0x00,0x65,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0xFB,0x01,0x00,0x00,0x77,0x00,0x00,0x00,0x05,0x00,0x03,0x00, - 0x01,0x02,0x00,0x00,0x62,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x15,0x02,0x00,0x00, - 0x63,0x6F,0x6E,0x64,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x48,0x02,0x00,0x00, - 0x69,0x73,0x5F,0x73,0x70,0x72,0x69,0x74,0x65,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x49,0x02,0x00,0x00,0x76,0x5F,0x74,0x79,0x70,0x65,0x00,0x00,0x05,0x00,0x04,0x00, - 0x52,0x02,0x00,0x00,0x69,0x73,0x5F,0x74,0x65,0x78,0x74,0x00,0x05,0x00,0x04,0x00, - 0x5B,0x02,0x00,0x00,0x69,0x73,0x5F,0x62,0x6F,0x78,0x00,0x00,0x05,0x00,0x04,0x00, - 0x64,0x02,0x00,0x00,0x69,0x73,0x5F,0x73,0x65,0x67,0x00,0x00,0x05,0x00,0x04,0x00, - 0x6D,0x02,0x00,0x00,0x69,0x73,0x5F,0x74,0x72,0x69,0x00,0x00,0x05,0x00,0x05,0x00, - 0x76,0x02,0x00,0x00,0x69,0x73,0x5F,0x74,0x72,0x69,0x5F,0x73,0x64,0x66,0x00,0x00, - 0x05,0x00,0x04,0x00,0x7F,0x02,0x00,0x00,0x69,0x73,0x5F,0x70,0x6F,0x6C,0x79,0x00, - 0x05,0x00,0x03,0x00,0x88,0x02,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x94,0x02,0x00,0x00,0x75,0x5F,0x69,0x6D,0x61,0x67,0x65,0x00,0x05,0x00,0x04,0x00, - 0x97,0x02,0x00,0x00,0x76,0x5F,0x75,0x76,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x98,0x02,0x00,0x00,0x75,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x5F,0x62,0x6C,0x6F,0x63, - 0x6B,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x98,0x02,0x00,0x00,0x00,0x00,0x00,0x00, - 0x75,0x5F,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00, - 0x06,0x00,0x07,0x00,0x98,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x75,0x5F,0x61,0x6C, - 0x70,0x68,0x61,0x5F,0x64,0x69,0x73,0x63,0x61,0x72,0x64,0x00,0x05,0x00,0x03,0x00, - 0x9A,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x9B,0x02,0x00,0x00, - 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x9D,0x02,0x00,0x00, - 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xA3,0x02,0x00,0x00, - 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xAC,0x02,0x00,0x00, - 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xB7,0x02,0x00,0x00, - 0x76,0x5F,0x63,0x6F,0x6C,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xC4,0x02,0x00,0x00, - 0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xC8,0x02,0x00,0x00,0x76,0x5F,0x70,0x6F, - 0x73,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xC9,0x02,0x00,0x00,0x76,0x5F,0x61,0x62, - 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xCA,0x02,0x00,0x00,0x76,0x5F,0x63,0x64, - 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xCB,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xCD,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xD0,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xD3,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xDB,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xDD,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xE0,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xE5,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xE7,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x0F,0x00,0x11,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, + 0x00,0x00,0x00,0x00,0x0B,0x01,0x00,0x00,0x4A,0x02,0x00,0x00,0x51,0x02,0x00,0x00, + 0x5E,0x02,0x00,0x00,0xC7,0x02,0x00,0x00,0xD8,0x02,0x00,0x00,0xD9,0x02,0x00,0x00, + 0x22,0x03,0x00,0x00,0x2B,0x03,0x00,0x00,0x34,0x03,0x00,0x00,0x63,0x03,0x00,0x00, + 0x6C,0x03,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xC2,0x01,0x00,0x00,0x04,0x00,0x09,0x00, + 0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x68,0x61,0x64,0x69,0x6E,0x67,0x5F,0x6C, + 0x61,0x6E,0x67,0x75,0x61,0x67,0x65,0x5F,0x69,0x6E,0x63,0x6C,0x75,0x64,0x65,0x00, + 0x04,0x00,0x0A,0x00,0x47,0x4C,0x5F,0x47,0x4F,0x4F,0x47,0x4C,0x45,0x5F,0x63,0x70, + 0x70,0x5F,0x73,0x74,0x79,0x6C,0x65,0x5F,0x6C,0x69,0x6E,0x65,0x5F,0x64,0x69,0x72, + 0x65,0x63,0x74,0x69,0x76,0x65,0x00,0x00,0x05,0x00,0x04,0x00,0x04,0x00,0x00,0x00, + 0x6D,0x61,0x69,0x6E,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0B,0x00,0x00,0x00, + 0x67,0x61,0x6D,0x6D,0x61,0x28,0x76,0x66,0x34,0x3B,0x00,0x00,0x05,0x00,0x03,0x00, + 0x0A,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x0E,0x00,0x00,0x00, + 0x64,0x65,0x5F,0x67,0x61,0x6D,0x6D,0x61,0x28,0x76,0x66,0x34,0x3B,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x0D,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x07,0x00, + 0x15,0x00,0x00,0x00,0x73,0x6D,0x6F,0x6F,0x74,0x68,0x5F,0x75,0x76,0x28,0x76,0x66, + 0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x13,0x00,0x00,0x00, + 0x75,0x76,0x00,0x00,0x05,0x00,0x06,0x00,0x14,0x00,0x00,0x00,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x1B,0x00,0x00,0x00,0x73,0x61,0x66,0x65,0x5F,0x64,0x69,0x76,0x28,0x66,0x31,0x3B, + 0x66,0x31,0x3B,0x00,0x05,0x00,0x03,0x00,0x19,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x1A,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x1F,0x00,0x00,0x00,0x73,0x61,0x66,0x65,0x5F,0x6C,0x65,0x6E,0x28,0x76,0x66,0x32, + 0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x1E,0x00,0x00,0x00,0x76,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x23,0x00,0x00,0x00,0x73,0x6B,0x65,0x77,0x28,0x76,0x66,0x32, + 0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x22,0x00,0x00,0x00,0x76,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x28,0x00,0x00,0x00,0x64,0x65,0x74,0x32,0x28,0x76,0x66,0x32, + 0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x26,0x00,0x00,0x00, + 0x61,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x27,0x00,0x00,0x00,0x62,0x00,0x00,0x00, + 0x05,0x00,0x06,0x00,0x2C,0x00,0x00,0x00,0x73,0x64,0x66,0x5F,0x73,0x74,0x72,0x6F, + 0x6B,0x65,0x28,0x66,0x31,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x2B,0x00,0x00,0x00, + 0x64,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x32,0x00,0x00,0x00,0x73,0x64,0x66,0x28, + 0x76,0x66,0x34,0x3B,0x76,0x66,0x34,0x3B,0x66,0x31,0x3B,0x00,0x05,0x00,0x03,0x00, + 0x2F,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x30,0x00,0x00,0x00, + 0x62,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x31,0x00,0x00,0x00,0x64,0x00,0x00,0x00, + 0x05,0x00,0x08,0x00,0x36,0x00,0x00,0x00,0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65, + 0x5F,0x61,0x61,0x62,0x62,0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00, + 0x05,0x00,0x03,0x00,0x34,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x35,0x00,0x00,0x00,0x68,0x65,0x00,0x00,0x05,0x00,0x0A,0x00,0x3D,0x00,0x00,0x00, + 0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x5F,0x62,0x6F,0x78,0x28,0x76,0x66,0x32, + 0x3B,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x39,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x3A,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x3B,0x00,0x00,0x00, + 0x68,0x65,0x00,0x00,0x05,0x00,0x03,0x00,0x3C,0x00,0x00,0x00,0x75,0x00,0x00,0x00, + 0x05,0x00,0x0A,0x00,0x43,0x00,0x00,0x00,0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65, + 0x5F,0x73,0x65,0x67,0x6D,0x65,0x6E,0x74,0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32, + 0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x40,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x41,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x42,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x05,0x00,0x0B,0x00, + 0x49,0x00,0x00,0x00,0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x5F,0x74,0x72,0x69, + 0x61,0x6E,0x67,0x6C,0x65,0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x76,0x66, + 0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x45,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x46,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x47,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x48,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x0A,0x00,0x55,0x00,0x00,0x00, + 0x64,0x69,0x73,0x74,0x61,0x6E,0x63,0x65,0x5F,0x70,0x6F,0x6C,0x79,0x67,0x6F,0x6E, + 0x28,0x76,0x66,0x32,0x3B,0x76,0x66,0x32,0x5B,0x38,0x5D,0x3B,0x69,0x31,0x3B,0x00, + 0x05,0x00,0x03,0x00,0x52,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x53,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x54,0x00,0x00,0x00, + 0x4E,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x57,0x00,0x00,0x00,0x53,0x68,0x61,0x64, + 0x65,0x72,0x50,0x61,0x72,0x61,0x6D,0x73,0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00, + 0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x76,0x69,0x65,0x77,0x5F,0x70,0x6F,0x73, + 0x00,0x00,0x00,0x00,0x06,0x00,0x04,0x00,0x57,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x75,0x76,0x00,0x00,0x06,0x00,0x05,0x00,0x57,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x75,0x76,0x5F,0x6D,0x69,0x6E,0x00,0x00,0x06,0x00,0x05,0x00,0x57,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x75,0x76,0x5F,0x6D,0x61,0x78,0x00,0x00,0x06,0x00,0x06,0x00, + 0x57,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75, + 0x76,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x57,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x73,0x00,0x00,0x05,0x00,0x11,0x00, + 0x5C,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x65,0x72,0x28,0x76,0x66,0x34,0x3B,0x73, + 0x74,0x72,0x75,0x63,0x74,0x2D,0x53,0x68,0x61,0x64,0x65,0x72,0x50,0x61,0x72,0x61, + 0x6D,0x73,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76, + 0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x34,0x31,0x3B,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x5A,0x00,0x00,0x00,0x63,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x5B,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x73,0x00,0x00, + 0x05,0x00,0x04,0x00,0x7C,0x00,0x00,0x00,0x70,0x69,0x78,0x65,0x6C,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x80,0x00,0x00,0x00,0x73,0x65,0x61,0x6D,0x00,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0xA5,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0xCE,0x00,0x00,0x00,0x76,0x5F,0x73,0x74,0x72,0x6F,0x6B,0x65,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xD3,0x00,0x00,0x00,0x77,0x69,0x72,0x65,0x5F,0x64,0x00,0x00, + 0x05,0x00,0x04,0x00,0xD4,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0xD7,0x00,0x00,0x00,0x73,0x74,0x72,0x6F,0x6B,0x65,0x5F,0x61, + 0x61,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xDA,0x00,0x00,0x00,0x76,0x5F,0x61,0x61, + 0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0xE0,0x00,0x00,0x00,0x73,0x74,0x72,0x6F, + 0x6B,0x65,0x5F,0x6E,0x6F,0x5F,0x61,0x61,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xE8,0x00,0x00,0x00,0x66,0x69,0x6C,0x6C,0x5F,0x61,0x61,0x00,0x05,0x00,0x05,0x00, + 0xF0,0x00,0x00,0x00,0x66,0x69,0x6C,0x6C,0x5F,0x6E,0x6F,0x5F,0x61,0x61,0x00,0x00, + 0x05,0x00,0x04,0x00,0xFA,0x00,0x00,0x00,0x73,0x74,0x72,0x6F,0x6B,0x65,0x00,0x00, + 0x05,0x00,0x04,0x00,0x02,0x01,0x00,0x00,0x66,0x69,0x6C,0x6C,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x0B,0x01,0x00,0x00,0x72,0x65,0x73,0x75,0x6C,0x74,0x00,0x00, + 0x05,0x00,0x04,0x00,0x0E,0x01,0x00,0x00,0x76,0x5F,0x66,0x69,0x6C,0x6C,0x00,0x00, + 0x05,0x00,0x03,0x00,0x15,0x01,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x29,0x01,0x00,0x00,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x2B,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x3C,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x3E,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x43,0x01,0x00,0x00, + 0x6E,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x47,0x01,0x00,0x00,0x70,0x61,0x00,0x00, + 0x05,0x00,0x03,0x00,0x4B,0x01,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x52,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x53,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x55,0x01,0x00,0x00,0x68,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x5D,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x61,0x01,0x00,0x00, + 0x65,0x30,0x00,0x00,0x05,0x00,0x03,0x00,0x65,0x01,0x00,0x00,0x65,0x31,0x00,0x00, + 0x05,0x00,0x03,0x00,0x69,0x01,0x00,0x00,0x65,0x32,0x00,0x00,0x05,0x00,0x03,0x00, + 0x6D,0x01,0x00,0x00,0x76,0x30,0x00,0x00,0x05,0x00,0x03,0x00,0x71,0x01,0x00,0x00, + 0x76,0x31,0x00,0x00,0x05,0x00,0x03,0x00,0x75,0x01,0x00,0x00,0x76,0x32,0x00,0x00, + 0x05,0x00,0x03,0x00,0x79,0x01,0x00,0x00,0x70,0x71,0x30,0x00,0x05,0x00,0x04,0x00, + 0x82,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x83,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x88,0x01,0x00,0x00,0x70,0x71,0x31,0x00,0x05,0x00,0x04,0x00,0x91,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x92,0x01,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x97,0x01,0x00,0x00, + 0x70,0x71,0x32,0x00,0x05,0x00,0x04,0x00,0xA0,0x01,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xA1,0x01,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xA6,0x01,0x00,0x00,0x73,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xA7,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0xA9,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0xAC,0x01,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xB1,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xB3,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xBC,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xBE,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xC8,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0xCA,0x01,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0xDA,0x01,0x00,0x00,0x64,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xE5,0x01,0x00,0x00, + 0x73,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xE6,0x01,0x00,0x00,0x69,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0xE7,0x01,0x00,0x00,0x6A,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0xF3,0x01,0x00,0x00,0x65,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xFB,0x01,0x00,0x00, + 0x77,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x01,0x02,0x00,0x00,0x62,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x15,0x02,0x00,0x00,0x63,0x6F,0x6E,0x64,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x48,0x02,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x4A,0x02,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x5F,0x75,0x76, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x4D,0x02,0x00,0x00,0x76,0x5F,0x75,0x76, + 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x50,0x02,0x00,0x00,0x76,0x5F,0x72,0x61, + 0x64,0x69,0x75,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x51,0x02,0x00,0x00, + 0x76,0x5F,0x73,0x68,0x61,0x70,0x65,0x00,0x05,0x00,0x04,0x00,0x5A,0x02,0x00,0x00, + 0x76,0x5F,0x74,0x79,0x70,0x65,0x00,0x00,0x05,0x00,0x04,0x00,0x5D,0x02,0x00,0x00, + 0x76,0x5F,0x61,0x6C,0x70,0x68,0x61,0x00,0x05,0x00,0x06,0x00,0x5E,0x02,0x00,0x00, + 0x76,0x5F,0x62,0x6C,0x65,0x6E,0x64,0x5F,0x70,0x6F,0x73,0x48,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x63,0x02,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x48,0x00,0x00, + 0x05,0x00,0x05,0x00,0x67,0x02,0x00,0x00,0x69,0x73,0x5F,0x73,0x70,0x72,0x69,0x74, + 0x65,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x6D,0x02,0x00,0x00,0x69,0x73,0x5F,0x74, + 0x65,0x78,0x74,0x00,0x05,0x00,0x04,0x00,0x74,0x02,0x00,0x00,0x69,0x73,0x5F,0x62, + 0x6F,0x78,0x00,0x00,0x05,0x00,0x04,0x00,0x7B,0x02,0x00,0x00,0x69,0x73,0x5F,0x73, + 0x65,0x67,0x00,0x00,0x05,0x00,0x04,0x00,0x82,0x02,0x00,0x00,0x69,0x73,0x5F,0x74, + 0x72,0x69,0x00,0x00,0x05,0x00,0x05,0x00,0x89,0x02,0x00,0x00,0x69,0x73,0x5F,0x74, + 0x72,0x69,0x5F,0x73,0x64,0x66,0x00,0x00,0x05,0x00,0x04,0x00,0x90,0x02,0x00,0x00, + 0x69,0x73,0x5F,0x70,0x6F,0x6C,0x79,0x00,0x05,0x00,0x03,0x00,0x97,0x02,0x00,0x00, + 0x63,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x99,0x02,0x00,0x00,0x75,0x76,0x00,0x00, + 0x05,0x00,0x06,0x00,0x9A,0x02,0x00,0x00,0x75,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x5F, + 0x62,0x6C,0x6F,0x63,0x6B,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x9A,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x75,0x5F,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5F,0x73,0x69, + 0x7A,0x65,0x00,0x00,0x06,0x00,0x07,0x00,0x9A,0x02,0x00,0x00,0x01,0x00,0x00,0x00, + 0x75,0x5F,0x61,0x6C,0x70,0x68,0x61,0x5F,0x64,0x69,0x73,0x63,0x61,0x72,0x64,0x00, + 0x06,0x00,0x07,0x00,0x9A,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x75,0x5F,0x75,0x73, + 0x65,0x5F,0x73,0x6D,0x6F,0x6F,0x74,0x68,0x5F,0x75,0x76,0x00,0x05,0x00,0x03,0x00, + 0x9C,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xA5,0x02,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xA7,0x02,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xAF,0x02,0x00,0x00, + 0x74,0x65,0x78,0x5F,0x63,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xB3,0x02,0x00,0x00, + 0x75,0x5F,0x69,0x6D,0x61,0x67,0x65,0x00,0x05,0x00,0x04,0x00,0xB7,0x02,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xBD,0x02,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xC7,0x02,0x00,0x00, + 0x76,0x5F,0x63,0x6F,0x6C,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0xD4,0x02,0x00,0x00, + 0x64,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xD8,0x02,0x00,0x00,0x76,0x5F,0x61,0x62, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xD9,0x02,0x00,0x00,0x76,0x5F,0x63,0x64, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xDA,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xDC,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xDF,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xE2,0x02,0x00,0x00,0x70,0x61,0x72,0x61, 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xEA,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xF3,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xF5,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xF8,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xFB,0x02,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x04,0x03,0x00,0x00,0x70,0x74,0x73,0x00, - 0x05,0x00,0x04,0x00,0x15,0x03,0x00,0x00,0x76,0x5F,0x65,0x66,0x00,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x1E,0x03,0x00,0x00,0x76,0x5F,0x67,0x68,0x00,0x00,0x00,0x00, - 0x05,0x00,0x03,0x00,0x27,0x03,0x00,0x00,0x76,0x5F,0x6E,0x00,0x05,0x00,0x04,0x00, - 0x28,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x2A,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x2C,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x05,0x00, - 0x3B,0x03,0x00,0x00,0x76,0x5F,0x72,0x61,0x64,0x69,0x75,0x73,0x00,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x3E,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x40,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x42,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, - 0x05,0x00,0x04,0x00,0x47,0x03,0x00,0x00,0x76,0x5F,0x61,0x6C,0x70,0x68,0x61,0x00, - 0x05,0x00,0x05,0x00,0x4B,0x03,0x00,0x00,0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75, - 0x76,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x4C,0x03,0x00,0x00,0x76,0x5F,0x70,0x6F, - 0x73,0x48,0x00,0x00,0x05,0x00,0x04,0x00,0x52,0x03,0x00,0x00,0x76,0x5F,0x75,0x73, - 0x65,0x72,0x00,0x00,0x05,0x00,0x04,0x00,0x53,0x03,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x55,0x03,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x57,0x03,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x59,0x03,0x00,0x00,0x70,0x61,0x72,0x61, - 0x6D,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xCE,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, - 0x09,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xDA,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, - 0x0A,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0B,0x01,0x00,0x00,0x1E,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x0E,0x01,0x00,0x00,0x1E,0x00,0x00,0x00, - 0x0D,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x49,0x02,0x00,0x00,0x1E,0x00,0x00,0x00, - 0x0B,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x94,0x02,0x00,0x00,0x21,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x94,0x02,0x00,0x00,0x22,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x97,0x02,0x00,0x00,0x1E,0x00,0x00,0x00, - 0x06,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x98,0x02,0x00,0x00,0x02,0x00,0x00,0x00, - 0x48,0x00,0x05,0x00,0x98,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x98,0x02,0x00,0x00,0x01,0x00,0x00,0x00, - 0x23,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x9A,0x02,0x00,0x00, - 0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x9A,0x02,0x00,0x00, - 0x22,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xB7,0x02,0x00,0x00, - 0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xC8,0x02,0x00,0x00, - 0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xC9,0x02,0x00,0x00, - 0x1E,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xCA,0x02,0x00,0x00, - 0x1E,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x15,0x03,0x00,0x00, - 0x1E,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1E,0x03,0x00,0x00, - 0x1E,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x27,0x03,0x00,0x00, - 0x0E,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x27,0x03,0x00,0x00,0x1E,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x3B,0x03,0x00,0x00,0x1E,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x47,0x03,0x00,0x00,0x1E,0x00,0x00,0x00, - 0x0C,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x4C,0x03,0x00,0x00,0x1E,0x00,0x00,0x00, - 0x0E,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x52,0x03,0x00,0x00,0x1E,0x00,0x00,0x00, - 0x0F,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00, - 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x21,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x21,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x10,0x00,0x00,0x00, - 0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x17,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x21,0x00,0x05,0x00,0x18,0x00,0x00,0x00, - 0x06,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x21,0x00,0x04,0x00, - 0x1D,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x04,0x00, - 0x21,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x05,0x00, - 0x25,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00, - 0x21,0x00,0x04,0x00,0x2A,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x17,0x00,0x00,0x00, - 0x21,0x00,0x06,0x00,0x2E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x21,0x00,0x07,0x00,0x38,0x00,0x00,0x00, - 0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00, - 0x11,0x00,0x00,0x00,0x21,0x00,0x06,0x00,0x3F,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x4B,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, - 0x4B,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x1C,0x00,0x04,0x00, - 0x4D,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x4E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x4D,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x4F,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x50,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x21,0x00,0x06,0x00, - 0x51,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x4E,0x00,0x00,0x00, - 0x50,0x00,0x00,0x00,0x21,0x00,0x07,0x00,0x57,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x5E,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x2F,0xBA,0xE8,0x3E, - 0x2C,0x00,0x06,0x00,0x5E,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x62,0x00,0x00,0x00, - 0x62,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4B,0x00,0x00,0x00, - 0x65,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0x71,0x00,0x00,0x00,0xCD,0xCC,0x0C,0x40,0x2C,0x00,0x06,0x00,0x5E,0x00,0x00,0x00, - 0x72,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x71,0x00,0x00,0x00, - 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x82,0x00,0x00,0x00,0x00,0x00,0x00,0x3F, - 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x8D,0x00,0x00,0x00,0x00,0x00,0x00,0xBF, - 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x14,0x00,0x02,0x00,0x99,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4B,0x00,0x00,0x00, - 0xB4,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4B,0x00,0x00,0x00, - 0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0xCD,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xCD,0x00,0x00,0x00, - 0xCE,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xCD,0x00,0x00,0x00, - 0xDA,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0xE5,0x00,0x00,0x00, - 0x99,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0xF2,0x00,0x00,0x00,0x00,0x00,0x80,0xBF,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0xF3,0x00,0x00,0x00,0x00,0x00,0x80,0x3F,0x20,0x00,0x04,0x00,0x0A,0x01,0x00,0x00, - 0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0A,0x01,0x00,0x00, - 0x0B,0x01,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xCD,0x00,0x00,0x00, - 0x0E,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x18,0x00,0x04,0x00,0x27,0x01,0x00,0x00, - 0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x28,0x01,0x00,0x00, - 0x07,0x00,0x00,0x00,0x27,0x01,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, - 0xDC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, - 0xE9,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x13,0x02,0x00,0x00, - 0x99,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x14,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x13,0x02,0x00,0x00,0x20,0x00,0x04,0x00,0x47,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x99,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xCD,0x00,0x00,0x00, - 0x49,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0x4F,0x02,0x00,0x00,0x81,0x80,0x00,0x3B,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0x58,0x02,0x00,0x00,0xC1,0xC0,0xC0,0x3B,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0x61,0x02,0x00,0x00,0xA1,0xA0,0x20,0x3C,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0x6A,0x02,0x00,0x00,0xE1,0xE0,0x60,0x3C,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0x73,0x02,0x00,0x00,0x91,0x90,0x90,0x3C,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0x7C,0x02,0x00,0x00,0xB1,0xB0,0xB0,0x3C,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0x85,0x02,0x00,0x00,0xD1,0xD0,0xD0,0x3C,0x2C,0x00,0x07,0x00,0x07,0x00,0x00,0x00, - 0x89,0x02,0x00,0x00,0x98,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x98,0x00,0x00,0x00, - 0x98,0x00,0x00,0x00,0x19,0x00,0x09,0x00,0x91,0x02,0x00,0x00,0x06,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1B,0x00,0x03,0x00,0x92,0x02,0x00,0x00, - 0x91,0x02,0x00,0x00,0x20,0x00,0x04,0x00,0x93,0x02,0x00,0x00,0x00,0x00,0x00,0x00, - 0x92,0x02,0x00,0x00,0x3B,0x00,0x04,0x00,0x93,0x02,0x00,0x00,0x94,0x02,0x00,0x00, - 0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x96,0x02,0x00,0x00,0x01,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x96,0x02,0x00,0x00,0x97,0x02,0x00,0x00, - 0x01,0x00,0x00,0x00,0x1E,0x00,0x04,0x00,0x98,0x02,0x00,0x00,0x10,0x00,0x00,0x00, - 0x4F,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x99,0x02,0x00,0x00,0x02,0x00,0x00,0x00, - 0x98,0x02,0x00,0x00,0x3B,0x00,0x04,0x00,0x99,0x02,0x00,0x00,0x9A,0x02,0x00,0x00, - 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x9E,0x02,0x00,0x00,0x02,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0xB6,0x02,0x00,0x00,0x01,0x00,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xB6,0x02,0x00,0x00,0xB7,0x02,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x96,0x02,0x00,0x00,0xC8,0x02,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xB6,0x02,0x00,0x00,0xC9,0x02,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xB6,0x02,0x00,0x00,0xCA,0x02,0x00,0x00, - 0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x03,0x03,0x00,0x00,0x06,0x00,0x00,0x00, - 0x4D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x03,0x03,0x00,0x00,0x04,0x03,0x00,0x00, - 0x06,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x07,0x03,0x00,0x00,0x06,0x00,0x00,0x00, - 0x10,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x0C,0x03,0x00,0x00, - 0x02,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x10,0x03,0x00,0x00, - 0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x14,0x03,0x00,0x00, - 0x04,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xB6,0x02,0x00,0x00,0x15,0x03,0x00,0x00, - 0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x19,0x03,0x00,0x00, - 0x05,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x1D,0x03,0x00,0x00, - 0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xB6,0x02,0x00,0x00,0x1E,0x03,0x00,0x00, - 0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x22,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x26,0x03,0x00,0x00,0x01,0x00,0x00,0x00, - 0x4F,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x26,0x03,0x00,0x00,0x27,0x03,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xCD,0x00,0x00,0x00,0x3B,0x03,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xCD,0x00,0x00,0x00,0x47,0x03,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x96,0x02,0x00,0x00,0x4C,0x03,0x00,0x00, - 0x01,0x00,0x00,0x00,0x2C,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x4E,0x03,0x00,0x00, - 0xF3,0x00,0x00,0x00,0xF2,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0xB6,0x02,0x00,0x00, - 0x52,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x5C,0x03,0x00,0x00, - 0x02,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, - 0x05,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x47,0x02,0x00,0x00,0x48,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x47,0x02,0x00,0x00,0x52,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x47,0x02,0x00,0x00,0x5B,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x47,0x02,0x00,0x00,0x64,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x47,0x02,0x00,0x00,0x6D,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x47,0x02,0x00,0x00,0x76,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x47,0x02,0x00,0x00,0x7F,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x88,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x8E,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x9B,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x9D,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xA3,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xA9,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xAC,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0xB3,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0xC4,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xCB,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xCD,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xD0,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xD3,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xDB,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xDD,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xE0,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xE5,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xE7,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xEA,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xF3,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xF5,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xF8,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0xFB,0x02,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x28,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x4E,0x00,0x00,0x00,0x2A,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x50,0x00,0x00,0x00,0x2C,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x37,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x3E,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x40,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x42,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x4B,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x53,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x55,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x57,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x59,0x03,0x00,0x00, - 0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x4A,0x02,0x00,0x00, - 0x49,0x02,0x00,0x00,0xBE,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x4B,0x02,0x00,0x00, - 0x4A,0x02,0x00,0x00,0x98,0x00,0x00,0x00,0xF7,0x00,0x03,0x00,0x4D,0x02,0x00,0x00, - 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x4B,0x02,0x00,0x00,0x4C,0x02,0x00,0x00, - 0x4D,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0x4C,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x4E,0x02,0x00,0x00,0x49,0x02,0x00,0x00,0xB8,0x00,0x05,0x00, - 0x99,0x00,0x00,0x00,0x50,0x02,0x00,0x00,0x4E,0x02,0x00,0x00,0x4F,0x02,0x00,0x00, - 0xF9,0x00,0x02,0x00,0x4D,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0x4D,0x02,0x00,0x00, - 0xF5,0x00,0x07,0x00,0x99,0x00,0x00,0x00,0x51,0x02,0x00,0x00,0x4B,0x02,0x00,0x00, - 0x05,0x00,0x00,0x00,0x50,0x02,0x00,0x00,0x4C,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x48,0x02,0x00,0x00,0x51,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0x53,0x02,0x00,0x00,0x49,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00, - 0x54,0x02,0x00,0x00,0x53,0x02,0x00,0x00,0x4F,0x02,0x00,0x00,0xF7,0x00,0x03,0x00, - 0x56,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x54,0x02,0x00,0x00, - 0x55,0x02,0x00,0x00,0x56,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0x55,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x57,0x02,0x00,0x00,0x49,0x02,0x00,0x00, - 0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x59,0x02,0x00,0x00,0x57,0x02,0x00,0x00, - 0x58,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0x56,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, - 0x56,0x02,0x00,0x00,0xF5,0x00,0x07,0x00,0x99,0x00,0x00,0x00,0x5A,0x02,0x00,0x00, - 0x54,0x02,0x00,0x00,0x4D,0x02,0x00,0x00,0x59,0x02,0x00,0x00,0x55,0x02,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x52,0x02,0x00,0x00,0x5A,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x5C,0x02,0x00,0x00,0x49,0x02,0x00,0x00,0xBA,0x00,0x05,0x00, - 0x99,0x00,0x00,0x00,0x5D,0x02,0x00,0x00,0x5C,0x02,0x00,0x00,0x58,0x02,0x00,0x00, - 0xF7,0x00,0x03,0x00,0x5F,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, - 0x5D,0x02,0x00,0x00,0x5E,0x02,0x00,0x00,0x5F,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, - 0x5E,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x60,0x02,0x00,0x00, - 0x49,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x62,0x02,0x00,0x00, - 0x60,0x02,0x00,0x00,0x61,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0x5F,0x02,0x00,0x00, - 0xF8,0x00,0x02,0x00,0x5F,0x02,0x00,0x00,0xF5,0x00,0x07,0x00,0x99,0x00,0x00,0x00, - 0x63,0x02,0x00,0x00,0x5D,0x02,0x00,0x00,0x56,0x02,0x00,0x00,0x62,0x02,0x00,0x00, - 0x5E,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x5B,0x02,0x00,0x00,0x63,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x65,0x02,0x00,0x00,0x49,0x02,0x00,0x00, - 0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x66,0x02,0x00,0x00,0x65,0x02,0x00,0x00, - 0x61,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0x68,0x02,0x00,0x00,0x00,0x00,0x00,0x00, - 0xFA,0x00,0x04,0x00,0x66,0x02,0x00,0x00,0x67,0x02,0x00,0x00,0x68,0x02,0x00,0x00, - 0xF8,0x00,0x02,0x00,0x67,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0x69,0x02,0x00,0x00,0x49,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00, - 0x6B,0x02,0x00,0x00,0x69,0x02,0x00,0x00,0x6A,0x02,0x00,0x00,0xF9,0x00,0x02,0x00, - 0x68,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0x68,0x02,0x00,0x00,0xF5,0x00,0x07,0x00, - 0x99,0x00,0x00,0x00,0x6C,0x02,0x00,0x00,0x66,0x02,0x00,0x00,0x5F,0x02,0x00,0x00, - 0x6B,0x02,0x00,0x00,0x67,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x64,0x02,0x00,0x00, - 0x6C,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x6E,0x02,0x00,0x00, - 0x49,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x6F,0x02,0x00,0x00, - 0x6E,0x02,0x00,0x00,0x6A,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0x71,0x02,0x00,0x00, - 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x6F,0x02,0x00,0x00,0x70,0x02,0x00,0x00, - 0x71,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0x70,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x72,0x02,0x00,0x00,0x49,0x02,0x00,0x00,0xB8,0x00,0x05,0x00, - 0x99,0x00,0x00,0x00,0x74,0x02,0x00,0x00,0x72,0x02,0x00,0x00,0x73,0x02,0x00,0x00, - 0xF9,0x00,0x02,0x00,0x71,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0x71,0x02,0x00,0x00, - 0xF5,0x00,0x07,0x00,0x99,0x00,0x00,0x00,0x75,0x02,0x00,0x00,0x6F,0x02,0x00,0x00, - 0x68,0x02,0x00,0x00,0x74,0x02,0x00,0x00,0x70,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x6D,0x02,0x00,0x00,0x75,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0x77,0x02,0x00,0x00,0x49,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00, - 0x78,0x02,0x00,0x00,0x77,0x02,0x00,0x00,0x73,0x02,0x00,0x00,0xF7,0x00,0x03,0x00, - 0x7A,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x78,0x02,0x00,0x00, - 0x79,0x02,0x00,0x00,0x7A,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0x79,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x7B,0x02,0x00,0x00,0x49,0x02,0x00,0x00, - 0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x7D,0x02,0x00,0x00,0x7B,0x02,0x00,0x00, - 0x7C,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0x7A,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, - 0x7A,0x02,0x00,0x00,0xF5,0x00,0x07,0x00,0x99,0x00,0x00,0x00,0x7E,0x02,0x00,0x00, - 0x78,0x02,0x00,0x00,0x71,0x02,0x00,0x00,0x7D,0x02,0x00,0x00,0x79,0x02,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x76,0x02,0x00,0x00,0x7E,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x80,0x02,0x00,0x00,0x49,0x02,0x00,0x00,0xBA,0x00,0x05,0x00, - 0x99,0x00,0x00,0x00,0x81,0x02,0x00,0x00,0x80,0x02,0x00,0x00,0x7C,0x02,0x00,0x00, - 0xF7,0x00,0x03,0x00,0x83,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, - 0x81,0x02,0x00,0x00,0x82,0x02,0x00,0x00,0x83,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, - 0x82,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x84,0x02,0x00,0x00, - 0x49,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x86,0x02,0x00,0x00, - 0x84,0x02,0x00,0x00,0x85,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0x83,0x02,0x00,0x00, - 0xF8,0x00,0x02,0x00,0x83,0x02,0x00,0x00,0xF5,0x00,0x07,0x00,0x99,0x00,0x00,0x00, - 0x87,0x02,0x00,0x00,0x81,0x02,0x00,0x00,0x7A,0x02,0x00,0x00,0x86,0x02,0x00,0x00, - 0x82,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x7F,0x02,0x00,0x00,0x87,0x02,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x88,0x02,0x00,0x00,0x89,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x99,0x00,0x00,0x00,0x8A,0x02,0x00,0x00,0x48,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x99,0x00,0x00,0x00,0x8B,0x02,0x00,0x00,0x52,0x02,0x00,0x00,0xA7,0x00,0x05,0x00, - 0x99,0x00,0x00,0x00,0x8C,0x02,0x00,0x00,0x8A,0x02,0x00,0x00,0x8B,0x02,0x00,0x00, - 0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x8D,0x02,0x00,0x00,0x8C,0x02,0x00,0x00, - 0xF7,0x00,0x03,0x00,0x90,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, - 0x8D,0x02,0x00,0x00,0x8F,0x02,0x00,0x00,0xA5,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, - 0x8F,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x92,0x02,0x00,0x00,0x95,0x02,0x00,0x00, - 0x94,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x9C,0x02,0x00,0x00, - 0x97,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x9B,0x02,0x00,0x00,0x9C,0x02,0x00,0x00, - 0x41,0x00,0x05,0x00,0x9E,0x02,0x00,0x00,0x9F,0x02,0x00,0x00,0x9A,0x02,0x00,0x00, - 0xDC,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xA0,0x02,0x00,0x00, - 0x9F,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x9D,0x02,0x00,0x00,0xA0,0x02,0x00,0x00, - 0x39,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0xA1,0x02,0x00,0x00,0x15,0x00,0x00,0x00, - 0x9B,0x02,0x00,0x00,0x9D,0x02,0x00,0x00,0x57,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0xA2,0x02,0x00,0x00,0x95,0x02,0x00,0x00,0xA1,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, - 0xA3,0x02,0x00,0x00,0xA2,0x02,0x00,0x00,0x39,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0xA4,0x02,0x00,0x00,0x0E,0x00,0x00,0x00,0xA3,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x8E,0x02,0x00,0x00,0xA4,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0x90,0x02,0x00,0x00, - 0xF8,0x00,0x02,0x00,0xA5,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0xA6,0x02,0x00,0x00,0x88,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x8E,0x02,0x00,0x00, - 0xA6,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0x90,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, - 0x90,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xA7,0x02,0x00,0x00, - 0x8E,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x88,0x02,0x00,0x00,0xA7,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0xA8,0x02,0x00,0x00,0x48,0x02,0x00,0x00, - 0xF7,0x00,0x03,0x00,0xAB,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, - 0xA8,0x02,0x00,0x00,0xAA,0x02,0x00,0x00,0xAF,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, - 0xAA,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xAD,0x02,0x00,0x00, - 0x88,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xAC,0x02,0x00,0x00,0xAD,0x02,0x00,0x00, - 0x39,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0xAE,0x02,0x00,0x00,0x0B,0x00,0x00,0x00, - 0xAC,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xA9,0x02,0x00,0x00,0xAE,0x02,0x00,0x00, - 0xF9,0x00,0x02,0x00,0xAB,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xAF,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xB0,0x02,0x00,0x00,0x88,0x02,0x00,0x00, - 0x3E,0x00,0x03,0x00,0xA9,0x02,0x00,0x00,0xB0,0x02,0x00,0x00,0xF9,0x00,0x02,0x00, - 0xAB,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xAB,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0xB1,0x02,0x00,0x00,0xA9,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x88,0x02,0x00,0x00,0xB1,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00, - 0xB2,0x02,0x00,0x00,0x52,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0xB5,0x02,0x00,0x00, - 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0xB2,0x02,0x00,0x00,0xB4,0x02,0x00,0x00, - 0xBC,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xB4,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0xB8,0x02,0x00,0x00,0xB7,0x02,0x00,0x00,0x41,0x00,0x05,0x00, - 0x17,0x00,0x00,0x00,0xB9,0x02,0x00,0x00,0x88,0x02,0x00,0x00,0x65,0x00,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xBA,0x02,0x00,0x00,0xB9,0x02,0x00,0x00, - 0x8E,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0xBB,0x02,0x00,0x00,0xB8,0x02,0x00,0x00, - 0xBA,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xB3,0x02,0x00,0x00,0xBB,0x02,0x00,0x00, - 0xF9,0x00,0x02,0x00,0xB5,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xBC,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xBD,0x02,0x00,0x00,0x88,0x02,0x00,0x00, - 0x3E,0x00,0x03,0x00,0xB3,0x02,0x00,0x00,0xBD,0x02,0x00,0x00,0xF9,0x00,0x02,0x00, - 0xB5,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xB5,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0xBE,0x02,0x00,0x00,0xB3,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x88,0x02,0x00,0x00,0xBE,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00, - 0xBF,0x02,0x00,0x00,0x6D,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0xC0,0x02,0x00,0x00,0xB7,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0xC1,0x02,0x00,0x00,0x88,0x02,0x00,0x00,0x50,0x00,0x07,0x00,0xE5,0x00,0x00,0x00, - 0xC2,0x02,0x00,0x00,0xBF,0x02,0x00,0x00,0xBF,0x02,0x00,0x00,0xBF,0x02,0x00,0x00, - 0xBF,0x02,0x00,0x00,0xA9,0x00,0x06,0x00,0x07,0x00,0x00,0x00,0xC3,0x02,0x00,0x00, - 0xC2,0x02,0x00,0x00,0xC0,0x02,0x00,0x00,0xC1,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x88,0x02,0x00,0x00,0xC3,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xC4,0x02,0x00,0x00, - 0x98,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0xC5,0x02,0x00,0x00, - 0x5B,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0xC7,0x02,0x00,0x00,0x00,0x00,0x00,0x00, - 0xFA,0x00,0x04,0x00,0xC5,0x02,0x00,0x00,0xC6,0x02,0x00,0x00,0xD7,0x02,0x00,0x00, - 0xF8,0x00,0x02,0x00,0xC6,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, - 0xCC,0x02,0x00,0x00,0xC8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xCB,0x02,0x00,0x00, - 0xCC,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xCE,0x02,0x00,0x00, - 0xC9,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xCF,0x02,0x00,0x00, - 0xCE,0x02,0x00,0x00,0xCE,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x3E,0x00,0x03,0x00,0xCD,0x02,0x00,0x00,0xCF,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0xD1,0x02,0x00,0x00,0xC9,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, - 0x10,0x00,0x00,0x00,0xD2,0x02,0x00,0x00,0xD1,0x02,0x00,0x00,0xD1,0x02,0x00,0x00, - 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xD0,0x02,0x00,0x00, - 0xD2,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xD4,0x02,0x00,0x00, - 0xCA,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xD5,0x02,0x00,0x00, - 0xD4,0x02,0x00,0x00,0xD4,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x3E,0x00,0x03,0x00,0xD3,0x02,0x00,0x00,0xD5,0x02,0x00,0x00,0x39,0x00,0x08,0x00, - 0x06,0x00,0x00,0x00,0xD6,0x02,0x00,0x00,0x3D,0x00,0x00,0x00,0xCB,0x02,0x00,0x00, - 0xCD,0x02,0x00,0x00,0xD0,0x02,0x00,0x00,0xD3,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, - 0xC4,0x02,0x00,0x00,0xD6,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xC7,0x02,0x00,0x00, - 0xF8,0x00,0x02,0x00,0xD7,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00, - 0xD8,0x02,0x00,0x00,0x64,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0xDA,0x02,0x00,0x00, - 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0xD8,0x02,0x00,0x00,0xD9,0x02,0x00,0x00, - 0xEF,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xD9,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0xDC,0x02,0x00,0x00,0xC8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, - 0xDB,0x02,0x00,0x00,0xDC,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0xDE,0x02,0x00,0x00,0xC9,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, - 0xDF,0x02,0x00,0x00,0xDE,0x02,0x00,0x00,0xDE,0x02,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xDD,0x02,0x00,0x00,0xDF,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xE1,0x02,0x00,0x00,0xC9,0x02,0x00,0x00, - 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xE2,0x02,0x00,0x00,0xE1,0x02,0x00,0x00, - 0xE1,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0xE0,0x02,0x00,0x00,0xE2,0x02,0x00,0x00,0x39,0x00,0x07,0x00,0x06,0x00,0x00,0x00, - 0xE3,0x02,0x00,0x00,0x43,0x00,0x00,0x00,0xDB,0x02,0x00,0x00,0xDD,0x02,0x00,0x00, - 0xE0,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xC4,0x02,0x00,0x00,0xE3,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0xE4,0x02,0x00,0x00,0xC4,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xE6,0x02,0x00,0x00,0xC8,0x02,0x00,0x00, - 0x3E,0x00,0x03,0x00,0xE5,0x02,0x00,0x00,0xE6,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0xE8,0x02,0x00,0x00,0xC9,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, - 0x10,0x00,0x00,0x00,0xE9,0x02,0x00,0x00,0xE8,0x02,0x00,0x00,0xE8,0x02,0x00,0x00, - 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xE7,0x02,0x00,0x00, - 0xE9,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xEB,0x02,0x00,0x00, - 0xCA,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xEC,0x02,0x00,0x00, - 0xEB,0x02,0x00,0x00,0xEB,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x3E,0x00,0x03,0x00,0xEA,0x02,0x00,0x00,0xEC,0x02,0x00,0x00,0x39,0x00,0x07,0x00, - 0x06,0x00,0x00,0x00,0xED,0x02,0x00,0x00,0x43,0x00,0x00,0x00,0xE5,0x02,0x00,0x00, - 0xE7,0x02,0x00,0x00,0xEA,0x02,0x00,0x00,0x0C,0x00,0x07,0x00,0x06,0x00,0x00,0x00, - 0xEE,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0xE4,0x02,0x00,0x00, - 0xED,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xC4,0x02,0x00,0x00,0xEE,0x02,0x00,0x00, - 0xF9,0x00,0x02,0x00,0xDA,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xEF,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0xF0,0x02,0x00,0x00,0x76,0x02,0x00,0x00, - 0xF7,0x00,0x03,0x00,0xF2,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, - 0xF0,0x02,0x00,0x00,0xF1,0x02,0x00,0x00,0xFF,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, - 0xF1,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xF4,0x02,0x00,0x00, - 0xC8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xF3,0x02,0x00,0x00,0xF4,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xF6,0x02,0x00,0x00,0xC9,0x02,0x00,0x00, - 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xF7,0x02,0x00,0x00,0xF6,0x02,0x00,0x00, - 0xF6,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0xF5,0x02,0x00,0x00,0xF7,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0xF9,0x02,0x00,0x00,0xC9,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, - 0xFA,0x02,0x00,0x00,0xF9,0x02,0x00,0x00,0xF9,0x02,0x00,0x00,0x02,0x00,0x00,0x00, - 0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xF8,0x02,0x00,0x00,0xFA,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xFC,0x02,0x00,0x00,0xCA,0x02,0x00,0x00, - 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xFD,0x02,0x00,0x00,0xFC,0x02,0x00,0x00, - 0xFC,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0xFB,0x02,0x00,0x00,0xFD,0x02,0x00,0x00,0x39,0x00,0x08,0x00,0x06,0x00,0x00,0x00, - 0xFE,0x02,0x00,0x00,0x49,0x00,0x00,0x00,0xF3,0x02,0x00,0x00,0xF5,0x02,0x00,0x00, - 0xF8,0x02,0x00,0x00,0xFB,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xC4,0x02,0x00,0x00, - 0xFE,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xF2,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, - 0xFF,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x00,0x03,0x00,0x00, - 0x7F,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0x02,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0xFA,0x00,0x04,0x00,0x00,0x03,0x00,0x00,0x01,0x03,0x00,0x00,0x02,0x03,0x00,0x00, - 0xF8,0x00,0x02,0x00,0x01,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x05,0x03,0x00,0x00,0xC9,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, - 0x06,0x03,0x00,0x00,0x05,0x03,0x00,0x00,0x05,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x07,0x03,0x00,0x00,0x08,0x03,0x00,0x00, - 0x04,0x03,0x00,0x00,0xDC,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x08,0x03,0x00,0x00, - 0x06,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x09,0x03,0x00,0x00, - 0xC9,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x0A,0x03,0x00,0x00, - 0x09,0x03,0x00,0x00,0x09,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x07,0x03,0x00,0x00,0x0B,0x03,0x00,0x00,0x04,0x03,0x00,0x00, - 0xE9,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x0B,0x03,0x00,0x00,0x0A,0x03,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0D,0x03,0x00,0x00,0xCA,0x02,0x00,0x00, - 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x0E,0x03,0x00,0x00,0x0D,0x03,0x00,0x00, - 0x0D,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00, - 0x07,0x03,0x00,0x00,0x0F,0x03,0x00,0x00,0x04,0x03,0x00,0x00,0x0C,0x03,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x0F,0x03,0x00,0x00,0x0E,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x11,0x03,0x00,0x00,0xCA,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, - 0x10,0x00,0x00,0x00,0x12,0x03,0x00,0x00,0x11,0x03,0x00,0x00,0x11,0x03,0x00,0x00, - 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x07,0x03,0x00,0x00, - 0x13,0x03,0x00,0x00,0x04,0x03,0x00,0x00,0x10,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x13,0x03,0x00,0x00,0x12,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x16,0x03,0x00,0x00,0x15,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, - 0x17,0x03,0x00,0x00,0x16,0x03,0x00,0x00,0x16,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x07,0x03,0x00,0x00,0x18,0x03,0x00,0x00, - 0x04,0x03,0x00,0x00,0x14,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x18,0x03,0x00,0x00, - 0x17,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1A,0x03,0x00,0x00, - 0x15,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x1B,0x03,0x00,0x00, - 0x1A,0x03,0x00,0x00,0x1A,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x07,0x03,0x00,0x00,0x1C,0x03,0x00,0x00,0x04,0x03,0x00,0x00, - 0x19,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x1C,0x03,0x00,0x00,0x1B,0x03,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1F,0x03,0x00,0x00,0x1E,0x03,0x00,0x00, - 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x20,0x03,0x00,0x00,0x1F,0x03,0x00,0x00, - 0x1F,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00, - 0x07,0x03,0x00,0x00,0x21,0x03,0x00,0x00,0x04,0x03,0x00,0x00,0x1D,0x03,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x21,0x03,0x00,0x00,0x20,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x23,0x03,0x00,0x00,0x1E,0x03,0x00,0x00,0x4F,0x00,0x07,0x00, - 0x10,0x00,0x00,0x00,0x24,0x03,0x00,0x00,0x23,0x03,0x00,0x00,0x23,0x03,0x00,0x00, - 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x07,0x03,0x00,0x00, - 0x25,0x03,0x00,0x00,0x04,0x03,0x00,0x00,0x22,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x25,0x03,0x00,0x00,0x24,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, - 0x29,0x03,0x00,0x00,0xC8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x28,0x03,0x00,0x00, - 0x29,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x4D,0x00,0x00,0x00,0x2B,0x03,0x00,0x00, - 0x04,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x2A,0x03,0x00,0x00,0x2B,0x03,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x2D,0x03,0x00,0x00,0x27,0x03,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x2C,0x03,0x00,0x00,0x2D,0x03,0x00,0x00,0x39,0x00,0x07,0x00, - 0x06,0x00,0x00,0x00,0x2E,0x03,0x00,0x00,0x55,0x00,0x00,0x00,0x28,0x03,0x00,0x00, - 0x2A,0x03,0x00,0x00,0x2C,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0xC4,0x02,0x00,0x00, - 0x2E,0x03,0x00,0x00,0xF9,0x00,0x02,0x00,0x02,0x03,0x00,0x00,0xF8,0x00,0x02,0x00, - 0x02,0x03,0x00,0x00,0xF9,0x00,0x02,0x00,0xF2,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, - 0xF2,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xDA,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, - 0xDA,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xC7,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, - 0xC7,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x2F,0x03,0x00,0x00, - 0x48,0x02,0x00,0x00,0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x30,0x03,0x00,0x00, - 0x2F,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x31,0x03,0x00,0x00, - 0x52,0x02,0x00,0x00,0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x32,0x03,0x00,0x00, - 0x31,0x03,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x33,0x03,0x00,0x00, - 0x30,0x03,0x00,0x00,0x32,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00, - 0x34,0x03,0x00,0x00,0x6D,0x02,0x00,0x00,0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00, - 0x35,0x03,0x00,0x00,0x34,0x03,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00, - 0x36,0x03,0x00,0x00,0x33,0x03,0x00,0x00,0x35,0x03,0x00,0x00,0xF7,0x00,0x03,0x00, - 0x39,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x36,0x03,0x00,0x00, - 0x38,0x03,0x00,0x00,0x44,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x38,0x03,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x3A,0x03,0x00,0x00,0xC4,0x02,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x3C,0x03,0x00,0x00,0x3B,0x03,0x00,0x00, - 0x83,0x00,0x05,0x00,0x06,0x00,0x00,0x00,0x3D,0x03,0x00,0x00,0x3A,0x03,0x00,0x00, - 0x3C,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x3F,0x03,0x00,0x00, - 0x88,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x3E,0x03,0x00,0x00,0x3F,0x03,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x41,0x03,0x00,0x00,0xB7,0x02,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x40,0x03,0x00,0x00,0x41,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x42,0x03,0x00,0x00,0x3D,0x03,0x00,0x00,0x39,0x00,0x07,0x00,0x07,0x00,0x00,0x00, - 0x43,0x03,0x00,0x00,0x32,0x00,0x00,0x00,0x3E,0x03,0x00,0x00,0x40,0x03,0x00,0x00, - 0x42,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x37,0x03,0x00,0x00,0x43,0x03,0x00,0x00, - 0xF9,0x00,0x02,0x00,0x39,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x44,0x03,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x45,0x03,0x00,0x00,0x88,0x02,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x37,0x03,0x00,0x00,0x45,0x03,0x00,0x00,0xF9,0x00,0x02,0x00, - 0x39,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x39,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x46,0x03,0x00,0x00,0x37,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x88,0x02,0x00,0x00,0x46,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, - 0x48,0x03,0x00,0x00,0x47,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x49,0x03,0x00,0x00,0x88,0x02,0x00,0x00,0x8E,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x4A,0x03,0x00,0x00,0x49,0x03,0x00,0x00,0x48,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x88,0x02,0x00,0x00,0x4A,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, - 0x4D,0x03,0x00,0x00,0x4C,0x03,0x00,0x00,0x81,0x00,0x05,0x00,0x10,0x00,0x00,0x00, - 0x4F,0x03,0x00,0x00,0x4D,0x03,0x00,0x00,0x4E,0x03,0x00,0x00,0x8E,0x00,0x05,0x00, - 0x10,0x00,0x00,0x00,0x50,0x03,0x00,0x00,0x4F,0x03,0x00,0x00,0x82,0x00,0x00,0x00, - 0x85,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x51,0x03,0x00,0x00,0x50,0x03,0x00,0x00, - 0x4E,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x4B,0x03,0x00,0x00,0x51,0x03,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x54,0x03,0x00,0x00,0x88,0x02,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x53,0x03,0x00,0x00,0x54,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x10,0x00,0x00,0x00,0x56,0x03,0x00,0x00,0xC8,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x55,0x03,0x00,0x00,0x56,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, - 0x58,0x03,0x00,0x00,0x4B,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x57,0x03,0x00,0x00, - 0x58,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x5A,0x03,0x00,0x00, - 0x52,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x59,0x03,0x00,0x00,0x5A,0x03,0x00,0x00, - 0x39,0x00,0x08,0x00,0x07,0x00,0x00,0x00,0x5B,0x03,0x00,0x00,0x5C,0x00,0x00,0x00, - 0x53,0x03,0x00,0x00,0x55,0x03,0x00,0x00,0x57,0x03,0x00,0x00,0x59,0x03,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x88,0x02,0x00,0x00,0x5B,0x03,0x00,0x00,0x41,0x00,0x05,0x00, - 0x5C,0x03,0x00,0x00,0x5D,0x03,0x00,0x00,0x9A,0x02,0x00,0x00,0xE9,0x01,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x5E,0x03,0x00,0x00,0x5D,0x03,0x00,0x00, - 0xAB,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x5F,0x03,0x00,0x00,0x5E,0x03,0x00,0x00, - 0xDC,0x01,0x00,0x00,0xF7,0x00,0x03,0x00,0x61,0x03,0x00,0x00,0x00,0x00,0x00,0x00, - 0xFA,0x00,0x04,0x00,0x5F,0x03,0x00,0x00,0x60,0x03,0x00,0x00,0x61,0x03,0x00,0x00, - 0xF8,0x00,0x02,0x00,0x60,0x03,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, - 0x62,0x03,0x00,0x00,0x88,0x02,0x00,0x00,0x65,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x63,0x03,0x00,0x00,0x62,0x03,0x00,0x00,0xB4,0x00,0x05,0x00, - 0x99,0x00,0x00,0x00,0x64,0x03,0x00,0x00,0x63,0x03,0x00,0x00,0x98,0x00,0x00,0x00, - 0xF9,0x00,0x02,0x00,0x61,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x61,0x03,0x00,0x00, - 0xF5,0x00,0x07,0x00,0x99,0x00,0x00,0x00,0x65,0x03,0x00,0x00,0x5F,0x03,0x00,0x00, - 0x39,0x03,0x00,0x00,0x64,0x03,0x00,0x00,0x60,0x03,0x00,0x00,0xF7,0x00,0x03,0x00, - 0x67,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x65,0x03,0x00,0x00, - 0x66,0x03,0x00,0x00,0x67,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x66,0x03,0x00,0x00, - 0xFC,0x00,0x01,0x00,0xF8,0x00,0x02,0x00,0x67,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x69,0x03,0x00,0x00,0x88,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x0B,0x01,0x00,0x00,0x69,0x03,0x00,0x00,0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xEC,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xEF,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xF4,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xF6,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0xF9,0x02,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x02,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x04,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x07,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x0A,0x03,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x13,0x03,0x00,0x00,0x70,0x74,0x73,0x00, + 0x05,0x00,0x04,0x00,0x22,0x03,0x00,0x00,0x76,0x5F,0x65,0x66,0x00,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x2B,0x03,0x00,0x00,0x76,0x5F,0x67,0x68,0x00,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x34,0x03,0x00,0x00,0x76,0x5F,0x6E,0x00,0x05,0x00,0x04,0x00, + 0x35,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x37,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x39,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x4A,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x4C,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x4E,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x56,0x03,0x00,0x00,0x70,0x6F,0x73,0x00,0x05,0x00,0x05,0x00,0x58,0x03,0x00,0x00, + 0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75,0x76,0x00,0x00,0x00,0x05,0x00,0x03,0x00, + 0x5E,0x03,0x00,0x00,0x73,0x70,0x00,0x00,0x05,0x00,0x05,0x00,0x63,0x03,0x00,0x00, + 0x76,0x5F,0x75,0x76,0x5F,0x62,0x6F,0x75,0x6E,0x64,0x73,0x00,0x05,0x00,0x04,0x00, + 0x6C,0x03,0x00,0x00,0x76,0x5F,0x75,0x73,0x65,0x72,0x00,0x00,0x05,0x00,0x04,0x00, + 0x6F,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, + 0x71,0x03,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x0B,0x01,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x4A,0x02,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x51,0x02,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x47,0x00,0x04,0x00, + 0x5E,0x02,0x00,0x00,0x1E,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x9A,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x9A,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x9A,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x9A,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x0C,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x9C,0x02,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x9C,0x02,0x00,0x00,0x22,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xB3,0x02,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xB3,0x02,0x00,0x00,0x22,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xC7,0x02,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xD8,0x02,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0xD9,0x02,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x22,0x03,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2B,0x03,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x34,0x03,0x00,0x00,0x0E,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x34,0x03,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x63,0x03,0x00,0x00,0x1E,0x00,0x00,0x00,0x0A,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x6C,0x03,0x00,0x00,0x1E,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x21,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x21,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x17,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x21,0x00,0x05,0x00,0x18,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x21,0x00,0x04,0x00,0x1D,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x04,0x00,0x21,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x05,0x00,0x25,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x04,0x00, + 0x2A,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x21,0x00,0x06,0x00, + 0x2E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x21,0x00,0x07,0x00,0x38,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x21,0x00,0x06,0x00,0x3F,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x4B,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x4B,0x00,0x00,0x00, + 0x4C,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x1C,0x00,0x04,0x00,0x4D,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x4E,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x4D,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x4F,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x50,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x21,0x00,0x06,0x00,0x51,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x4E,0x00,0x00,0x00,0x50,0x00,0x00,0x00, + 0x1E,0x00,0x08,0x00,0x57,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x58,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x57,0x00,0x00,0x00, + 0x21,0x00,0x05,0x00,0x59,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x58,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x5E,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x62,0x00,0x00,0x00, + 0x2F,0xBA,0xE8,0x3E,0x2C,0x00,0x06,0x00,0x5E,0x00,0x00,0x00,0x63,0x00,0x00,0x00, + 0x62,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4B,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0xCD,0xCC,0x0C,0x40,0x2C,0x00,0x06,0x00, + 0x5E,0x00,0x00,0x00,0x72,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0x71,0x00,0x00,0x00, + 0x71,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x82,0x00,0x00,0x00, + 0x00,0x00,0x00,0x3F,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x8D,0x00,0x00,0x00, + 0x00,0x00,0x00,0xBF,0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x14,0x00,0x02,0x00,0x99,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4B,0x00,0x00,0x00,0xB4,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4B,0x00,0x00,0x00,0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0xCE,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0xDA,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0xE5,0x00,0x00,0x00,0x99,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xF2,0x00,0x00,0x00,0x00,0x00,0x80,0xBF,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xF3,0x00,0x00,0x00,0x00,0x00,0x80,0x3F,0x20,0x00,0x04,0x00, + 0x0A,0x01,0x00,0x00,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x0A,0x01,0x00,0x00,0x0B,0x01,0x00,0x00,0x03,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0x0E,0x01,0x00,0x00,0x06,0x00,0x00,0x00,0x18,0x00,0x04,0x00, + 0x27,0x01,0x00,0x00,0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x28,0x01,0x00,0x00,0x07,0x00,0x00,0x00,0x27,0x01,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xDC,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xE9,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x13,0x02,0x00,0x00,0x99,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x14,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x13,0x02,0x00,0x00,0x20,0x00,0x04,0x00, + 0x47,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x47,0x02,0x00,0x00,0x48,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x49,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x49,0x02,0x00,0x00,0x4A,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x47,0x02,0x00,0x00,0x4D,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0x50,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x49,0x02,0x00,0x00,0x51,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x52,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4B,0x00,0x00,0x00,0x57,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0x5A,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xCD,0x00,0x00,0x00,0x5D,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x49,0x02,0x00,0x00,0x5E,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x47,0x02,0x00,0x00,0x63,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x66,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x99,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x71,0x02,0x00,0x00,0x00,0x00,0xC0,0x3F,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x78,0x02,0x00,0x00,0x00,0x00,0x20,0x40,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x7F,0x02,0x00,0x00,0x00,0x00,0x60,0x40,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x86,0x02,0x00,0x00,0x00,0x00,0x90,0x40,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x8D,0x02,0x00,0x00,0x00,0x00,0xB0,0x40,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x94,0x02,0x00,0x00,0x00,0x00,0xD0,0x40,0x2C,0x00,0x07,0x00, + 0x07,0x00,0x00,0x00,0x98,0x02,0x00,0x00,0x98,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x98,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x1E,0x00,0x05,0x00,0x9A,0x02,0x00,0x00, + 0x10,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x9B,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x9A,0x02,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x9B,0x02,0x00,0x00,0x9C,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x9D,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x9E,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0xA8,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x19,0x00,0x09,0x00, + 0xB0,0x02,0x00,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x1B,0x00,0x03,0x00,0xB1,0x02,0x00,0x00,0xB0,0x02,0x00,0x00,0x20,0x00,0x04,0x00, + 0xB2,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xB1,0x02,0x00,0x00,0x3B,0x00,0x04,0x00, + 0xB2,0x02,0x00,0x00,0xB3,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x49,0x02,0x00,0x00,0xC7,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x49,0x02,0x00,0x00,0xD8,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x49,0x02,0x00,0x00,0xD9,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x12,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x4D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x12,0x03,0x00,0x00,0x13,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x1D,0x03,0x00,0x00,0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x21,0x03,0x00,0x00,0x04,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x49,0x02,0x00,0x00,0x22,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x26,0x03,0x00,0x00,0x05,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x2A,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x49,0x02,0x00,0x00,0x2B,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x2F,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x33,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x33,0x03,0x00,0x00,0x34,0x03,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x47,0x02,0x00,0x00,0x56,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x47,0x02,0x00,0x00,0x58,0x03,0x00,0x00,0x06,0x00,0x00,0x00,0x2C,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x5A,0x03,0x00,0x00,0xF3,0x00,0x00,0x00,0xF2,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x49,0x02,0x00,0x00,0x63,0x03,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3B,0x00,0x04,0x00,0x49,0x02,0x00,0x00,0x6C,0x03,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x66,0x02,0x00,0x00,0x67,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x66,0x02,0x00,0x00,0x6D,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x66,0x02,0x00,0x00,0x74,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x66,0x02,0x00,0x00,0x7B,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x66,0x02,0x00,0x00,0x82,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x66,0x02,0x00,0x00,0x89,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x66,0x02,0x00,0x00,0x90,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x97,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x99,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xA2,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xA5,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xA7,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0xAF,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0xB7,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0xBA,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0xBD,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0xC4,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x17,0x00,0x00,0x00,0xD4,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xDA,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xDC,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xDF,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xE2,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xEA,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xEC,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xEF,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xF4,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xF6,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0xF9,0x02,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x02,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x04,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x07,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x0A,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x35,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x4E,0x00,0x00,0x00,0x37,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x50,0x00,0x00,0x00,0x39,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x44,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x4A,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x4C,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x17,0x00,0x00,0x00,0x4E,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x58,0x00,0x00,0x00,0x5E,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x6F,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x58,0x00,0x00,0x00,0x71,0x03,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x4B,0x02,0x00,0x00,0x4A,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x4C,0x02,0x00,0x00,0x4B,0x02,0x00,0x00,0x4B,0x02,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x48,0x02,0x00,0x00, + 0x4C,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x4E,0x02,0x00,0x00, + 0x4A,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x4F,0x02,0x00,0x00, + 0x4E,0x02,0x00,0x00,0x4E,0x02,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x4D,0x02,0x00,0x00,0x4F,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x52,0x02,0x00,0x00,0x53,0x02,0x00,0x00,0x51,0x02,0x00,0x00,0xB8,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x54,0x02,0x00,0x00,0x53,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x50,0x02,0x00,0x00,0x54,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x52,0x02,0x00,0x00,0x55,0x02,0x00,0x00,0x51,0x02,0x00,0x00,0xB4,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x56,0x02,0x00,0x00,0x55,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xCE,0x00,0x00,0x00,0x56,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x52,0x02,0x00,0x00,0x58,0x02,0x00,0x00,0x51,0x02,0x00,0x00,0x57,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x59,0x02,0x00,0x00,0x58,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xDA,0x00,0x00,0x00,0x59,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x52,0x02,0x00,0x00,0x5B,0x02,0x00,0x00,0x51,0x02,0x00,0x00,0x65,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x5C,0x02,0x00,0x00,0x5B,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x5A,0x02,0x00,0x00,0x5C,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x52,0x02,0x00,0x00,0x5F,0x02,0x00,0x00,0x5E,0x02,0x00,0x00,0xB8,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x60,0x02,0x00,0x00,0x5F,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x5D,0x02,0x00,0x00,0x60,0x02,0x00,0x00,0x41,0x00,0x05,0x00, + 0x52,0x02,0x00,0x00,0x61,0x02,0x00,0x00,0x5E,0x02,0x00,0x00,0xB4,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x62,0x02,0x00,0x00,0x61,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x0E,0x01,0x00,0x00,0x62,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x64,0x02,0x00,0x00,0x5E,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x65,0x02,0x00,0x00,0x64,0x02,0x00,0x00,0x64,0x02,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x63,0x02,0x00,0x00, + 0x65,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x68,0x02,0x00,0x00, + 0x5A,0x02,0x00,0x00,0xBE,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x69,0x02,0x00,0x00, + 0x68,0x02,0x00,0x00,0x98,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x6A,0x02,0x00,0x00,0x5A,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0x6B,0x02,0x00,0x00,0x6A,0x02,0x00,0x00,0x82,0x00,0x00,0x00,0xA7,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x6C,0x02,0x00,0x00,0x69,0x02,0x00,0x00,0x6B,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x67,0x02,0x00,0x00,0x6C,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x6E,0x02,0x00,0x00,0x5A,0x02,0x00,0x00,0xBA,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x6F,0x02,0x00,0x00,0x6E,0x02,0x00,0x00,0x82,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x70,0x02,0x00,0x00,0x5A,0x02,0x00,0x00, + 0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x72,0x02,0x00,0x00,0x70,0x02,0x00,0x00, + 0x71,0x02,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x73,0x02,0x00,0x00, + 0x6F,0x02,0x00,0x00,0x72,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x6D,0x02,0x00,0x00, + 0x73,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x75,0x02,0x00,0x00, + 0x5A,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x76,0x02,0x00,0x00, + 0x75,0x02,0x00,0x00,0x71,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x77,0x02,0x00,0x00,0x5A,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0x79,0x02,0x00,0x00,0x77,0x02,0x00,0x00,0x78,0x02,0x00,0x00,0xA7,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x7A,0x02,0x00,0x00,0x76,0x02,0x00,0x00,0x79,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x74,0x02,0x00,0x00,0x7A,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x7C,0x02,0x00,0x00,0x5A,0x02,0x00,0x00,0xBA,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x7D,0x02,0x00,0x00,0x7C,0x02,0x00,0x00,0x78,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x7E,0x02,0x00,0x00,0x5A,0x02,0x00,0x00, + 0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x80,0x02,0x00,0x00,0x7E,0x02,0x00,0x00, + 0x7F,0x02,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x81,0x02,0x00,0x00, + 0x7D,0x02,0x00,0x00,0x80,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x7B,0x02,0x00,0x00, + 0x81,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x83,0x02,0x00,0x00, + 0x5A,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x84,0x02,0x00,0x00, + 0x83,0x02,0x00,0x00,0x7F,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x85,0x02,0x00,0x00,0x5A,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0x87,0x02,0x00,0x00,0x85,0x02,0x00,0x00,0x86,0x02,0x00,0x00,0xA7,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x88,0x02,0x00,0x00,0x84,0x02,0x00,0x00,0x87,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x82,0x02,0x00,0x00,0x88,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x8A,0x02,0x00,0x00,0x5A,0x02,0x00,0x00,0xBA,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x8B,0x02,0x00,0x00,0x8A,0x02,0x00,0x00,0x86,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x8C,0x02,0x00,0x00,0x5A,0x02,0x00,0x00, + 0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x8E,0x02,0x00,0x00,0x8C,0x02,0x00,0x00, + 0x8D,0x02,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x8F,0x02,0x00,0x00, + 0x8B,0x02,0x00,0x00,0x8E,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x89,0x02,0x00,0x00, + 0x8F,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x91,0x02,0x00,0x00, + 0x5A,0x02,0x00,0x00,0xBA,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x92,0x02,0x00,0x00, + 0x91,0x02,0x00,0x00,0x8D,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00, + 0x93,0x02,0x00,0x00,0x5A,0x02,0x00,0x00,0xB8,0x00,0x05,0x00,0x99,0x00,0x00,0x00, + 0x95,0x02,0x00,0x00,0x93,0x02,0x00,0x00,0x94,0x02,0x00,0x00,0xA7,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x96,0x02,0x00,0x00,0x92,0x02,0x00,0x00,0x95,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x90,0x02,0x00,0x00,0x96,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x97,0x02,0x00,0x00,0x98,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x9E,0x02,0x00,0x00, + 0x9F,0x02,0x00,0x00,0x9C,0x02,0x00,0x00,0x9D,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0xA0,0x02,0x00,0x00,0x9F,0x02,0x00,0x00,0xAA,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0xA1,0x02,0x00,0x00,0xA0,0x02,0x00,0x00,0xDC,0x01,0x00,0x00, + 0xF7,0x00,0x03,0x00,0xA4,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0xA1,0x02,0x00,0x00,0xA3,0x02,0x00,0x00,0xAC,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xA3,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xA6,0x02,0x00,0x00, + 0x4D,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xA5,0x02,0x00,0x00,0xA6,0x02,0x00,0x00, + 0x41,0x00,0x05,0x00,0xA8,0x02,0x00,0x00,0xA9,0x02,0x00,0x00,0x9C,0x02,0x00,0x00, + 0xDC,0x01,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xAA,0x02,0x00,0x00, + 0xA9,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xA7,0x02,0x00,0x00,0xAA,0x02,0x00,0x00, + 0x39,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0xAB,0x02,0x00,0x00,0x15,0x00,0x00,0x00, + 0xA5,0x02,0x00,0x00,0xA7,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xA2,0x02,0x00,0x00, + 0xAB,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xA4,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xAC,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xAD,0x02,0x00,0x00, + 0x4D,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xA2,0x02,0x00,0x00,0xAD,0x02,0x00,0x00, + 0xF9,0x00,0x02,0x00,0xA4,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xA4,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xAE,0x02,0x00,0x00,0xA2,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x99,0x02,0x00,0x00,0xAE,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0xB1,0x02,0x00,0x00,0xB4,0x02,0x00,0x00,0xB3,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xB5,0x02,0x00,0x00,0x99,0x02,0x00,0x00,0x57,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0xB6,0x02,0x00,0x00,0xB4,0x02,0x00,0x00,0xB5,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xB7,0x02,0x00,0x00,0xB6,0x02,0x00,0x00,0x39,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0xB8,0x02,0x00,0x00,0x0E,0x00,0x00,0x00,0xB7,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xAF,0x02,0x00,0x00,0xB8,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x99,0x00,0x00,0x00,0xB9,0x02,0x00,0x00,0x67,0x02,0x00,0x00,0xF7,0x00,0x03,0x00, + 0xBC,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0xB9,0x02,0x00,0x00, + 0xBB,0x02,0x00,0x00,0xC0,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xBB,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xBE,0x02,0x00,0x00,0xAF,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xBD,0x02,0x00,0x00,0xBE,0x02,0x00,0x00,0x39,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0xBF,0x02,0x00,0x00,0x0B,0x00,0x00,0x00,0xBD,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xBA,0x02,0x00,0x00,0xBF,0x02,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xBC,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xC0,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xC1,0x02,0x00,0x00,0x97,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xBA,0x02,0x00,0x00,0xC1,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xBC,0x02,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xBC,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xC2,0x02,0x00,0x00,0xBA,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x97,0x02,0x00,0x00, + 0xC2,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0xC3,0x02,0x00,0x00, + 0x6D,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0xC6,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFA,0x00,0x04,0x00,0xC3,0x02,0x00,0x00,0xC5,0x02,0x00,0x00,0xCC,0x02,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xC5,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xC8,0x02,0x00,0x00,0xC7,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0xC9,0x02,0x00,0x00,0xAF,0x02,0x00,0x00,0x65,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xCA,0x02,0x00,0x00,0xC9,0x02,0x00,0x00,0x8E,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0xCB,0x02,0x00,0x00,0xC8,0x02,0x00,0x00,0xCA,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xC4,0x02,0x00,0x00,0xCB,0x02,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xC6,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xCC,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xCD,0x02,0x00,0x00,0x97,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xC4,0x02,0x00,0x00,0xCD,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xC6,0x02,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xC6,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xCE,0x02,0x00,0x00,0xC4,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x97,0x02,0x00,0x00, + 0xCE,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0xCF,0x02,0x00,0x00, + 0x82,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xD0,0x02,0x00,0x00, + 0xC7,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xD1,0x02,0x00,0x00, + 0x97,0x02,0x00,0x00,0x50,0x00,0x07,0x00,0xE5,0x00,0x00,0x00,0xD2,0x02,0x00,0x00, + 0xCF,0x02,0x00,0x00,0xCF,0x02,0x00,0x00,0xCF,0x02,0x00,0x00,0xCF,0x02,0x00,0x00, + 0xA9,0x00,0x06,0x00,0x07,0x00,0x00,0x00,0xD3,0x02,0x00,0x00,0xD2,0x02,0x00,0x00, + 0xD0,0x02,0x00,0x00,0xD1,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x97,0x02,0x00,0x00, + 0xD3,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xD4,0x02,0x00,0x00,0x98,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0xD5,0x02,0x00,0x00,0x74,0x02,0x00,0x00, + 0xF7,0x00,0x03,0x00,0xD7,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0xD5,0x02,0x00,0x00,0xD6,0x02,0x00,0x00,0xE6,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xD6,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0xDB,0x02,0x00,0x00, + 0x48,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xDA,0x02,0x00,0x00,0xDB,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xDD,0x02,0x00,0x00,0xD8,0x02,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xDE,0x02,0x00,0x00,0xDD,0x02,0x00,0x00, + 0xDD,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xDC,0x02,0x00,0x00,0xDE,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xE0,0x02,0x00,0x00,0xD8,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0xE1,0x02,0x00,0x00,0xE0,0x02,0x00,0x00,0xE0,0x02,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xDF,0x02,0x00,0x00,0xE1,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xE3,0x02,0x00,0x00,0xD9,0x02,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xE4,0x02,0x00,0x00,0xE3,0x02,0x00,0x00, + 0xE3,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xE2,0x02,0x00,0x00,0xE4,0x02,0x00,0x00,0x39,0x00,0x08,0x00,0x06,0x00,0x00,0x00, + 0xE5,0x02,0x00,0x00,0x3D,0x00,0x00,0x00,0xDA,0x02,0x00,0x00,0xDC,0x02,0x00,0x00, + 0xDF,0x02,0x00,0x00,0xE2,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xD4,0x02,0x00,0x00, + 0xE5,0x02,0x00,0x00,0xF9,0x00,0x02,0x00,0xD7,0x02,0x00,0x00,0xF8,0x00,0x02,0x00, + 0xE6,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0xE7,0x02,0x00,0x00, + 0x7B,0x02,0x00,0x00,0xF7,0x00,0x03,0x00,0xE9,0x02,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFA,0x00,0x04,0x00,0xE7,0x02,0x00,0x00,0xE8,0x02,0x00,0x00,0xFE,0x02,0x00,0x00, + 0xF8,0x00,0x02,0x00,0xE8,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0xEB,0x02,0x00,0x00,0x48,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0xEA,0x02,0x00,0x00, + 0xEB,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xED,0x02,0x00,0x00, + 0xD8,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xEE,0x02,0x00,0x00, + 0xED,0x02,0x00,0x00,0xED,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xEC,0x02,0x00,0x00,0xEE,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0xF0,0x02,0x00,0x00,0xD8,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0xF1,0x02,0x00,0x00,0xF0,0x02,0x00,0x00,0xF0,0x02,0x00,0x00, + 0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xEF,0x02,0x00,0x00, + 0xF1,0x02,0x00,0x00,0x39,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0xF2,0x02,0x00,0x00, + 0x43,0x00,0x00,0x00,0xEA,0x02,0x00,0x00,0xEC,0x02,0x00,0x00,0xEF,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xD4,0x02,0x00,0x00,0xF2,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0xF3,0x02,0x00,0x00,0xD4,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0xF5,0x02,0x00,0x00,0x48,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xF4,0x02,0x00,0x00,0xF5,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0xF7,0x02,0x00,0x00,0xD8,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0xF8,0x02,0x00,0x00,0xF7,0x02,0x00,0x00,0xF7,0x02,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0xF6,0x02,0x00,0x00,0xF8,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0xFA,0x02,0x00,0x00,0xD9,0x02,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0xFB,0x02,0x00,0x00,0xFA,0x02,0x00,0x00, + 0xFA,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0xF9,0x02,0x00,0x00,0xFB,0x02,0x00,0x00,0x39,0x00,0x07,0x00,0x06,0x00,0x00,0x00, + 0xFC,0x02,0x00,0x00,0x43,0x00,0x00,0x00,0xF4,0x02,0x00,0x00,0xF6,0x02,0x00,0x00, + 0xF9,0x02,0x00,0x00,0x0C,0x00,0x07,0x00,0x06,0x00,0x00,0x00,0xFD,0x02,0x00,0x00, + 0x01,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0xF3,0x02,0x00,0x00,0xFC,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0xD4,0x02,0x00,0x00,0xFD,0x02,0x00,0x00,0xF9,0x00,0x02,0x00, + 0xE9,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xFE,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x99,0x00,0x00,0x00,0xFF,0x02,0x00,0x00,0x89,0x02,0x00,0x00,0xF7,0x00,0x03,0x00, + 0x01,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0xFF,0x02,0x00,0x00, + 0x00,0x03,0x00,0x00,0x0E,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x00,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x03,0x03,0x00,0x00,0x48,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x02,0x03,0x00,0x00,0x03,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x05,0x03,0x00,0x00,0xD8,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x06,0x03,0x00,0x00,0x05,0x03,0x00,0x00,0x05,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x04,0x03,0x00,0x00, + 0x06,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x08,0x03,0x00,0x00, + 0xD8,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x09,0x03,0x00,0x00, + 0x08,0x03,0x00,0x00,0x08,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x07,0x03,0x00,0x00,0x09,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x0B,0x03,0x00,0x00,0xD9,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x0C,0x03,0x00,0x00,0x0B,0x03,0x00,0x00,0x0B,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x0A,0x03,0x00,0x00, + 0x0C,0x03,0x00,0x00,0x39,0x00,0x08,0x00,0x06,0x00,0x00,0x00,0x0D,0x03,0x00,0x00, + 0x49,0x00,0x00,0x00,0x02,0x03,0x00,0x00,0x04,0x03,0x00,0x00,0x07,0x03,0x00,0x00, + 0x0A,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0xD4,0x02,0x00,0x00,0x0D,0x03,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x01,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x0E,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x0F,0x03,0x00,0x00,0x90,0x02,0x00,0x00, + 0xF7,0x00,0x03,0x00,0x11,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, + 0x0F,0x03,0x00,0x00,0x10,0x03,0x00,0x00,0x11,0x03,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x10,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x14,0x03,0x00,0x00, + 0xD8,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x15,0x03,0x00,0x00, + 0x14,0x03,0x00,0x00,0x14,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x47,0x02,0x00,0x00,0x16,0x03,0x00,0x00,0x13,0x03,0x00,0x00, + 0xDC,0x01,0x00,0x00,0x3E,0x00,0x03,0x00,0x16,0x03,0x00,0x00,0x15,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x17,0x03,0x00,0x00,0xD8,0x02,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x18,0x03,0x00,0x00,0x17,0x03,0x00,0x00, + 0x17,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x47,0x02,0x00,0x00,0x19,0x03,0x00,0x00,0x13,0x03,0x00,0x00,0xE9,0x01,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x19,0x03,0x00,0x00,0x18,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x1A,0x03,0x00,0x00,0xD9,0x02,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x1B,0x03,0x00,0x00,0x1A,0x03,0x00,0x00,0x1A,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x47,0x02,0x00,0x00, + 0x1C,0x03,0x00,0x00,0x13,0x03,0x00,0x00,0x9D,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x1C,0x03,0x00,0x00,0x1B,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x1E,0x03,0x00,0x00,0xD9,0x02,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x1F,0x03,0x00,0x00,0x1E,0x03,0x00,0x00,0x1E,0x03,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x47,0x02,0x00,0x00,0x20,0x03,0x00,0x00, + 0x13,0x03,0x00,0x00,0x1D,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x20,0x03,0x00,0x00, + 0x1F,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x23,0x03,0x00,0x00, + 0x22,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x24,0x03,0x00,0x00, + 0x23,0x03,0x00,0x00,0x23,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x47,0x02,0x00,0x00,0x25,0x03,0x00,0x00,0x13,0x03,0x00,0x00, + 0x21,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x25,0x03,0x00,0x00,0x24,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x27,0x03,0x00,0x00,0x22,0x03,0x00,0x00, + 0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x28,0x03,0x00,0x00,0x27,0x03,0x00,0x00, + 0x27,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x47,0x02,0x00,0x00,0x29,0x03,0x00,0x00,0x13,0x03,0x00,0x00,0x26,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x29,0x03,0x00,0x00,0x28,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x2C,0x03,0x00,0x00,0x2B,0x03,0x00,0x00,0x4F,0x00,0x07,0x00, + 0x10,0x00,0x00,0x00,0x2D,0x03,0x00,0x00,0x2C,0x03,0x00,0x00,0x2C,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x47,0x02,0x00,0x00, + 0x2E,0x03,0x00,0x00,0x13,0x03,0x00,0x00,0x2A,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x2E,0x03,0x00,0x00,0x2D,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x30,0x03,0x00,0x00,0x2B,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x31,0x03,0x00,0x00,0x30,0x03,0x00,0x00,0x30,0x03,0x00,0x00,0x02,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x47,0x02,0x00,0x00,0x32,0x03,0x00,0x00, + 0x13,0x03,0x00,0x00,0x2F,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x32,0x03,0x00,0x00, + 0x31,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x36,0x03,0x00,0x00, + 0x48,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x35,0x03,0x00,0x00,0x36,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x4D,0x00,0x00,0x00,0x38,0x03,0x00,0x00,0x13,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x37,0x03,0x00,0x00,0x38,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x4F,0x00,0x00,0x00,0x3A,0x03,0x00,0x00,0x34,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x39,0x03,0x00,0x00,0x3A,0x03,0x00,0x00,0x39,0x00,0x07,0x00,0x06,0x00,0x00,0x00, + 0x3B,0x03,0x00,0x00,0x55,0x00,0x00,0x00,0x35,0x03,0x00,0x00,0x37,0x03,0x00,0x00, + 0x39,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0xD4,0x02,0x00,0x00,0x3B,0x03,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x11,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x11,0x03,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x01,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x01,0x03,0x00,0x00, + 0xF9,0x00,0x02,0x00,0xE9,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xE9,0x02,0x00,0x00, + 0xF9,0x00,0x02,0x00,0xD7,0x02,0x00,0x00,0xF8,0x00,0x02,0x00,0xD7,0x02,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x3C,0x03,0x00,0x00,0x67,0x02,0x00,0x00, + 0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x3D,0x03,0x00,0x00,0x3C,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x3E,0x03,0x00,0x00,0x6D,0x02,0x00,0x00, + 0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x3F,0x03,0x00,0x00,0x3E,0x03,0x00,0x00, + 0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x40,0x03,0x00,0x00,0x3D,0x03,0x00,0x00, + 0x3F,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x41,0x03,0x00,0x00, + 0x82,0x02,0x00,0x00,0xA8,0x00,0x04,0x00,0x99,0x00,0x00,0x00,0x42,0x03,0x00,0x00, + 0x41,0x03,0x00,0x00,0xA7,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x43,0x03,0x00,0x00, + 0x40,0x03,0x00,0x00,0x42,0x03,0x00,0x00,0xF7,0x00,0x03,0x00,0x46,0x03,0x00,0x00, + 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x43,0x03,0x00,0x00,0x45,0x03,0x00,0x00, + 0x50,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x45,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x47,0x03,0x00,0x00,0xD4,0x02,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x48,0x03,0x00,0x00,0x50,0x02,0x00,0x00,0x83,0x00,0x05,0x00, + 0x06,0x00,0x00,0x00,0x49,0x03,0x00,0x00,0x47,0x03,0x00,0x00,0x48,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x4B,0x03,0x00,0x00,0x97,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x4A,0x03,0x00,0x00,0x4B,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x4D,0x03,0x00,0x00,0xC7,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x4C,0x03,0x00,0x00,0x4D,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x4E,0x03,0x00,0x00, + 0x49,0x03,0x00,0x00,0x39,0x00,0x07,0x00,0x07,0x00,0x00,0x00,0x4F,0x03,0x00,0x00, + 0x32,0x00,0x00,0x00,0x4A,0x03,0x00,0x00,0x4C,0x03,0x00,0x00,0x4E,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x44,0x03,0x00,0x00,0x4F,0x03,0x00,0x00,0xF9,0x00,0x02,0x00, + 0x46,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x50,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x51,0x03,0x00,0x00,0x97,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x44,0x03,0x00,0x00,0x51,0x03,0x00,0x00,0xF9,0x00,0x02,0x00,0x46,0x03,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x46,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x52,0x03,0x00,0x00,0x44,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x97,0x02,0x00,0x00, + 0x52,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x53,0x03,0x00,0x00, + 0x5D,0x02,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x54,0x03,0x00,0x00, + 0x97,0x02,0x00,0x00,0x8E,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x55,0x03,0x00,0x00, + 0x54,0x03,0x00,0x00,0x53,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x97,0x02,0x00,0x00, + 0x55,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x57,0x03,0x00,0x00, + 0x48,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x56,0x03,0x00,0x00,0x57,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x59,0x03,0x00,0x00,0x63,0x02,0x00,0x00, + 0x81,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x5B,0x03,0x00,0x00,0x59,0x03,0x00,0x00, + 0x5A,0x03,0x00,0x00,0x8E,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x5C,0x03,0x00,0x00, + 0x5B,0x03,0x00,0x00,0x82,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x10,0x00,0x00,0x00, + 0x5D,0x03,0x00,0x00,0x5C,0x03,0x00,0x00,0x5A,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x58,0x03,0x00,0x00,0x5D,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x5F,0x03,0x00,0x00,0x56,0x03,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00, + 0x60,0x03,0x00,0x00,0x5E,0x03,0x00,0x00,0xDC,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x60,0x03,0x00,0x00,0x5F,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00, + 0x61,0x03,0x00,0x00,0x4D,0x02,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00, + 0x62,0x03,0x00,0x00,0x5E,0x03,0x00,0x00,0xE9,0x01,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x62,0x03,0x00,0x00,0x61,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x64,0x03,0x00,0x00,0x63,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00, + 0x65,0x03,0x00,0x00,0x64,0x03,0x00,0x00,0x64,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x66,0x03,0x00,0x00, + 0x5E,0x03,0x00,0x00,0x9D,0x02,0x00,0x00,0x3E,0x00,0x03,0x00,0x66,0x03,0x00,0x00, + 0x65,0x03,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x67,0x03,0x00,0x00, + 0x63,0x03,0x00,0x00,0x4F,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x68,0x03,0x00,0x00, + 0x67,0x03,0x00,0x00,0x67,0x03,0x00,0x00,0x02,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x69,0x03,0x00,0x00,0x5E,0x03,0x00,0x00, + 0x1D,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x69,0x03,0x00,0x00,0x68,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x6A,0x03,0x00,0x00,0x58,0x03,0x00,0x00, + 0x41,0x00,0x05,0x00,0x11,0x00,0x00,0x00,0x6B,0x03,0x00,0x00,0x5E,0x03,0x00,0x00, + 0x21,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x6B,0x03,0x00,0x00,0x6A,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x6D,0x03,0x00,0x00,0x6C,0x03,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x6E,0x03,0x00,0x00,0x5E,0x03,0x00,0x00, + 0x26,0x03,0x00,0x00,0x3E,0x00,0x03,0x00,0x6E,0x03,0x00,0x00,0x6D,0x03,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x70,0x03,0x00,0x00,0x97,0x02,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x6F,0x03,0x00,0x00,0x70,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x57,0x00,0x00,0x00,0x72,0x03,0x00,0x00,0x5E,0x03,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x71,0x03,0x00,0x00,0x72,0x03,0x00,0x00,0x39,0x00,0x06,0x00,0x07,0x00,0x00,0x00, + 0x73,0x03,0x00,0x00,0x5C,0x00,0x00,0x00,0x6F,0x03,0x00,0x00,0x71,0x03,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x97,0x02,0x00,0x00,0x73,0x03,0x00,0x00,0x41,0x00,0x05,0x00, + 0x9E,0x02,0x00,0x00,0x74,0x03,0x00,0x00,0x9C,0x02,0x00,0x00,0xE9,0x01,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x75,0x03,0x00,0x00,0x74,0x03,0x00,0x00, + 0xAB,0x00,0x05,0x00,0x99,0x00,0x00,0x00,0x76,0x03,0x00,0x00,0x75,0x03,0x00,0x00, + 0xDC,0x01,0x00,0x00,0xF7,0x00,0x03,0x00,0x78,0x03,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFA,0x00,0x04,0x00,0x76,0x03,0x00,0x00,0x77,0x03,0x00,0x00,0x78,0x03,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x77,0x03,0x00,0x00,0x41,0x00,0x05,0x00,0x17,0x00,0x00,0x00, + 0x79,0x03,0x00,0x00,0x97,0x02,0x00,0x00,0x65,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x7A,0x03,0x00,0x00,0x79,0x03,0x00,0x00,0xB4,0x00,0x05,0x00, + 0x99,0x00,0x00,0x00,0x7B,0x03,0x00,0x00,0x7A,0x03,0x00,0x00,0x98,0x00,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x78,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x78,0x03,0x00,0x00, + 0xF5,0x00,0x07,0x00,0x99,0x00,0x00,0x00,0x7C,0x03,0x00,0x00,0x76,0x03,0x00,0x00, + 0x46,0x03,0x00,0x00,0x7B,0x03,0x00,0x00,0x77,0x03,0x00,0x00,0xF7,0x00,0x03,0x00, + 0x7E,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x7C,0x03,0x00,0x00, + 0x7D,0x03,0x00,0x00,0x7E,0x03,0x00,0x00,0xF8,0x00,0x02,0x00,0x7D,0x03,0x00,0x00, + 0xFC,0x00,0x01,0x00,0xF8,0x00,0x02,0x00,0x7E,0x03,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x97,0x02,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x0B,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00, 0x36,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x09,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x0A,0x00,0x00,0x00, 0xF8,0x00,0x02,0x00,0x0C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, @@ -2028,45 +2038,61 @@ static const uint8_t s_draw_fs_bytecode_content[20720] = { 0x1F,0x00,0x00,0x00,0x3F,0x02,0x00,0x00,0x85,0x00,0x05,0x00,0x06,0x00,0x00,0x00, 0x41,0x02,0x00,0x00,0x3E,0x02,0x00,0x00,0x40,0x02,0x00,0x00,0xFE,0x00,0x02,0x00, 0x41,0x02,0x00,0x00,0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x5C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x37,0x00,0x03,0x00, - 0x08,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00, - 0x59,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x5A,0x00,0x00,0x00, - 0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x5B,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, - 0x5D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x44,0x02,0x00,0x00, - 0x58,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0x44,0x02,0x00,0x00,0x38,0x00,0x01,0x00, + 0x5C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x37,0x00,0x03,0x00, + 0x08,0x00,0x00,0x00,0x5A,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x58,0x00,0x00,0x00, + 0x5B,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x5D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x44,0x02,0x00,0x00,0x5A,0x00,0x00,0x00,0xFE,0x00,0x02,0x00, + 0x44,0x02,0x00,0x00,0x38,0x00,0x01,0x00, }; #endif -static const char s_draw_fs_bytecode_glsl300_src[10335] = +static const char s_draw_fs_bytecode_glsl300_src[9812] = "#version 300 es\n" "precision mediump float;\n" "precision highp int;\n" "\n" +"struct ShaderParams\n" +"{\n" +" highp vec2 view_pos;\n" +" highp vec2 uv;\n" +" highp vec2 uv_min;\n" +" highp vec2 uv_max;\n" +" highp vec2 screen_uv;\n" +" highp vec4 attributes;\n" +"};\n" +"\n" "layout(std140) uniform uniform_block\n" "{\n" " highp vec2 u_texture_size;\n" " int u_alpha_discard;\n" -"} _666;\n" +" int u_use_smooth_uv;\n" +"} _668;\n" "\n" "uniform highp sampler2D u_image;\n" "\n" -"in highp float v_stroke;\n" -"in highp float v_aa;\n" "layout(location = 0) out highp vec4 result;\n" -"in highp float v_fill;\n" -"in highp float v_type;\n" -"in highp vec2 v_uv;\n" +"in highp vec4 v_pos_uv;\n" +"in highp vec4 v_shape;\n" +"in highp vec4 v_blend_posH;\n" "in highp vec4 v_col;\n" -"in highp vec2 v_pos;\n" "in highp vec4 v_ab;\n" "in highp vec4 v_cd;\n" "in highp vec4 v_ef;\n" "in highp vec4 v_gh;\n" "flat in int v_n;\n" -"in highp float v_radius;\n" -"in highp float v_alpha;\n" -"in highp vec2 v_posH;\n" +"in highp vec4 v_uv_bounds;\n" "in highp vec4 v_user;\n" +"highp float v_stroke;\n" +"highp float v_aa;\n" +"highp float v_fill;\n" +"highp vec2 v_pos;\n" +"highp vec2 v_uv;\n" +"highp float v_radius;\n" +"highp float v_type;\n" +"highp float v_alpha;\n" +"highp vec2 v_posH;\n" "highp vec2 pts[8];\n" +"highp vec2 pos;\n" +"highp vec2 screen_uv;\n" "\n" "highp vec2 smooth_uv(highp vec2 uv, highp vec2 texture_size)\n" "{\n" @@ -2235,127 +2261,67 @@ static const char s_draw_fs_bytecode_glsl300_src[10335] = " return result;\n" "}\n" "\n" -"highp vec4 shader(highp vec4 color, highp vec2 pos, highp vec2 screen_uv, highp vec4 params)\n" +"highp vec4 shader(highp vec4 color, ShaderParams params)\n" "{\n" " return color;\n" "}\n" "\n" "void main()\n" "{\n" -" bool _587 = v_type >= 0.0;\n" -" bool _593;\n" -" if (_587)\n" -" {\n" -" _593 = v_type < 0.00196078442968428134918212890625;\n" -" }\n" -" else\n" -" {\n" -" _593 = _587;\n" -" }\n" -" bool is_sprite = _593;\n" -" bool _596 = v_type > 0.00196078442968428134918212890625;\n" -" bool _602;\n" -" if (_596)\n" -" {\n" -" _602 = v_type < 0.0058823530562222003936767578125;\n" -" }\n" -" else\n" -" {\n" -" _602 = _596;\n" -" }\n" -" bool is_text = _602;\n" -" bool _605 = v_type > 0.0058823530562222003936767578125;\n" -" bool _611;\n" -" if (_605)\n" -" {\n" -" _611 = v_type < 0.009803921915590763092041015625;\n" -" }\n" -" else\n" -" {\n" -" _611 = _605;\n" -" }\n" -" bool is_box = _611;\n" -" bool _614 = v_type > 0.009803921915590763092041015625;\n" -" bool _620;\n" -" if (_614)\n" -" {\n" -" _620 = v_type < 0.013725490309298038482666015625;\n" -" }\n" -" else\n" -" {\n" -" _620 = _614;\n" -" }\n" -" bool is_seg = _620;\n" -" bool _623 = v_type > 0.013725490309298038482666015625;\n" -" bool _629;\n" -" if (_623)\n" -" {\n" -" _629 = v_type < 0.01764705963432788848876953125;\n" -" }\n" -" else\n" -" {\n" -" _629 = _623;\n" -" }\n" -" bool is_tri = _629;\n" -" bool _632 = v_type > 0.01764705963432788848876953125;\n" -" bool _638;\n" -" if (_632)\n" -" {\n" -" _638 = v_type < 0.02156862802803516387939453125;\n" -" }\n" -" else\n" -" {\n" -" _638 = _632;\n" -" }\n" -" bool is_tri_sdf = _638;\n" -" bool _641 = v_type > 0.02156862802803516387939453125;\n" -" bool _647;\n" -" if (_641)\n" -" {\n" -" _647 = v_type < 0.02549019642174243927001953125;\n" -" }\n" -" else\n" -" {\n" -" _647 = _641;\n" -" }\n" -" bool is_poly = _647;\n" +" v_pos = v_pos_uv.xy;\n" +" v_uv = v_pos_uv.zw;\n" +" v_radius = v_shape.x;\n" +" v_stroke = v_shape.y;\n" +" v_aa = v_shape.z;\n" +" v_type = v_shape.w;\n" +" v_alpha = v_blend_posH.x;\n" +" v_fill = v_blend_posH.y;\n" +" v_posH = v_blend_posH.zw;\n" +" bool is_sprite = (v_type >= 0.0) && (v_type < 0.5);\n" +" bool is_text = (v_type > 0.5) && (v_type < 1.5);\n" +" bool is_box = (v_type > 1.5) && (v_type < 2.5);\n" +" bool is_seg = (v_type > 2.5) && (v_type < 3.5);\n" +" bool is_tri = (v_type > 3.5) && (v_type < 4.5);\n" +" bool is_tri_sdf = (v_type > 4.5) && (v_type < 5.5);\n" +" bool is_poly = (v_type > 5.5) && (v_type < 6.5);\n" " highp vec4 c = vec4(0.0);\n" -" highp vec4 _654;\n" -" if (!(is_sprite && is_text))\n" +" highp vec2 _674;\n" +" if (_668.u_use_smooth_uv == 0)\n" " {\n" " highp vec2 param = v_uv;\n" -" highp vec2 param_1 = _666.u_texture_size;\n" -" highp vec4 param_2 = texture(u_image, smooth_uv(param, param_1));\n" -" _654 = de_gamma(param_2);\n" +" highp vec2 param_1 = _668.u_texture_size;\n" +" _674 = smooth_uv(param, param_1);\n" " }\n" " else\n" " {\n" -" _654 = c;\n" +" _674 = v_uv;\n" " }\n" -" c = _654;\n" -" highp vec4 _681;\n" +" highp vec2 uv = _674;\n" +" highp vec4 param_2 = texture(u_image, uv);\n" +" highp vec4 tex_c = de_gamma(param_2);\n" +" highp vec4 _698;\n" " if (is_sprite)\n" " {\n" -" highp vec4 param_3 = c;\n" -" _681 = gamma(param_3);\n" +" highp vec4 param_3 = tex_c;\n" +" _698 = gamma(param_3);\n" " }\n" " else\n" " {\n" -" _681 = c;\n" +" _698 = c;\n" " }\n" -" c = _681;\n" -" highp vec4 _691;\n" +" c = _698;\n" +" highp vec4 _708;\n" " if (is_text)\n" " {\n" -" _691 = v_col * c.w;\n" +" _708 = v_col * tex_c.w;\n" " }\n" " else\n" " {\n" -" _691 = c;\n" +" _708 = c;\n" " }\n" -" c = _691;\n" -" bvec4 _706 = bvec4(is_tri);\n" -" c = vec4(_706.x ? v_col.x : c.x, _706.y ? v_col.y : c.y, _706.z ? v_col.z : c.z, _706.w ? v_col.w : c.w);\n" +" c = _708;\n" +" bvec4 _722 = bvec4(is_tri);\n" +" c = vec4(_722.x ? v_col.x : c.x, _722.y ? v_col.y : c.y, _722.z ? v_col.z : c.z, _722.w ? v_col.w : c.w);\n" " highp float d = 0.0;\n" " if (is_box)\n" " {\n" @@ -2363,8 +2329,8 @@ static const char s_draw_fs_bytecode_glsl300_src[10335] = " highp vec2 param_5 = v_ab.xy;\n" " highp vec2 param_6 = v_ab.zw;\n" " highp vec2 param_7 = v_cd.xy;\n" -" highp float _726 = distance_box(param_4, param_5, param_6, param_7);\n" -" d = _726;\n" +" highp float _741 = distance_box(param_4, param_5, param_6, param_7);\n" +" d = _741;\n" " }\n" " else\n" " {\n" @@ -2409,38 +2375,44 @@ static const char s_draw_fs_bytecode_glsl300_src[10335] = " }\n" " }\n" " }\n" -" highp vec4 _823;\n" +" highp vec4 _836;\n" " if (((!is_sprite) && (!is_text)) && (!is_tri))\n" " {\n" " highp vec4 param_21 = c;\n" " highp vec4 param_22 = v_col;\n" " highp float param_23 = d - v_radius;\n" -" highp vec4 _835 = sdf(param_21, param_22, param_23);\n" -" _823 = _835;\n" +" highp vec4 _847 = sdf(param_21, param_22, param_23);\n" +" _836 = _847;\n" " }\n" " else\n" " {\n" -" _823 = c;\n" +" _836 = c;\n" " }\n" -" c = _823;\n" +" c = _836;\n" " c *= v_alpha;\n" -" highp vec2 screen_uv = ((v_posH + vec2(1.0, -1.0)) * 0.5) * vec2(1.0, -1.0);\n" +" pos = v_pos;\n" +" screen_uv = ((v_posH + vec2(1.0, -1.0)) * 0.5) * vec2(1.0, -1.0);\n" +" ShaderParams sp;\n" +" sp.view_pos = pos;\n" +" sp.uv = v_uv;\n" +" sp.uv_min = v_uv_bounds.xy;\n" +" sp.uv_max = v_uv_bounds.zw;\n" +" sp.screen_uv = screen_uv;\n" +" sp.attributes = v_user;\n" " highp vec4 param_24 = c;\n" -" highp vec2 param_25 = v_pos;\n" -" highp vec2 param_26 = screen_uv;\n" -" highp vec4 param_27 = v_user;\n" -" c = shader(param_24, param_25, param_26, param_27);\n" -" bool _863 = _666.u_alpha_discard != 0;\n" -" bool _869;\n" -" if (_863)\n" +" ShaderParams param_25 = sp;\n" +" c = shader(param_24, param_25);\n" +" bool _886 = _668.u_alpha_discard != 0;\n" +" bool _892;\n" +" if (_886)\n" " {\n" -" _869 = c.w == 0.0;\n" +" _892 = c.w == 0.0;\n" " }\n" " else\n" " {\n" -" _869 = _863;\n" +" _892 = _886;\n" " }\n" -" if (_869)\n" +" if (_892)\n" " {\n" " discard;\n" " }\n" @@ -2451,15 +2423,16 @@ static const char s_draw_fs_bytecode_glsl300_src[10335] = static const char* s_draw_fs_bytecode_image_names[1] = { "u_image", }; +static int s_draw_fs_bytecode_image_binding_slots[1] = { 0, }; static CF_ShaderUniformInfo s_draw_fs_bytecode_uniforms[1] = { { .block_name = "uniform_block", .block_index = 0, .block_size = 16, - .num_members = 2, + .num_members = 3, }, }; -static CF_ShaderUniformMemberInfo s_draw_fs_bytecode_uniform_members[2] = { +static CF_ShaderUniformMemberInfo s_draw_fs_bytecode_uniform_members[3] = { { .name = "u_texture_size", .type = CF_SHADER_INFO_TYPE_FLOAT2, @@ -2472,24 +2445,33 @@ static CF_ShaderUniformMemberInfo s_draw_fs_bytecode_uniform_members[2] = { .offset = 8, .array_length = 1, }, + { + .name = "u_use_smooth_uv", + .type = CF_SHADER_INFO_TYPE_SINT, + .offset = 12, + .array_length = 1, + }, }; #define s_draw_fs_bytecode_inputs NULL static const CF_ShaderBytecode s_draw_fs_bytecode = { #ifndef __EMSCRIPTEN__ .content = s_draw_fs_bytecode_content, - .size = 20720, + .size = 21400, #endif .glsl300_src = s_draw_fs_bytecode_glsl300_src, - .glsl300_src_size = 10334, + .glsl300_src_size = 9811, .shader_info = { .num_samplers = 1, .num_storage_textures = 0, .num_storage_buffers = 0, + .num_readwrite_storage_textures = 0, + .num_readwrite_storage_buffers = 0, .num_images = 1, .image_names = s_draw_fs_bytecode_image_names, + .image_binding_slots = s_draw_fs_bytecode_image_binding_slots, .num_uniforms = 1, .uniforms = s_draw_fs_bytecode_uniforms, - .num_uniform_members = 2, + .num_uniform_members = 3, .uniform_members = s_draw_fs_bytecode_uniform_members, .num_inputs = 0, .inputs = s_draw_fs_bytecode_inputs, @@ -2582,6 +2564,7 @@ static const char s_basic_vs_bytecode_glsl300_src[210] = "\n" ""; #define s_basic_vs_bytecode_image_names NULL +#define s_basic_vs_bytecode_image_binding_slots NULL #define s_basic_vs_bytecode_uniforms NULL #define s_basic_vs_bytecode_uniform_members NULL static CF_ShaderInputInfo s_basic_vs_bytecode_inputs[1] = { @@ -2602,8 +2585,11 @@ static const CF_ShaderBytecode s_basic_vs_bytecode = { .num_samplers = 0, .num_storage_textures = 0, .num_storage_buffers = 0, + .num_readwrite_storage_textures = 0, + .num_readwrite_storage_buffers = 0, .num_images = 0, .image_names = s_basic_vs_bytecode_image_names, + .image_binding_slots = s_basic_vs_bytecode_image_binding_slots, .num_uniforms = 0, .uniforms = s_basic_vs_bytecode_uniforms, .num_uniform_members = 0, @@ -2669,6 +2655,7 @@ static const char s_basic_fs_bytecode_glsl300_src[150] = "\n" ""; #define s_basic_fs_bytecode_image_names NULL +#define s_basic_fs_bytecode_image_binding_slots NULL #define s_basic_fs_bytecode_uniforms NULL #define s_basic_fs_bytecode_uniform_members NULL #define s_basic_fs_bytecode_inputs NULL @@ -2683,8 +2670,11 @@ static const CF_ShaderBytecode s_basic_fs_bytecode = { .num_samplers = 0, .num_storage_textures = 0, .num_storage_buffers = 0, + .num_readwrite_storage_textures = 0, + .num_readwrite_storage_buffers = 0, .num_images = 0, .image_names = s_basic_fs_bytecode_image_names, + .image_binding_slots = s_basic_fs_bytecode_image_binding_slots, .num_uniforms = 0, .uniforms = s_basic_fs_bytecode_uniforms, .num_uniform_members = 0, @@ -2796,6 +2786,7 @@ static const char s_backbuffer_vs_bytecode_glsl300_src[275] = "\n" ""; #define s_backbuffer_vs_bytecode_image_names NULL +#define s_backbuffer_vs_bytecode_image_binding_slots NULL #define s_backbuffer_vs_bytecode_uniforms NULL #define s_backbuffer_vs_bytecode_uniform_members NULL static CF_ShaderInputInfo s_backbuffer_vs_bytecode_inputs[2] = { @@ -2821,8 +2812,11 @@ static const CF_ShaderBytecode s_backbuffer_vs_bytecode = { .num_samplers = 0, .num_storage_textures = 0, .num_storage_buffers = 0, + .num_readwrite_storage_textures = 0, + .num_readwrite_storage_buffers = 0, .num_images = 0, .image_names = s_backbuffer_vs_bytecode_image_names, + .image_binding_slots = s_backbuffer_vs_bytecode_image_binding_slots, .num_uniforms = 0, .uniforms = s_backbuffer_vs_bytecode_uniforms, .num_uniform_members = 0, @@ -3013,6 +3007,7 @@ static const char s_backbuffer_fs_bytecode_glsl300_src[690] = static const char* s_backbuffer_fs_bytecode_image_names[1] = { "u_image", }; +static int s_backbuffer_fs_bytecode_image_binding_slots[1] = { 0, }; static CF_ShaderUniformInfo s_backbuffer_fs_bytecode_uniforms[1] = { { .block_name = "uniform_block", @@ -3041,8 +3036,11 @@ static const CF_ShaderBytecode s_backbuffer_fs_bytecode = { .num_samplers = 1, .num_storage_textures = 0, .num_storage_buffers = 0, + .num_readwrite_storage_textures = 0, + .num_readwrite_storage_buffers = 0, .num_images = 1, .image_names = s_backbuffer_fs_bytecode_image_names, + .image_binding_slots = s_backbuffer_fs_bytecode_image_binding_slots, .num_uniforms = 1, .uniforms = s_backbuffer_fs_bytecode_uniforms, .num_uniform_members = 1, @@ -3193,6 +3191,7 @@ static const char s_blit_vs_bytecode_glsl300_src[476] = "\n" ""; #define s_blit_vs_bytecode_image_names NULL +#define s_blit_vs_bytecode_image_binding_slots NULL #define s_blit_vs_bytecode_uniforms NULL #define s_blit_vs_bytecode_uniform_members NULL static CF_ShaderInputInfo s_blit_vs_bytecode_inputs[4] = { @@ -3228,8 +3227,11 @@ static const CF_ShaderBytecode s_blit_vs_bytecode = { .num_samplers = 0, .num_storage_textures = 0, .num_storage_buffers = 0, + .num_readwrite_storage_textures = 0, + .num_readwrite_storage_buffers = 0, .num_images = 0, .image_names = s_blit_vs_bytecode_image_names, + .image_binding_slots = s_blit_vs_bytecode_image_binding_slots, .num_uniforms = 0, .uniforms = s_blit_vs_bytecode_uniforms, .num_uniform_members = 0, @@ -3267,32 +3269,54 @@ vec2 smooth_uv(vec2 uv, vec2 texture_size) return pixel / texture_size; } #line 17 0 + +vec2 pos; +vec2 screen_uv; + +struct ShaderParams +{ + vec2 view_pos; + vec2 uv; + vec2 uv_min; + vec2 uv_max; + vec2 screen_uv; + vec4 attributes; +}; + #line 1 "shader_stub.shd" -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { return color; } -#line 18 0 +#line 32 0 void main() { vec4 color = texture(u_image, smooth_uv(v_uv, u_texture_size)); - vec2 screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1); - vec4 c = shader(color, v_pos, screen_uv, v_params); + pos = v_pos; + screen_uv = (v_posH + vec2(1, - 1)) * 0.5 * vec2(1, - 1); + ShaderParams sp; + sp.view_pos = pos; + sp.uv = v_uv; + sp.uv_min = vec2(0); + sp.uv_max = vec2(0); + sp.screen_uv = screen_uv; + sp.attributes = v_params; + vec4 c = shader(color, sp); if (u_alpha_discard != 0 && c.a == 0) discard; result = c; } */ #ifndef __EMSCRIPTEN__ -static const uint8_t s_blit_fs_bytecode_content[3052] = { - 0x03,0x02,0x23,0x07,0x00,0x03,0x01,0x00,0x0B,0x00,0x08,0x00,0x76,0x00,0x00,0x00, +static const uint8_t s_blit_fs_bytecode_content[3536] = { + 0x03,0x02,0x23,0x07,0x00,0x03,0x01,0x00,0x0B,0x00,0x08,0x00,0x85,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0B,0x00,0x06,0x00, 0x01,0x00,0x00,0x00,0x47,0x4C,0x53,0x4C,0x2E,0x73,0x74,0x64,0x2E,0x34,0x35,0x30, 0x00,0x00,0x00,0x00,0x0E,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, 0x0F,0x00,0x0A,0x00,0x04,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x6D,0x61,0x69,0x6E, - 0x00,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x4B,0x00,0x00,0x00,0x54,0x00,0x00,0x00, - 0x56,0x00,0x00,0x00,0x74,0x00,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x4F,0x00,0x00,0x00, + 0x68,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0x10,0x00,0x03,0x00,0x04,0x00,0x00,0x00, 0x07,0x00,0x00,0x00,0x03,0x00,0x03,0x00,0x02,0x00,0x00,0x00,0xC2,0x01,0x00,0x00, 0x04,0x00,0x09,0x00,0x47,0x4C,0x5F,0x41,0x52,0x42,0x5F,0x73,0x68,0x61,0x64,0x69, 0x6E,0x67,0x5F,0x6C,0x61,0x6E,0x67,0x75,0x61,0x67,0x65,0x5F,0x69,0x6E,0x63,0x6C, @@ -3303,99 +3327,117 @@ static const uint8_t s_blit_fs_bytecode_content[3052] = { 0x0C,0x00,0x00,0x00,0x73,0x6D,0x6F,0x6F,0x74,0x68,0x5F,0x75,0x76,0x28,0x76,0x66, 0x32,0x3B,0x76,0x66,0x32,0x3B,0x00,0x00,0x05,0x00,0x03,0x00,0x0A,0x00,0x00,0x00, 0x75,0x76,0x00,0x00,0x05,0x00,0x06,0x00,0x0B,0x00,0x00,0x00,0x74,0x65,0x78,0x74, - 0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00,0x00,0x00,0x05,0x00,0x08,0x00, - 0x15,0x00,0x00,0x00,0x73,0x68,0x61,0x64,0x65,0x72,0x28,0x76,0x66,0x34,0x3B,0x76, - 0x66,0x32,0x3B,0x76,0x66,0x32,0x3B,0x76,0x66,0x34,0x3B,0x00,0x05,0x00,0x04,0x00, - 0x11,0x00,0x00,0x00,0x63,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00,0x05,0x00,0x03,0x00, - 0x12,0x00,0x00,0x00,0x70,0x6F,0x73,0x00,0x05,0x00,0x05,0x00,0x13,0x00,0x00,0x00, - 0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75,0x76,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x14,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x73,0x00,0x00,0x05,0x00,0x04,0x00, - 0x17,0x00,0x00,0x00,0x70,0x69,0x78,0x65,0x6C,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x1B,0x00,0x00,0x00,0x73,0x65,0x61,0x6D,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x35,0x00,0x00,0x00,0x63,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x39,0x00,0x00,0x00,0x75,0x5F,0x69,0x6D,0x61,0x67,0x65,0x00,0x05,0x00,0x04,0x00, - 0x3C,0x00,0x00,0x00,0x76,0x5F,0x75,0x76,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, - 0x3E,0x00,0x00,0x00,0x75,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x5F,0x62,0x6C,0x6F,0x63, - 0x6B,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x75,0x5F,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00, - 0x06,0x00,0x07,0x00,0x3E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x75,0x5F,0x61,0x6C, - 0x70,0x68,0x61,0x5F,0x64,0x69,0x73,0x63,0x61,0x72,0x64,0x00,0x05,0x00,0x03,0x00, - 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x42,0x00,0x00,0x00, - 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x44,0x00,0x00,0x00, - 0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x4A,0x00,0x00,0x00, - 0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75,0x76,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x4B,0x00,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x48,0x00,0x00,0x05,0x00,0x03,0x00, - 0x53,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x54,0x00,0x00,0x00, - 0x76,0x5F,0x70,0x6F,0x73,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x56,0x00,0x00,0x00, - 0x76,0x5F,0x70,0x61,0x72,0x61,0x6D,0x73,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x57,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x59,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x5B,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x5D,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00, - 0x74,0x00,0x00,0x00,0x72,0x65,0x73,0x75,0x6C,0x74,0x00,0x00,0x47,0x00,0x04,0x00, - 0x39,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x39,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00, - 0x3C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x00,0x03,0x00, - 0x3E,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x3E,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, - 0x3E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x40,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x40,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x4B,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x54,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x56,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x03,0x00,0x00,0x00, - 0x47,0x00,0x04,0x00,0x74,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x03,0x00,0x00,0x00, - 0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x00,0x00, - 0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x21,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x08,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x06,0x00,0x00,0x00, - 0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0F,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x0E,0x00,0x00,0x00,0x21,0x00,0x07,0x00,0x10,0x00,0x00,0x00,0x0E,0x00,0x00,0x00, - 0x0F,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, - 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x00,0x00,0x00,0x3F, - 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0xBF, - 0x19,0x00,0x09,0x00,0x36,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x1B,0x00,0x03,0x00,0x37,0x00,0x00,0x00,0x36,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x37,0x00,0x00,0x00, - 0x3B,0x00,0x04,0x00,0x38,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x3B,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x3B,0x00,0x04,0x00,0x3B,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x15,0x00,0x04,0x00,0x3D,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x1E,0x00,0x04,0x00,0x3E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x3F,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3E,0x00,0x00,0x00, - 0x3B,0x00,0x04,0x00,0x3F,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00, - 0x2B,0x00,0x04,0x00,0x3D,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x20,0x00,0x04,0x00,0x45,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00, - 0x3B,0x00,0x04,0x00,0x3B,0x00,0x00,0x00,0x4B,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x4D,0x00,0x00,0x00,0x00,0x00,0x80,0x3F, - 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x4E,0x00,0x00,0x00,0x00,0x00,0x80,0xBF, - 0x2C,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x4D,0x00,0x00,0x00, - 0x4E,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x3B,0x00,0x00,0x00,0x54,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x55,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x0E,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x55,0x00,0x00,0x00,0x56,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x14,0x00,0x02,0x00,0x60,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, - 0x3D,0x00,0x00,0x00,0x61,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x62,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3D,0x00,0x00,0x00,0x15,0x00,0x04,0x00, - 0x68,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, - 0x68,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x6A,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, - 0x06,0x00,0x00,0x00,0x6D,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00, - 0x73,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, - 0x73,0x00,0x00,0x00,0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x10,0x00,0x00,0x00,0x53,0x68,0x61,0x64,0x65,0x72,0x50,0x61,0x72,0x61,0x6D,0x73, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x76,0x69,0x65,0x77,0x5F,0x70,0x6F,0x73,0x00,0x00,0x00,0x00,0x06,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x06,0x00,0x05,0x00, + 0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x75,0x76,0x5F,0x6D,0x69,0x6E,0x00,0x00, + 0x06,0x00,0x05,0x00,0x10,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x75,0x76,0x5F,0x6D, + 0x61,0x78,0x00,0x00,0x06,0x00,0x06,0x00,0x10,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75,0x76,0x00,0x00,0x00,0x06,0x00,0x06,0x00, + 0x10,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74, + 0x65,0x73,0x00,0x00,0x05,0x00,0x11,0x00,0x15,0x00,0x00,0x00,0x73,0x68,0x61,0x64, + 0x65,0x72,0x28,0x76,0x66,0x34,0x3B,0x73,0x74,0x72,0x75,0x63,0x74,0x2D,0x53,0x68, + 0x61,0x64,0x65,0x72,0x50,0x61,0x72,0x61,0x6D,0x73,0x2D,0x76,0x66,0x32,0x2D,0x76, + 0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76,0x66,0x32,0x2D,0x76, + 0x66,0x34,0x31,0x3B,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x13,0x00,0x00,0x00, + 0x63,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x14,0x00,0x00,0x00, + 0x70,0x61,0x72,0x61,0x6D,0x73,0x00,0x00,0x05,0x00,0x04,0x00,0x17,0x00,0x00,0x00, + 0x70,0x69,0x78,0x65,0x6C,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x1B,0x00,0x00,0x00, + 0x73,0x65,0x61,0x6D,0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x35,0x00,0x00,0x00, + 0x63,0x6F,0x6C,0x6F,0x72,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x39,0x00,0x00,0x00, + 0x75,0x5F,0x69,0x6D,0x61,0x67,0x65,0x00,0x05,0x00,0x04,0x00,0x3C,0x00,0x00,0x00, + 0x76,0x5F,0x75,0x76,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00,0x3E,0x00,0x00,0x00, + 0x75,0x6E,0x69,0x66,0x6F,0x72,0x6D,0x5F,0x62,0x6C,0x6F,0x63,0x6B,0x00,0x00,0x00, + 0x06,0x00,0x07,0x00,0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x75,0x5F,0x74,0x65, + 0x78,0x74,0x75,0x72,0x65,0x5F,0x73,0x69,0x7A,0x65,0x00,0x00,0x06,0x00,0x07,0x00, + 0x3E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x75,0x5F,0x61,0x6C,0x70,0x68,0x61,0x5F, + 0x64,0x69,0x73,0x63,0x61,0x72,0x64,0x00,0x05,0x00,0x03,0x00,0x40,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x42,0x00,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x44,0x00,0x00,0x00,0x70,0x61,0x72,0x61, + 0x6D,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x4B,0x00,0x00,0x00,0x70,0x6F,0x73,0x00, + 0x05,0x00,0x04,0x00,0x4C,0x00,0x00,0x00,0x76,0x5F,0x70,0x6F,0x73,0x00,0x00,0x00, + 0x05,0x00,0x05,0x00,0x4E,0x00,0x00,0x00,0x73,0x63,0x72,0x65,0x65,0x6E,0x5F,0x75, + 0x76,0x00,0x00,0x00,0x05,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x76,0x5F,0x70,0x6F, + 0x73,0x48,0x00,0x00,0x05,0x00,0x03,0x00,0x57,0x00,0x00,0x00,0x73,0x70,0x00,0x00, + 0x05,0x00,0x05,0x00,0x68,0x00,0x00,0x00,0x76,0x5F,0x70,0x61,0x72,0x61,0x6D,0x73, + 0x00,0x00,0x00,0x00,0x05,0x00,0x03,0x00,0x6B,0x00,0x00,0x00,0x63,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x6C,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x6E,0x00,0x00,0x00,0x70,0x61,0x72,0x61,0x6D,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x83,0x00,0x00,0x00,0x72,0x65,0x73,0x75,0x6C,0x74,0x00,0x00, + 0x47,0x00,0x04,0x00,0x39,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x39,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x3C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x03,0x00,0x3E,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x3E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x3E,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x40,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x40,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x4C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x4F,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x68,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x83,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x02,0x00,0x00,0x00,0x21,0x00,0x03,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x06,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x21,0x00,0x05,0x00,0x09,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x0E,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x0F,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x1E,0x00,0x08,0x00,0x10,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x21,0x00,0x05,0x00,0x12,0x00,0x00,0x00, + 0x0E,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x00,0x00,0x00,0xBF,0x19,0x00,0x09,0x00, + 0x36,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x1B,0x00,0x03,0x00,0x37,0x00,0x00,0x00,0x36,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x37,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x38,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x3B,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x3B,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x3D,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1E,0x00,0x04,0x00, + 0x3E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x3F,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x3F,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x3D,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x45,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x4A,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x4A,0x00,0x00,0x00,0x4B,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x3B,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x4A,0x00,0x00,0x00,0x4E,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x3B,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x51,0x00,0x00,0x00,0x00,0x00,0x80,0x3F,0x2B,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x52,0x00,0x00,0x00,0x00,0x00,0x80,0xBF,0x2C,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x51,0x00,0x00,0x00,0x52,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x3D,0x00,0x00,0x00,0x5A,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x3D,0x00,0x00,0x00,0x5D,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x2B,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x5E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x2C,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x5F,0x00,0x00,0x00,0x5E,0x00,0x00,0x00, + 0x5E,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x3D,0x00,0x00,0x00,0x61,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x3D,0x00,0x00,0x00,0x63,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x2B,0x00,0x04,0x00,0x3D,0x00,0x00,0x00,0x66,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x20,0x00,0x04,0x00,0x67,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0E,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x67,0x00,0x00,0x00,0x68,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x14,0x00,0x02,0x00,0x71,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x72,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x3D,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x78,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2B,0x00,0x04,0x00, + 0x78,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x7A,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x82,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x3B,0x00,0x04,0x00, + 0x82,0x00,0x00,0x00,0x83,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x36,0x00,0x05,0x00, 0x02,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, 0xF8,0x00,0x02,0x00,0x05,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00, 0x35,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, 0x42,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, - 0x44,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, - 0x4A,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00, - 0x53,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00, - 0x57,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, - 0x59,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, - 0x5B,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00, - 0x5D,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x37,0x00,0x00,0x00, + 0x44,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x57,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00, + 0x6B,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x0F,0x00,0x00,0x00, + 0x6C,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x11,0x00,0x00,0x00, + 0x6E,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x37,0x00,0x00,0x00, 0x3A,0x00,0x00,0x00,0x39,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, 0x43,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x42,0x00,0x00,0x00, 0x43,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x45,0x00,0x00,0x00,0x46,0x00,0x00,0x00, @@ -3405,85 +3447,107 @@ static const uint8_t s_blit_fs_bytecode_content[3052] = { 0x0C,0x00,0x00,0x00,0x42,0x00,0x00,0x00,0x44,0x00,0x00,0x00,0x57,0x00,0x05,0x00, 0x0E,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x3A,0x00,0x00,0x00,0x48,0x00,0x00,0x00, 0x3E,0x00,0x03,0x00,0x35,0x00,0x00,0x00,0x49,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x4B,0x00,0x00,0x00,0x81,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x4F,0x00,0x00,0x00, - 0x8E,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x51,0x00,0x00,0x00,0x50,0x00,0x00,0x00, - 0x1D,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x52,0x00,0x00,0x00, - 0x51,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x4A,0x00,0x00,0x00, - 0x52,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x58,0x00,0x00,0x00, - 0x35,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x57,0x00,0x00,0x00,0x58,0x00,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x5A,0x00,0x00,0x00,0x54,0x00,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x59,0x00,0x00,0x00,0x5A,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, - 0x07,0x00,0x00,0x00,0x5C,0x00,0x00,0x00,0x4A,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x5B,0x00,0x00,0x00,0x5C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00, - 0x5E,0x00,0x00,0x00,0x56,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x5D,0x00,0x00,0x00, - 0x5E,0x00,0x00,0x00,0x39,0x00,0x08,0x00,0x0E,0x00,0x00,0x00,0x5F,0x00,0x00,0x00, - 0x15,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x5B,0x00,0x00,0x00, - 0x5D,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x53,0x00,0x00,0x00,0x5F,0x00,0x00,0x00, - 0x41,0x00,0x05,0x00,0x62,0x00,0x00,0x00,0x63,0x00,0x00,0x00,0x40,0x00,0x00,0x00, - 0x61,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x3D,0x00,0x00,0x00,0x64,0x00,0x00,0x00, - 0x63,0x00,0x00,0x00,0xAB,0x00,0x05,0x00,0x60,0x00,0x00,0x00,0x65,0x00,0x00,0x00, - 0x64,0x00,0x00,0x00,0x41,0x00,0x00,0x00,0xF7,0x00,0x03,0x00,0x67,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x65,0x00,0x00,0x00,0x66,0x00,0x00,0x00, - 0x67,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x66,0x00,0x00,0x00,0x41,0x00,0x05,0x00, - 0x6A,0x00,0x00,0x00,0x6B,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x69,0x00,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x06,0x00,0x00,0x00,0x6C,0x00,0x00,0x00,0x6B,0x00,0x00,0x00, - 0xB4,0x00,0x05,0x00,0x60,0x00,0x00,0x00,0x6E,0x00,0x00,0x00,0x6C,0x00,0x00,0x00, - 0x6D,0x00,0x00,0x00,0xF9,0x00,0x02,0x00,0x67,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, - 0x67,0x00,0x00,0x00,0xF5,0x00,0x07,0x00,0x60,0x00,0x00,0x00,0x6F,0x00,0x00,0x00, - 0x65,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6E,0x00,0x00,0x00,0x66,0x00,0x00,0x00, - 0xF7,0x00,0x03,0x00,0x71,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00, - 0x6F,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x71,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, - 0x70,0x00,0x00,0x00,0xFC,0x00,0x01,0x00,0xF8,0x00,0x02,0x00,0x71,0x00,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x75,0x00,0x00,0x00,0x53,0x00,0x00,0x00, - 0x3E,0x00,0x03,0x00,0x74,0x00,0x00,0x00,0x75,0x00,0x00,0x00,0xFD,0x00,0x01,0x00, - 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x0C,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00, - 0x0A,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x0B,0x00,0x00,0x00, - 0xF8,0x00,0x02,0x00,0x0D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, - 0x17,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00, - 0x1B,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x18,0x00,0x00,0x00,0x0A,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x19,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x1A,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, - 0x17,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x1C,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x1E,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x81,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, - 0x0C,0x00,0x06,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00, - 0x08,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x1B,0x00,0x00,0x00, - 0x20,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x21,0x00,0x00,0x00, - 0x1B,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x22,0x00,0x00,0x00, - 0x17,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x23,0x00,0x00,0x00, - 0x1B,0x00,0x00,0x00,0x83,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x24,0x00,0x00,0x00, - 0x22,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x25,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0xD1,0x00,0x04,0x00,0x07,0x00,0x00,0x00, - 0x26,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x88,0x00,0x05,0x00,0x07,0x00,0x00,0x00, - 0x27,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x50,0x00,0x05,0x00, - 0x07,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x28,0x00,0x00,0x00, - 0x50,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x2A,0x00,0x00,0x00,0x1D,0x00,0x00,0x00, - 0x1D,0x00,0x00,0x00,0x0C,0x00,0x08,0x00,0x07,0x00,0x00,0x00,0x2B,0x00,0x00,0x00, - 0x01,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x29,0x00,0x00,0x00, - 0x2A,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x2C,0x00,0x00,0x00, - 0x21,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x17,0x00,0x00,0x00, - 0x2C,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x2D,0x00,0x00,0x00, - 0x17,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x2E,0x00,0x00,0x00, - 0x0B,0x00,0x00,0x00,0x88,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x2F,0x00,0x00,0x00, - 0x2D,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0x2F,0x00,0x00,0x00, - 0x38,0x00,0x01,0x00,0x36,0x00,0x05,0x00,0x0E,0x00,0x00,0x00,0x15,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x0F,0x00,0x00,0x00, - 0x11,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x12,0x00,0x00,0x00, - 0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x37,0x00,0x03,0x00, - 0x0F,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x16,0x00,0x00,0x00, - 0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x32,0x00,0x00,0x00,0x11,0x00,0x00,0x00, - 0xFE,0x00,0x02,0x00,0x32,0x00,0x00,0x00,0x38,0x00,0x01,0x00, + 0x07,0x00,0x00,0x00,0x4D,0x00,0x00,0x00,0x4C,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x4B,0x00,0x00,0x00,0x4D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x4F,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x54,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x53,0x00,0x00,0x00,0x8E,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x55,0x00,0x00,0x00,0x54,0x00,0x00,0x00,0x1D,0x00,0x00,0x00, + 0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x56,0x00,0x00,0x00,0x55,0x00,0x00,0x00, + 0x53,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x4E,0x00,0x00,0x00,0x56,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x4B,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x57,0x00,0x00,0x00, + 0x41,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x59,0x00,0x00,0x00,0x58,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x5B,0x00,0x00,0x00,0x3C,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x5C,0x00,0x00,0x00,0x57,0x00,0x00,0x00, + 0x5A,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x5C,0x00,0x00,0x00,0x5B,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x57,0x00,0x00,0x00, + 0x5D,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x60,0x00,0x00,0x00,0x5F,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x62,0x00,0x00,0x00,0x57,0x00,0x00,0x00, + 0x61,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x62,0x00,0x00,0x00,0x5F,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x4E,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x08,0x00,0x00,0x00,0x65,0x00,0x00,0x00,0x57,0x00,0x00,0x00, + 0x63,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x65,0x00,0x00,0x00,0x64,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x68,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x0F,0x00,0x00,0x00,0x6A,0x00,0x00,0x00,0x57,0x00,0x00,0x00, + 0x66,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x6A,0x00,0x00,0x00,0x69,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x6D,0x00,0x00,0x00,0x35,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x6C,0x00,0x00,0x00,0x6D,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x10,0x00,0x00,0x00,0x6F,0x00,0x00,0x00,0x57,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x6E,0x00,0x00,0x00,0x6F,0x00,0x00,0x00,0x39,0x00,0x06,0x00,0x0E,0x00,0x00,0x00, + 0x70,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x6C,0x00,0x00,0x00,0x6E,0x00,0x00,0x00, + 0x3E,0x00,0x03,0x00,0x6B,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x41,0x00,0x05,0x00, + 0x72,0x00,0x00,0x00,0x73,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x5A,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x3D,0x00,0x00,0x00,0x74,0x00,0x00,0x00,0x73,0x00,0x00,0x00, + 0xAB,0x00,0x05,0x00,0x71,0x00,0x00,0x00,0x75,0x00,0x00,0x00,0x74,0x00,0x00,0x00, + 0x41,0x00,0x00,0x00,0xF7,0x00,0x03,0x00,0x77,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xFA,0x00,0x04,0x00,0x75,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x77,0x00,0x00,0x00, + 0xF8,0x00,0x02,0x00,0x76,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x7A,0x00,0x00,0x00, + 0x7B,0x00,0x00,0x00,0x6B,0x00,0x00,0x00,0x79,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x06,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x7B,0x00,0x00,0x00,0xB4,0x00,0x05,0x00, + 0x71,0x00,0x00,0x00,0x7D,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0x5E,0x00,0x00,0x00, + 0xF9,0x00,0x02,0x00,0x77,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x77,0x00,0x00,0x00, + 0xF5,0x00,0x07,0x00,0x71,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x75,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x7D,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0xF7,0x00,0x03,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFA,0x00,0x04,0x00,0x7E,0x00,0x00,0x00, + 0x7F,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xF8,0x00,0x02,0x00,0x7F,0x00,0x00,0x00, + 0xFC,0x00,0x01,0x00,0xF8,0x00,0x02,0x00,0x80,0x00,0x00,0x00,0x3D,0x00,0x04,0x00, + 0x0E,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0x6B,0x00,0x00,0x00,0x3E,0x00,0x03,0x00, + 0x83,0x00,0x00,0x00,0x84,0x00,0x00,0x00,0xFD,0x00,0x01,0x00,0x38,0x00,0x01,0x00, + 0x36,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x0A,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x08,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x0D,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3B,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x1B,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x18,0x00,0x00,0x00, + 0x0A,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x19,0x00,0x00,0x00, + 0x0B,0x00,0x00,0x00,0x85,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1A,0x00,0x00,0x00, + 0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x17,0x00,0x00,0x00, + 0x1A,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x1C,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00, + 0x1D,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x81,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x1F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x0C,0x00,0x06,0x00, + 0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x1F,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x1B,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x1B,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x1B,0x00,0x00,0x00, + 0x83,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x23,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x17,0x00,0x00,0x00,0xD1,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x26,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x88,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x27,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x50,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x29,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x50,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x2A,0x00,0x00,0x00,0x1D,0x00,0x00,0x00,0x1D,0x00,0x00,0x00, + 0x0C,0x00,0x08,0x00,0x07,0x00,0x00,0x00,0x2B,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2B,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x29,0x00,0x00,0x00,0x2A,0x00,0x00,0x00, + 0x81,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x2C,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x2B,0x00,0x00,0x00,0x3E,0x00,0x03,0x00,0x17,0x00,0x00,0x00,0x2C,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x2D,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x3D,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x2E,0x00,0x00,0x00,0x0B,0x00,0x00,0x00, + 0x88,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x2F,0x00,0x00,0x00,0x2D,0x00,0x00,0x00, + 0x2E,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0x2F,0x00,0x00,0x00,0x38,0x00,0x01,0x00, + 0x36,0x00,0x05,0x00,0x0E,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x12,0x00,0x00,0x00,0x37,0x00,0x03,0x00,0x0F,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x37,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xF8,0x00,0x02,0x00, + 0x16,0x00,0x00,0x00,0x3D,0x00,0x04,0x00,0x0E,0x00,0x00,0x00,0x32,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0xFE,0x00,0x02,0x00,0x32,0x00,0x00,0x00,0x38,0x00,0x01,0x00, }; #endif -static const char s_blit_fs_bytecode_glsl300_src[1368] = +static const char s_blit_fs_bytecode_glsl300_src[1631] = "#version 300 es\n" "precision mediump float;\n" "precision highp int;\n" "\n" +"struct ShaderParams\n" +"{\n" +" highp vec2 view_pos;\n" +" highp vec2 uv;\n" +" highp vec2 uv_min;\n" +" highp vec2 uv_max;\n" +" highp vec2 screen_uv;\n" +" highp vec4 attributes;\n" +"};\n" +"\n" "layout(std140) uniform uniform_block\n" "{\n" " highp vec2 u_texture_size;\n" @@ -3493,10 +3557,12 @@ static const char s_blit_fs_bytecode_glsl300_src[1368] = "uniform highp sampler2D u_image;\n" "\n" "in highp vec2 v_uv;\n" -"in highp vec2 v_posH;\n" "in highp vec2 v_pos;\n" +"in highp vec2 v_posH;\n" "in highp vec4 v_params;\n" "layout(location = 0) out highp vec4 result;\n" +"highp vec2 pos;\n" +"highp vec2 screen_uv;\n" "\n" "highp vec2 smooth_uv(highp vec2 uv, highp vec2 texture_size)\n" "{\n" @@ -3506,7 +3572,7 @@ static const char s_blit_fs_bytecode_glsl300_src[1368] = " return pixel / texture_size;\n" "}\n" "\n" -"highp vec4 shader(highp vec4 color, highp vec2 pos, highp vec2 screen_uv, highp vec4 params)\n" +"highp vec4 shader(highp vec4 color, ShaderParams params)\n" "{\n" " return color;\n" "}\n" @@ -3516,23 +3582,29 @@ static const char s_blit_fs_bytecode_glsl300_src[1368] = " highp vec2 param = v_uv;\n" " highp vec2 param_1 = _64.u_texture_size;\n" " highp vec4 color = texture(u_image, smooth_uv(param, param_1));\n" -" highp vec2 screen_uv = ((v_posH + vec2(1.0, -1.0)) * 0.5) * vec2(1.0, -1.0);\n" +" pos = v_pos;\n" +" screen_uv = ((v_posH + vec2(1.0, -1.0)) * 0.5) * vec2(1.0, -1.0);\n" +" ShaderParams sp;\n" +" sp.view_pos = pos;\n" +" sp.uv = v_uv;\n" +" sp.uv_min = vec2(0.0);\n" +" sp.uv_max = vec2(0.0);\n" +" sp.screen_uv = screen_uv;\n" +" sp.attributes = v_params;\n" " highp vec4 param_2 = color;\n" -" highp vec2 param_3 = v_pos;\n" -" highp vec2 param_4 = screen_uv;\n" -" highp vec4 param_5 = v_params;\n" -" highp vec4 c = shader(param_2, param_3, param_4, param_5);\n" -" bool _101 = _64.u_alpha_discard != 0;\n" -" bool _111;\n" -" if (_101)\n" +" ShaderParams param_3 = sp;\n" +" highp vec4 c = shader(param_2, param_3);\n" +" bool _117 = _64.u_alpha_discard != 0;\n" +" bool _126;\n" +" if (_117)\n" " {\n" -" _111 = c.w == 0.0;\n" +" _126 = c.w == 0.0;\n" " }\n" " else\n" " {\n" -" _111 = _101;\n" +" _126 = _117;\n" " }\n" -" if (_111)\n" +" if (_126)\n" " {\n" " discard;\n" " }\n" @@ -3543,6 +3615,7 @@ static const char s_blit_fs_bytecode_glsl300_src[1368] = static const char* s_blit_fs_bytecode_image_names[1] = { "u_image", }; +static int s_blit_fs_bytecode_image_binding_slots[1] = { 0, }; static CF_ShaderUniformInfo s_blit_fs_bytecode_uniforms[1] = { { .block_name = "uniform_block", @@ -3569,16 +3642,19 @@ static CF_ShaderUniformMemberInfo s_blit_fs_bytecode_uniform_members[2] = { static const CF_ShaderBytecode s_blit_fs_bytecode = { #ifndef __EMSCRIPTEN__ .content = s_blit_fs_bytecode_content, - .size = 3052, + .size = 3536, #endif .glsl300_src = s_blit_fs_bytecode_glsl300_src, - .glsl300_src_size = 1367, + .glsl300_src_size = 1630, .shader_info = { .num_samplers = 1, .num_storage_textures = 0, .num_storage_buffers = 0, + .num_readwrite_storage_textures = 0, + .num_readwrite_storage_buffers = 0, .num_images = 1, .image_names = s_blit_fs_bytecode_image_names, + .image_binding_slots = s_blit_fs_bytecode_image_binding_slots, .num_uniforms = 1, .uniforms = s_blit_fs_bytecode_uniforms, .num_uniform_members = 2, diff --git a/src/internal/cute_app_internal.h b/src/internal/cute_app_internal.h index 5a4d01622..250780c82 100644 --- a/src/internal/cute_app_internal.h +++ b/src/internal/cute_app_internal.h @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -29,7 +29,7 @@ struct SDL_Window; struct cs_context_t; -extern struct CF_App* app; +CF_API extern struct CF_App* app; struct CF_MouseState { @@ -74,12 +74,11 @@ struct CF_App CF_BackendType gfx_backend_type = CF_BACKEND_TYPE_INVALID; bool debug_opengl = false; cs_context_t* cute_sound = NULL; - bool spawned_mix_thread = false; void (*on_shader_changed_fn)(const char* path, void* udata) = NULL; void* on_shader_changed_udata = NULL; bool shader_directory_set = false; Cute::CF_Path shader_directory; - Cute::Map shader_file_infos; + Cute::Map shader_file_infos; bool gfx_enabled = false; float dpi_scale = 1.0f; float dpi_scale_prev = 1.0f; @@ -92,7 +91,7 @@ struct CF_App int y; int canvas_w; int canvas_h; - CF_Color clear_color = cf_color_black(); + CF_Color clear_color = { 0, 0, 0, 0 }; float clear_depth = 1.0f; uint32_t clear_stencil = 0; CF_Canvas offscreen_canvas = { }; @@ -100,7 +99,7 @@ struct CF_App CF_WindowState window_state_prev; int sample_count = 0; bool use_depth_stencil = false; - uint64_t default_image_id = CF_PNG_ID_RANGE_LO; + uint64_t default_image_id = CF_CUSTOM_SPRITE_ID_RANGE_LO; bool vsync = false; bool audio_needs_updates = false; void* update_udata = NULL; @@ -132,15 +131,16 @@ struct CF_App // Font stuff. uint64_t font_image_id_gen = CF_FONT_ID_RANGE_LO; - Cute::Map fonts; - Cute::Map font_pixels; - Cute::Map text_effect_states; - Cute::Map parsed_text_states; - Cute::Map text_effect_fns; + Cute::Map fonts; + Cute::Map font_pixels; + Cute::Map text_effect_states; + Cute::Map parsed_text_states; + Cute::Map text_effect_fns; // Easy sprite stuff. uint64_t easy_sprite_id_gen = CF_EASY_ID_RANGE_LO; - Cute::Map easy_sprites; + Cute::Map easy_sprites; + Cute::Map easy_sprite_id_cache; }; #endif // CF_APP_INTERNAL_H diff --git a/src/internal/cute_aseprite_cache_internal.h b/src/internal/cute_aseprite_cache_internal.h index 00e5f1071..48d902a3c 100644 --- a/src/internal/cute_aseprite_cache_internal.h +++ b/src/internal/cute_aseprite_cache_internal.h @@ -9,17 +9,51 @@ #define CF_ASEPRITE_CACHE_INTERNAL_H #include +#include +#include +#include #include +#define CF_SPRITE_ID_INVALID (~0ULL) + CF_Result cf_aseprite_cache_load(const char* aseprite_path, CF_Sprite* sprite_out); CF_Result cf_aseprite_cache_load_from_memory(const char* unique_name, const void* data, int sz, CF_Sprite* sprite_out); void cf_aseprite_cache_unload(const char* aseprite_path); CF_Result cf_aseprite_cache_load_ase(const char* aseprite_path, ase_t** ase); -void cf_make_aseprite_cache(); -void cf_destroy_aseprite_cache(); +CF_API void cf_make_aseprite_cache(); +CF_API void cf_destroy_aseprite_cache(); void cf_aseprite_cache_get_pixels(uint64_t image_id, void* buffer, int bytes_to_fill); +// Per-aseprite asset. Indexed by asset_id in the flat array. +struct CF_SpriteAsset +{ + const char* path; + ase_t* ase; + int w, h; + CF_MAP(CF_Animation*) animations; + dyna CF_SpriteSlice* slices; + dyna CF_V2* pivots; + dyna CF_Aabb* center_patches; + dyna uint64_t* frame_ids; + int blend_count; // Total blends (1 = default only). + dyna uint64_t** blend_frame_ids; // Array of frame_id arrays per blend. + dyna void** owned_pixel_buffers; // Pixel buffers from re-blending (index 1+). +}; + +CF_SpriteAsset* cf_sprite_get_asset(uint64_t asset_id); + +int cf_aseprite_cache_add_blend(const char* path, uint64_t layer_mask); +uint64_t cf_aseprite_layer_mask(ase_t* ase, const char** layer_names, int count); + +// Register an externally-owned animation table as a sprite asset (used by custom sprite cache). +// The caller retains ownership of the animations; the asset will not free them. +uint64_t cf_register_external_sprite_asset(const char* name, int w, int h, CF_MAP(CF_Animation*) animations); + +// Reload an asset's pixel data and animations from a newly parsed ase_t. +// Reuses frame IDs where possible so other sprites sharing the asset pick up changes. +// Takes ownership of new_ase. +void cf_aseprite_cache_reload_asset(uint64_t asset_id, ase_t* new_ase); #endif // CF_ASEPRITE_CACHE_INTERNAL_H diff --git a/src/internal/cute_binding_internal.h b/src/internal/cute_binding_internal.h new file mode 100644 index 000000000..c2f53fea6 --- /dev/null +++ b/src/internal/cute_binding_internal.h @@ -0,0 +1,13 @@ +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +#ifndef CF_BINDING_INTERNAL_H +#define CF_BINDING_INTERNAL_H + +void cf_binding_update(); + +#endif // CF_BINDING_INTERNAL_H diff --git a/src/internal/cute_custom_sprite_internal.h b/src/internal/cute_custom_sprite_internal.h new file mode 100644 index 000000000..86a2516f5 --- /dev/null +++ b/src/internal/cute_custom_sprite_internal.h @@ -0,0 +1,17 @@ +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +#ifndef CF_CUSTOM_SPRITE_INTERNAL_H +#define CF_CUSTOM_SPRITE_INTERNAL_H + +#include + +CF_API void cf_make_custom_sprite_cache(); +CF_API void cf_destroy_custom_sprite_cache(); +void cf_custom_sprite_get_pixels(uint64_t image_id, void* buffer, int bytes_to_fill); + +#endif // CF_CUSTOM_SPRITE_INTERNAL_H diff --git a/src/internal/cute_draw_internal.h b/src/internal/cute_draw_internal.h index 43ae23cfd..8d967818f 100644 --- a/src/internal/cute_draw_internal.h +++ b/src/internal/cute_draw_internal.h @@ -44,8 +44,12 @@ struct BatchGeometry bool is_text; bool is_sprite; bool fill; - bool unused; + bool use_tri_colors; // Per-vertex colors for triangles. + bool use_tri_attributes; // Per-vertex attributes for triangles. CF_Color user_params; + CF_Pixel tri_colors[3]; // Per-vertex colors (only for BATCH_GEOMETRY_TYPE_TRI). + CF_Color tri_attributes[3]; // Per-vertex attributes (only for BATCH_GEOMETRY_TYPE_TRI). + float uv_bounds[4]; // uv_min.xy, uv_max.zw. Zero for shapes. }; #define SPRITEBATCH_SPRITE_GEOMETRY BatchGeometry @@ -78,6 +82,7 @@ struct CF_Command CF_Rect scissor = { 0, 0, -1, -1 }; CF_Rect viewport = { 0, 0, -1, -1 }; float alpha_discard = 1.0f; + CF_DrawFilterMode filter_mode = CF_DRAW_FILTER_SMOOTH; CF_RenderState render_state; CF_Shader shader; Cute::Array items; @@ -135,6 +140,7 @@ struct CF_Draw cmd.scissor = scissors.last(); cmd.viewport = viewports.last(); cmd.alpha_discard = alpha_discards.last(); + cmd.filter_mode = filter_modes.last(); cmd.render_state = render_states.last(); cmd.shader = shaders.last(); return cmd; @@ -150,21 +156,28 @@ struct CF_Draw CF_Mesh mesh; CF_Material material; CF_Arena uniform_arena; - Cute::Array alpha_discards = { true }; + Cute::Array alpha_discards = { 1.0f }; + Cute::Array filter_modes = { CF_DRAW_FILTER_SMOOTH }; Cute::Array colors = { cf_color_white() }; - Cute::Array antialias = { true }; - Cute::Array antialias_scale = { 1.5f }; + Cute::Array antialias = { 1.5f }; Cute::Array render_states; Cute::Array scissors = { { 0, 0, -1, -1 } }; Cute::Array viewports = { { 0, 0, -1, -1 } }; Cute::Array layers = { 0 }; Cute::Array cam_stack = { cf_make_identity() }; + Cute::Array projection_stack; float aaf = 0; CF_M3x2 projection; CF_M3x2 mvp; void reset_cam(); void set_aaf(); Cute::Array user_params = { cf_make_color_hex(0) }; + Cute::Array tri_colors0 = { cf_color_white() }; + Cute::Array tri_colors1 = { cf_color_white() }; + Cute::Array tri_colors2 = { cf_color_white() }; + Cute::Array tri_attributes0 = { cf_make_color_hex(0) }; + Cute::Array tri_attributes1 = { cf_make_color_hex(0) }; + Cute::Array tri_attributes2 = { cf_make_color_hex(0) }; Cute::Array shaders; Cute::Array temp; Cute::Array font_sizes = { 18 }; @@ -175,13 +188,18 @@ struct CF_Draw Cute::Array text_ids = { 0 }; Cute::Array strikes; Cute::Array text_effects = { true }; - Cute::Map premade_sub_image_id_to_sub_image; - Cute::Map draw_shd_to_blit_shd; + Cute::Map premade_sub_image_id_to_sub_image; + Cute::Map draw_shd_to_blit_shd; + Cute::Map shader_paths; // shader.id -> interned file path for reload bool blit_init = false; CF_Mesh blit_mesh = { 0 }; CF_VertexFn* vertex_fn = NULL; bool need_flush = false; bool has_drawn_something = false; + + // Samplers for filter mode (backend-specific, stored as void* for cross-platform compatibility) + void* sampler_nearest = NULL; + void* sampler_linear = NULL; }; void cf_make_draw(); @@ -192,9 +210,9 @@ void cf_destroy_draw(); #define CF_IMAGE_ID_RANGE_SIZE ((1ULL << 60) - 1) #define CF_ASEPRITE_ID_RANGE_LO (0ULL) #define CF_ASEPRITE_ID_RANGE_HI (CF_ASEPRITE_ID_RANGE_LO + CF_IMAGE_ID_RANGE_SIZE) -#define CF_PNG_ID_RANGE_LO (CF_ASEPRITE_ID_RANGE_HI + 1) -#define CF_PNG_ID_RANGE_HI (CF_PNG_ID_RANGE_LO + CF_IMAGE_ID_RANGE_SIZE) -#define CF_FONT_ID_RANGE_LO (CF_PNG_ID_RANGE_HI + 1) +#define CF_CUSTOM_SPRITE_ID_RANGE_LO (CF_ASEPRITE_ID_RANGE_HI + 1) +#define CF_CUSTOM_SPRITE_ID_RANGE_HI (CF_CUSTOM_SPRITE_ID_RANGE_LO + CF_IMAGE_ID_RANGE_SIZE) +#define CF_FONT_ID_RANGE_LO (CF_CUSTOM_SPRITE_ID_RANGE_HI + 1) #define CF_FONT_ID_RANGE_HI (CF_FONT_ID_RANGE_LO + CF_IMAGE_ID_RANGE_SIZE) #define CF_EASY_ID_RANGE_LO (CF_FONT_ID_RANGE_HI + 1) #define CF_EASY_ID_RANGE_HI (CF_EASY_ID_RANGE_LO + CF_IMAGE_ID_RANGE_SIZE) diff --git a/src/internal/cute_font_internal.h b/src/internal/cute_font_internal.h index c406a2909..61d41d17a 100644 --- a/src/internal/cute_font_internal.h +++ b/src/internal/cute_font_internal.h @@ -9,7 +9,7 @@ #define CF_FONT_INTERNAL_H #include -#include +#include #include #include #include @@ -31,8 +31,8 @@ struct CF_Font { uint8_t* file_data = NULL; stbtt_fontinfo info; - Cute::Map kerning; - Cute::Map glyphs; + Cute::Map kerning; + Cute::Map glyphs; Cute::Array image_ids; int ascent; int descent; @@ -42,9 +42,10 @@ struct CF_Font int height; }; -CF_Font* cf_font_get(const char* font_name); -CF_Glyph* cf_font_get_glyph(CF_Font* font, int codepoint, float font_size, int blur); -float cf_font_get_kern(CF_Font* font, float font_size, int codepoint0, int codepoint1); +CF_API CF_Font* CF_CALL cf_font_get(const char* font_name); +CF_API CF_Glyph* CF_CALL cf_font_get_glyph(CF_Font* font, int codepoint, float font_size, int blur); +CF_API float CF_CALL cf_font_get_kern(CF_Font* font, float font_size, int codepoint0, int codepoint1); +CF_API float CF_CALL cf_font_scale_for_pixel_height(CF_Font* font, float pixel_height); #define CF_KERN_KEY(cp0, cp1) (((uint64_t)cp0) << 32 | ((uint64_t)cp1)) @@ -54,7 +55,7 @@ struct CF_TextCode int index_in_string; int glyph_count; CF_TextEffectFn* fn; - Cute::Map params; + Cute::Map params; }; struct TextEffect : public CF_TextEffect @@ -94,7 +95,7 @@ struct TextEffect : public CF_TextEffect // "private" state -- don't touch. int initial_index; - const Cute::Map* params; + const Cute::Map* params; CF_TextEffectFn* fn; bool line_bound_init = false; CF_Aabb line_bound; diff --git a/src/internal/cute_graphics_internal.h b/src/internal/cute_graphics_internal.h index 3e4a81bbe..fb3471fe4 100644 --- a/src/internal/cute_graphics_internal.h +++ b/src/internal/cute_graphics_internal.h @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #define CF_MAX_UNIFORM_BLOCK_COUNT (4) @@ -146,6 +148,7 @@ struct CF_MaterialInternal CF_RenderState state; CF_MaterialState vs; CF_MaterialState fs; + CF_MaterialState cs; CF_Arena uniform_arena; CF_Arena block_arena; }; @@ -238,6 +241,7 @@ SDL_GPUCommandBuffer* cf_sdlgpu_get_command_buffer(); void cf_sdlgpu_attach(SDL_Window* window); bool cf_sdlgpu_supports_msaa(int sample_count); void cf_sdlgpu_flush(); +void cf_sdlgpu_gpu_sync(); void cf_sdlgpu_set_vsync(bool true_turn_on_vsync); void cf_sdlgpu_set_vsync_mailbox(bool true_turn_on_vsync); void cf_sdlgpu_begin_frame(); @@ -252,10 +256,18 @@ SDL_GLContext cf_gles_get_gl_context(); void cf_gles_attach(SDL_Window* window); bool cf_gles_supports_msaa(int sample_count); void cf_gles_flush(); +void cf_gles_gpu_sync(); void cf_gles_set_vsync(bool true_turn_on_vsync); void cf_gles_begin_frame(); void cf_gles_blit_canvas(CF_Canvas canvas); void cf_gles_end_frame(); void cf_gles_cleanup(); +// Draw system sampler override API. +// Used by the draw system to dynamically switch between nearest/linear filtering. +// Dispatch shims are defined in cute_graphics.cpp via CF_DISPATCH_SHIM macros. +void* cf_create_draw_sampler(CF_Filter filter); +void cf_destroy_draw_sampler(void* sampler); +void cf_set_sampler_override(void* sampler); + #endif // CF_GRAPHICS_INTERNAL_H diff --git a/src/internal/cute_png_cache_internal.h b/src/internal/cute_png_cache_internal.h deleted file mode 100644 index 9f831391a..000000000 --- a/src/internal/cute_png_cache_internal.h +++ /dev/null @@ -1,17 +0,0 @@ -/* - Cute Framework - Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ - - This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info -*/ - -#ifndef CF_PNG_CACHE_INTERNAL_H -#define CF_PNG_CACHE_INTERNAL_H - -#include - -void cf_make_png_cache(); -void cf_destroy_png_cache(); -void cf_png_cache_get_pixels(uint64_t image_id, void* buffer, int bytes_to_fill); - -#endif // CF_PNG_CACHE_INTERNAL_H diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0f2255b11..9541e3136 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,16 +9,20 @@ set(CF_TEST_SRCS test_doubly_list.cpp test_hashtable.cpp test_path.cpp - test_png_cache.cpp + test_custom_sprite.cpp test_sprite.cpp test_string.cpp test_json.cpp test_markups.cpp - ) + test_math.cpp + test_math.c + test_ckit.c +) set(CF_TEST_HDRS test_harness.h) add_executable(tests ${CF_TEST_SRCS} ${CF_TEST_HDRS}) target_link_libraries(tests PRIVATE cute) +target_include_directories(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../src) set_target_properties(tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} ) diff --git a/test/main.cpp b/test/main.cpp index a64dfb83d..00093813f 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -34,11 +34,16 @@ TEST_SUITE(test_coroutine); TEST_SUITE(test_doubly_list); TEST_SUITE(test_hashtable); TEST_SUITE(test_path); -TEST_SUITE(test_png_cache); +TEST_SUITE(test_custom_sprite); TEST_SUITE(test_sprite); TEST_SUITE(test_string); TEST_SUITE(test_json); TEST_SUITE(test_markups); +TEST_SUITE(test_math); +extern "C" { +TEST_SUITE(test_math_c); +TEST_SUITE(test_ckit); +} #include @@ -56,26 +61,31 @@ int main(int argc, char* argv[]) _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); - #undef RUN_TEST_SUITE - #define RUN_TEST_SUITE(suite_fp) pu_run_suite(#suite_fp, suite_fp); sinuke(); SDL_Quit(); _CrtDumpMemoryLeaks(); #endif pu_display_colors(true); + setvbuf(stdout, NULL, _IONBF, 0); + setvbuf(stderr, NULL, _IONBF, 0); - RUN_TEST_SUITE(test_array); - RUN_TEST_SUITE(test_aseprite); - RUN_TEST_SUITE(test_audio); - RUN_TEST_SUITE(test_base64); - RUN_TEST_SUITE(test_color); - RUN_TEST_SUITE(test_coroutine); - RUN_TEST_SUITE(test_doubly_list); - RUN_TEST_SUITE(test_hashtable); - RUN_TEST_SUITE(test_path); - RUN_TEST_SUITE(test_png_cache); - RUN_TEST_SUITE(test_sprite); - RUN_TEST_SUITE(test_string); - RUN_TEST_SUITE(test_json); - RUN_TEST_SUITE(test_markups); +#define RUN_TRACED(suite_fp) fprintf(stderr, ">>> " #suite_fp "\n"); RUN_TEST_SUITE(suite_fp); fprintf(stderr, "<<< " #suite_fp "\n"); + RUN_TRACED(test_array); + RUN_TRACED(test_aseprite); + RUN_TRACED(test_audio); + RUN_TRACED(test_base64); + RUN_TRACED(test_color); + RUN_TRACED(test_coroutine); + RUN_TRACED(test_doubly_list); + RUN_TRACED(test_hashtable); + RUN_TRACED(test_path); + RUN_TRACED(test_custom_sprite); + RUN_TRACED(test_sprite); + RUN_TRACED(test_string); + RUN_TRACED(test_json); + RUN_TRACED(test_markups); + RUN_TRACED(test_math); + RUN_TRACED(test_math_c); + RUN_TRACED(test_ckit); +#undef RUN_TRACED pu_print_stats(); return pu_test_failed(); diff --git a/test/test_aseprite.cpp b/test/test_aseprite.cpp index 0e3dda5a2..d6f483bd5 100644 --- a/test/test_aseprite.cpp +++ b/test/test_aseprite.cpp @@ -11,7 +11,7 @@ using namespace Cute; #ifndef CF_STATIC -# define CF_ASEPRITE_IMPLEMENTATION +# define CUTE_ASEPRITE_IMPLEMENTATION # include #endif diff --git a/test/test_ckit.c b/test/test_ckit.c new file mode 100644 index 000000000..caeeda33e --- /dev/null +++ b/test/test_ckit.c @@ -0,0 +1,1630 @@ +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +#ifndef _CRT_SECURE_NO_WARNINGS +# define _CRT_SECURE_NO_WARNINGS +#endif + +#ifndef _CRT_NONSTDC_NO_DEPRECATE +# define _CRT_NONSTDC_NO_DEPRECATE +#endif + +// CKIT_IMPLEMENTATION is provided by cute.lib +#include + +#include +#include + +//-------------------------------------------------------------------------------------------------- +// Arrays. + +TEST_CASE(test_ckit_array_push_pop) +{ + int* a = NULL; + for (int i = 0; i < 10; ++i) apush(a, i); + REQUIRE(asize(a) == 10); + REQUIRE(acount(a) == 10); + for (int i = 0; i < 10; ++i) REQUIRE(a[i] == i); + REQUIRE(alast(a) == 9); + REQUIRE(apop(a) == 9); + REQUIRE(asize(a) == 9); + afree(a); + REQUIRE(a == NULL); + return true; +} + +TEST_CASE(test_ckit_array_fit_and_cap) +{ + int* a = NULL; + afit(a, 100); + REQUIRE(acap(a) >= 100); + REQUIRE(asize(a) == 0); + for (int i = 0; i < 100; ++i) apush(a, i); + REQUIRE(asize(a) == 100); + afree(a); + return true; +} + +TEST_CASE(test_ckit_array_clear) +{ + int* a = NULL; + for (int i = 0; i < 5; ++i) apush(a, i); + REQUIRE(asize(a) == 5); + aclear(a); + REQUIRE(asize(a) == 0); + REQUIRE(acap(a) >= 5); + afree(a); + return true; +} + +TEST_CASE(test_ckit_array_del) +{ + int* a = NULL; + apush(a, 10); + apush(a, 20); + apush(a, 30); + adel(a, 0); + REQUIRE(asize(a) == 2); + REQUIRE(a[0] == 30); + REQUIRE(a[1] == 20); + afree(a); + return true; +} + +TEST_CASE(test_ckit_array_end) +{ + int* a = NULL; + apush(a, 1); + apush(a, 2); + apush(a, 3); + int* e = aend(a); + REQUIRE(e == a + 3); + afree(a); + return true; +} + +TEST_CASE(test_ckit_array_large) +{ + int* a = NULL; + for (int i = 0; i < 10000; ++i) apush(a, i); + REQUIRE(asize(a) == 10000); + for (int i = 0; i < 10000; ++i) REQUIRE(a[i] == i); + afree(a); + return true; +} + +TEST_CASE(test_ckit_array_structs) +{ + typedef struct { int x; int y; } Pair; + Pair* a = NULL; + apush(a, ((Pair){1, 2})); + apush(a, ((Pair){3, 4})); + apush(a, ((Pair){5, 6})); + REQUIRE(a[0].x == 1 && a[0].y == 2); + REQUIRE(a[1].x == 3 && a[1].y == 4); + REQUIRE(a[2].x == 5 && a[2].y == 6); + Pair p = apop(a); + REQUIRE(p.x == 5 && p.y == 6); + afree(a); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Strings - basic. + +TEST_CASE(test_ckit_string_set_append) +{ + char* s = NULL; + sset(s, "Hello "); + sappend(s, "world!"); + REQUIRE(sequ(s, "Hello world!")); + REQUIRE(slen(s) == 12); + REQUIRE(scount(s) == 13); + sfree(s); + REQUIRE(s == NULL); + return true; +} + +TEST_CASE(test_ckit_string_push_pop) +{ + char* s = NULL; + spush(s, 'a'); + spush(s, 'b'); + spush(s, 'c'); + REQUIRE(slen(s) == 3); + REQUIRE(sequ(s, "abc")); + REQUIRE(sfirst(s) == 'a'); + REQUIRE(slast(s) == 'c'); + spop(s); + REQUIRE(slen(s) == 2); + REQUIRE(sequ(s, "ab")); + spopn(s, 2); + REQUIRE(slen(s) == 0); + REQUIRE(sempty(s)); + sfree(s); + return true; +} + +TEST_CASE(test_ckit_string_dup_make) +{ + char* orig = NULL; + sset(orig, "duplicate me"); + char* copy = sdup(orig); + REQUIRE(sequ(orig, copy)); + REQUIRE(orig != copy); + char* made = smake("from literal"); + REQUIRE(sequ(made, "from literal")); + sfree(orig); + sfree(copy); + sfree(made); + return true; +} + +TEST_CASE(test_ckit_string_clear) +{ + char* s = NULL; + sset(s, "hello"); + sclear(s); + REQUIRE(slen(s) == 0); + REQUIRE(sempty(s)); + sappend(s, "world"); + REQUIRE(sequ(s, "world")); + sfree(s); + return true; +} + +TEST_CASE(test_ckit_string_fit) +{ + char* s = NULL; + sfit(s, 200); + REQUIRE(scap(s) >= 200); + REQUIRE(slen(s) == 0); + sfree(s); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Strings - formatting. + +TEST_CASE(test_ckit_string_fmt) +{ + char* s = NULL; + sfmt(s, "x=%d y=%d", 10, 20); + REQUIRE(sequ(s, "x=10 y=20")); + sfmt(s, "%s", "overwrite"); + REQUIRE(sequ(s, "overwrite")); + sfree(s); + return true; +} + +TEST_CASE(test_ckit_string_fmt_append) +{ + char* s = NULL; + sfmt(s, "%d,", 1); + sfmt_append(s, "%d,", 2); + sfmt_append(s, "%d", 3); + REQUIRE(sequ(s, "1,2,3")); + sfree(s); + + // Test fmt_append on NULL. + char* s2 = NULL; + sfmt_append(s2, "%d,%d", 42, 99); + REQUIRE(sequ(s2, "42,99")); + sfree(s2); + return true; +} + +TEST_CASE(test_ckit_string_fmt_long) +{ + char* s = NULL; + const char* long_str = "some string longer than the default string length forcing an sfit call to test its code path."; + sfmt(s, "%s", long_str); + REQUIRE(sequ(s, long_str)); + sfree(s); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Strings - compare. + +TEST_CASE(test_ckit_string_compare) +{ + REQUIRE(sequ("abc", "abc")); + REQUIRE(!sequ("abc", "def")); + REQUIRE(sequ(NULL, NULL)); + REQUIRE(!sequ("abc", NULL)); + REQUIRE(!sequ(NULL, "abc")); + + REQUIRE(siequ("ABC", "abc")); + REQUIRE(siequ("Hello", "hello")); + REQUIRE(!siequ("abc", "def")); + + REQUIRE(scmp("abc", "abc") == 0); + REQUIRE(scmp("abc", "abd") < 0); + REQUIRE(sicmp("ABC", "abc") == 0); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Strings - prefix, suffix, contains, find. + +TEST_CASE(test_ckit_string_prefix_suffix) +{ + char* s = NULL; + sset(s, "hello.txt"); + REQUIRE(sprefix(s, "hello")); + REQUIRE(sprefix(s, "h")); + REQUIRE(!sprefix(s, "world")); + REQUIRE(ssuffix(s, ".txt")); + REQUIRE(ssuffix(s, "t")); + REQUIRE(!ssuffix(s, ".png")); + REQUIRE(scontains(s, "llo.tx")); + REQUIRE(!scontains(s, "xyz")); + sfree(s); + return true; +} + +TEST_CASE(test_ckit_string_index_of) +{ + char* s = NULL; + sset(s, "012330"); + REQUIRE(sfirst_index_of(s, '0') == 0); + REQUIRE(sfirst_index_of(s, '3') == 3); + REQUIRE(slast_index_of(s, '3') == 4); + REQUIRE(slast_index_of(s, '0') == 5); + REQUIRE(sfirst_index_of(s, 'z') == -1); + REQUIRE(slast_index_of(s, 'z') == -1); + sfree(s); + + REQUIRE(sfirst_index_of(NULL, 'a') == -1); + REQUIRE(slast_index_of(NULL, 'a') == -1); + return true; +} + +TEST_CASE(test_ckit_string_find) +{ + char* s = NULL; + sset(s, "hello world"); + REQUIRE(sfind(s, "world") != NULL); + REQUIRE(sfind(s, "xyz") == NULL); + sfree(s); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Strings - transform. + +TEST_CASE(test_ckit_string_case) +{ + char* s = NULL; + sset(s, "Hello World"); + stoupper(s); + REQUIRE(sequ(s, "HELLO WORLD")); + stolower(s); + REQUIRE(sequ(s, "hello world")); + sfree(s); + return true; +} + +TEST_CASE(test_ckit_string_trim) +{ + char* a = NULL; + sset(a, " spac es "); + strim(a); + REQUIRE(sequ(a, "spac es")); + REQUIRE(slen(a) == 7); + + char* b = NULL; + sset(b, " spac es "); + sltrim(b); + REQUIRE(sequ(b, "spac es ")); + + char* c = NULL; + sset(c, " spac es "); + srtrim(c); + REQUIRE(sequ(c, " spac es")); + + // Trim string with no whitespace. + char* d = NULL; + sset(d, "nospace"); + strim(d); + REQUIRE(sequ(d, "nospace")); + + sfree(a); + sfree(b); + sfree(c); + sfree(d); + return true; +} + +TEST_CASE(test_ckit_string_pad) +{ + char* s = NULL; + sset(s, "pad"); + srpad(s, 'x', 3); + REQUIRE(sequ(s, "padxxx")); + slpad(s, 'y', 2); + REQUIRE(sequ(s, "yypadxxx")); + sfree(s); + return true; +} + +TEST_CASE(test_ckit_string_replace) +{ + char* s = NULL; + sset(s, "hi hi hi"); + sreplace(s, "hi", "oof"); + REQUIRE(sequ(s, "oof oof oof")); + sreplace(s, "oof", "hi"); + REQUIRE(sequ(s, "hi hi hi")); + + // Replace with shorter. + sset(s, "aabbcc"); + sreplace(s, "bb", "x"); + REQUIRE(sequ(s, "aaxcc")); + + // Replace with longer. + sset(s, "ab"); + sreplace(s, "ab", "abcdef"); + REQUIRE(sequ(s, "abcdef")); + + // No match. + sset(s, "hello"); + sreplace(s, "xyz", "abc"); + REQUIRE(sequ(s, "hello")); + + sfree(s); + return true; +} + +TEST_CASE(test_ckit_string_dedup) +{ + char* s = NULL; + sset(s, "aaa//bb///cc"); + sdedup(s, '/'); + REQUIRE(sequ(s, "aaa/bb/cc")); + sfree(s); + return true; +} + +TEST_CASE(test_ckit_string_erase) +{ + char* s = NULL; + sset(s, "erase me"); + serase(s, 0, 2); + REQUIRE(sequ(s, "ase me")); + + sset(s, "erase.me.please"); + serase(s, 10, 100); + REQUIRE(sequ(s, "erase.me.p")); + + // Erase past end. + sset(s, "abc"); + serase(s, 30, 10); + REQUIRE(sequ(s, "abc")); + + // Negative index. + sset(s, "erase.me.please"); + serase(s, -5, 10); + REQUIRE(sequ(s, ".me.please")); + + sfree(s); + return true; +} + +TEST_CASE(test_ckit_string_append_range) +{ + char* s = NULL; + sset(s, "hello"); + const char* world = " world!"; + sappend_range(s, world, world + 6); + REQUIRE(sequ(s, "hello world")); + sfree(s); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Strings - split. + +TEST_CASE(test_ckit_string_split_once) +{ + char* s = NULL; + sset(s, "split.here"); + char* left = ssplit_once(s, '.'); + REQUIRE(sequ(left, "split")); + REQUIRE(sequ(s, "here")); + sfree(left); + sfree(s); + return true; +} + +TEST_CASE(test_ckit_string_split) +{ + char** parts = ssplit("a.b.c", '.'); + REQUIRE(asize(parts) == 3); + REQUIRE(sequ(parts[0], "a")); + REQUIRE(sequ(parts[1], "b")); + REQUIRE(sequ(parts[2], "c")); + for (int i = 0; i < asize(parts); ++i) sfree(parts[i]); + afree(parts); + + // Split with no delimiter found. + parts = ssplit("nosplit", '.'); + REQUIRE(asize(parts) == 1); + REQUIRE(sequ(parts[0], "nosplit")); + for (int i = 0; i < asize(parts); ++i) sfree(parts[i]); + afree(parts); + + // Split many segments. + const char* expected[] = { "split", "here", "in", "a", "loop" }; + parts = ssplit("split.here.in.a.loop", '.'); + REQUIRE(asize(parts) == 5); + for (int i = 0; i < 5; ++i) { + REQUIRE(sequ(parts[i], expected[i])); + sfree(parts[i]); + } + afree(parts); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Strings - conversions. + +TEST_CASE(test_ckit_string_conversions) +{ + REQUIRE(stoint("42") == 42); + REQUIRE(stoint("-7") == -7); + REQUIRE(stouint("100") == 100); + REQUIRE(stofloat("3.14") > 3.13f && stofloat("3.14") < 3.15f); + REQUIRE(stodouble("2.718") > 2.717 && stodouble("2.718") < 2.719); + REQUIRE(stobool("true")); + REQUIRE(!stobool("false")); + + // Hex. + REQUIRE(stohex("0xbaadf00d") == 0xbaadf00d); + REQUIRE(stohex("#baadf0") == 0xbaadf0FF); + REQUIRE(stohex("baadf0") == 0xbaadf0FF); + return true; +} + +TEST_CASE(test_ckit_string_from_primitives) +{ + char* s = NULL; + sint(s, 42); + REQUIRE(sequ(s, "42")); + sfree(s); + + sfloat(s, 3.0); + REQUIRE(sprefix(s, "3.0")); + sfree(s); + + sbool(s, 1); + REQUIRE(sequ(s, "true")); + sfree(s); + + sbool(s, 0); + REQUIRE(sequ(s, "false")); + sfree(s); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Strings - UTF-8. + +TEST_CASE(test_ckit_string_utf8) +{ + // ASCII. + char* s = NULL; + sappend_UTF8(s, 'A'); + REQUIRE(slen(s) == 1); + REQUIRE(s[0] == 'A'); + + // 2-byte: Latin Small Letter E With Acute (U+00E9). + sappend_UTF8(s, 0x00E9); + REQUIRE(slen(s) == 3); + + // Decode back. + int cp; + const char* p = decode_UTF8(s, &cp); + REQUIRE(cp == 'A'); + p = decode_UTF8(p, &cp); + REQUIRE(cp == 0x00E9); + (void)p; + + sfree(s); + + // 3-byte: Euro Sign (U+20AC). + s = NULL; + sappend_UTF8(s, 0x20AC); + REQUIRE(slen(s) == 3); + decode_UTF8(s, &cp); + REQUIRE(cp == 0x20AC); + sfree(s); + + // 4-byte: Grinning Face (U+1F600). + s = NULL; + sappend_UTF8(s, 0x1F600); + REQUIRE(slen(s) == 4); + decode_UTF8(s, &cp); + REQUIRE(cp == 0x1F600); + sfree(s); + + // Invalid codepoint -> replacement character. + s = NULL; + sappend_UTF8(s, 0x200000); + decode_UTF8(s, &cp); + REQUIRE(cp == 0xFFFD); + sfree(s); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Strings - hash. + +TEST_CASE(test_ckit_string_hash) +{ + char* a = NULL; + sset(a, "hello"); + char* b = NULL; + sset(b, "hello"); + char* c = NULL; + sset(c, "world"); + REQUIRE(shash(a) == shash(b)); + REQUIRE(shash(a) != shash(c)); + sfree(a); + sfree(b); + sfree(c); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Path utilities. + +TEST_CASE(test_ckit_path_fname) +{ + char* s; + + s = spfname("../root/file.txt"); + REQUIRE(sequ(s, "file.txt")); + sfree(s); + + s = spfname("./file.txt"); + REQUIRE(sequ(s, "file.txt")); + sfree(s); + + s = spfname("file.txt"); + REQUIRE(sequ(s, "file.txt")); + sfree(s); + + s = spfname("file"); + REQUIRE(sequ(s, "file")); + sfree(s); + + s = spfname("/.txt"); + REQUIRE(sequ(s, ".txt")); + sfree(s); + + s = spfname("/root/"); + REQUIRE(sequ(s, NULL)); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_path_fname_no_ext) +{ + char* s; + + s = spfname_no_ext("../root/file.txt"); + REQUIRE(sequ(s, "file")); + sfree(s); + + s = spfname_no_ext("file.txt"); + REQUIRE(sequ(s, "file")); + sfree(s); + + s = spfname_no_ext("/file"); + REQUIRE(sequ(s, "file")); + sfree(s); + + s = spfname_no_ext("/root/"); + REQUIRE(sequ(s, NULL)); + sfree(s); + + s = spfname_no_ext("/.txt"); + REQUIRE(sequ(s, NULL)); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_path_ext) +{ + char* s; + + s = spext("../root/file.txt"); + REQUIRE(sequ(s, ".txt")); + sfree(s); + + s = spext("../root/file"); + REQUIRE(sequ(s, NULL)); + sfree(s); + + REQUIRE(spext_equ("file.txt", ".txt")); + REQUIRE(!spext_equ("file.txt", ".png")); + REQUIRE(!spext_equ("file", ".txt")); + + return true; +} + +TEST_CASE(test_ckit_path_pop) +{ + char* s; + + s = sppop("../root/file.txt"); + REQUIRE(sequ(s, "../root")); + sfree(s); + + s = sppop("../root"); + REQUIRE(sequ(s, "..")); + sfree(s); + + s = sppop(".."); + REQUIRE(sequ(s, "/")); + sfree(s); + + s = sppop("/folder/file.txt"); + REQUIRE(sequ(s, "/folder")); + sfree(s); + + s = sppop("/folder"); + REQUIRE(sequ(s, "/")); + sfree(s); + + // sppopn. + s = sppopn("/a/b/c/d", 2); + REQUIRE(sequ(s, "/a/b")); + sfree(s); + + s = sppopn("/a/b", 10); + REQUIRE(sequ(s, "/")); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_path_compact) +{ + char* s; + + s = spcompact("/real_long_file_name.txt", 15); + REQUIRE(sequ(s, "/real_long_f...")); + sfree(s); + + s = spcompact("/folder/to/file.txt", 17); + REQUIRE(sequ(s, "/fold.../file.txt")); + sfree(s); + + // Short path that fits. + s = spcompact("/a.txt", 20); + REQUIRE(sequ(s, "/a.txt")); + sfree(s); + + // n too small. + s = spcompact("/a.txt", 5); + REQUIRE(s == NULL); + + return true; +} + +TEST_CASE(test_ckit_path_dir_of) +{ + char* s; + + s = spdir_of("/example/a/b/c/file.txt"); + REQUIRE(sequ(s, "/c")); + sfree(s); + + s = spdir_of("/example/file.txt"); + REQUIRE(sequ(s, "/example")); + sfree(s); + + s = spdir_of("/file.txt"); + REQUIRE(sequ(s, "/")); + sfree(s); + + s = spdir_of("../file.txt"); + REQUIRE(sequ(s, "..")); + sfree(s); + + s = spdir_of("./file.txt"); + REQUIRE(sequ(s, "/")); + sfree(s); + + REQUIRE(spdir_of("..") == NULL); + REQUIRE(spdir_of("../") == NULL); + REQUIRE(spdir_of("./") == NULL); + REQUIRE(spdir_of(".") == NULL); + REQUIRE(spdir_of("/") == NULL); + + return true; +} + +TEST_CASE(test_ckit_path_top_of) +{ + char* s; + + s = sptop_of("/example/a/b/c/file.txt"); + REQUIRE(sequ(s, "/example")); + sfree(s); + + s = sptop_of("/example/file.txt"); + REQUIRE(sequ(s, "/example")); + sfree(s); + + s = sptop_of("/file.txt"); + REQUIRE(sequ(s, "/")); + sfree(s); + + s = sptop_of("/a"); + REQUIRE(sequ(s, "/")); + sfree(s); + + s = sptop_of("/"); + REQUIRE(sequ(s, "/")); + sfree(s); + + REQUIRE(sptop_of(".") == NULL); + REQUIRE(sptop_of("..") == NULL); + + return true; +} + +TEST_CASE(test_ckit_path_norm) +{ + char* s; + + s = spnorm("C:\\Users\\foo"); + REQUIRE(sequ(s, "C:/Users/foo")); + sfree(s); + + s = spnorm("/first.last/.hidden"); + REQUIRE(sequ(s, "/first.last/.hidden")); + sfree(s); + + s = spnorm("C:\\Users\\..\\foo"); + REQUIRE(sequ(s, "C:/foo")); + sfree(s); + + // Unix relative path gets leading /. + s = spnorm("a/b/c"); + REQUIRE(sequ(s, "/a/b/c")); + sfree(s); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Map - typed. + +TEST_CASE(test_ckit_map_int) +{ + CK_MAP(int) m = NULL; + map_set(m, 1, 100); + map_set(m, 2, 200); + map_set(m, 3, 300); + REQUIRE(map_get(m, 1) == 100); + REQUIRE(map_get(m, 2) == 200); + REQUIRE(map_get(m, 3) == 300); + REQUIRE(map_get(m, 99) == 0); + REQUIRE(map_has(m, 1)); + REQUIRE(map_has(m, 2)); + REQUIRE(map_has(m, 3)); + REQUIRE(!map_has(m, 99)); + REQUIRE(map_size(m) == 3); + map_free(m); + REQUIRE(m == NULL); + return true; +} + +TEST_CASE(test_ckit_map_get_ptr) +{ + CK_MAP(int) m = NULL; + map_set(m, 10, 42); + int* ptr = map_get_ptr(m, 10); + REQUIRE(ptr != NULL); + REQUIRE(*ptr == 42); + REQUIRE(map_get_ptr(m, 99) == NULL); + map_free(m); + return true; +} + +TEST_CASE(test_ckit_map_update) +{ + CK_MAP(int) m = NULL; + map_set(m, 1, 100); + REQUIRE(map_get(m, 1) == 100); + map_set(m, 1, 999); + REQUIRE(map_get(m, 1) == 999); + REQUIRE(map_size(m) == 1); + map_free(m); + return true; +} + +TEST_CASE(test_ckit_map_del) +{ + CK_MAP(int) m = NULL; + map_set(m, 1, 100); + map_set(m, 2, 200); + map_set(m, 3, 300); + REQUIRE(map_del(m, 2)); + REQUIRE(!map_has(m, 2)); + REQUIRE(map_size(m) == 2); + REQUIRE(map_get(m, 1) == 100); + REQUIRE(map_get(m, 3) == 300); + + // Delete non-existent. + REQUIRE(!map_del(m, 99)); + map_free(m); + return true; +} + +TEST_CASE(test_ckit_map_clear) +{ + CK_MAP(int) m = NULL; + for (int i = 0; i < 10; ++i) map_set(m, i, i * 10); + REQUIRE(map_size(m) == 10); + map_clear(m); + REQUIRE(map_size(m) == 0); + REQUIRE(!map_has(m, 0)); + map_free(m); + return true; +} + +TEST_CASE(test_ckit_map_iteration) +{ + CK_MAP(int) m = NULL; + map_set(m, 10, 100); + map_set(m, 20, 200); + map_set(m, 30, 300); + int sum_keys = 0; + int sum_vals = 0; + for (int i = 0; i < map_size(m); ++i) { + sum_keys += (int)map_keys(m)[i]; + sum_vals += map_items(m)[i]; + } + REQUIRE(sum_keys == 60); + REQUIRE(sum_vals == 600); + map_free(m); + return true; +} + +TEST_CASE(test_ckit_map_large) +{ + CK_MAP(int) m = NULL; + for (int i = 0; i < 1000; ++i) { + map_set(m, i, i * 3); + } + REQUIRE(map_size(m) == 1000); + for (int i = 0; i < 1000; ++i) { + REQUIRE(map_get(m, i) == i * 3); + } + // Delete every other. + for (int i = 0; i < 1000; i += 2) { + map_del(m, i); + } + REQUIRE(map_size(m) == 500); + for (int i = 1; i < 1000; i += 2) { + REQUIRE(map_has(m, i)); + REQUIRE(map_get(m, i) == i * 3); + } + map_free(m); + return true; +} + +typedef struct +{ + float x; + float y; +} TestVec2; + +TEST_CASE(test_ckit_map_struct_values) +{ + CK_MAP(TestVec2) m = NULL; + map_set(m, 0, ((TestVec2){ 1.0f, 2.0f })); + map_set(m, 1, ((TestVec2){ 3.0f, 4.0f })); + map_set(m, 2, ((TestVec2){ -1.0f, -2.0f })); + TestVec2 v = map_get(m, 0); + REQUIRE(v.x == 1.0f && v.y == 2.0f); + v = map_get(m, 1); + REQUIRE(v.x == 3.0f && v.y == 4.0f); + v = map_get(m, 2); + REQUIRE(v.x == -1.0f && v.y == -2.0f); + + // Not found -> zero. + v = map_get(m, 99); + REQUIRE(v.x == 0.0f && v.y == 0.0f); + + // Get ptr. + TestVec2* p = map_get_ptr(m, 1); + REQUIRE(p && p->x == 3.0f && p->y == 4.0f); + REQUIRE(map_get_ptr(m, 99) == NULL); + + map_free(m); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Map - uint64_t convenience. + +TEST_CASE(test_ckit_map_u64_convenience) +{ + CK_MAP(uint64_t) m = NULL; + map_add(m, 10, 42); + map_add(m, 20, 84); + map_add(m, 30, 126); + REQUIRE(map_find(m, 10) == 42); + REQUIRE(map_find(m, 20) == 84); + REQUIRE(map_find(m, 30) == 126); + REQUIRE(map_find(m, 99) == 0); + map_free(m); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Map - swap and sort. + +TEST_CASE(test_ckit_map_swap) +{ + CK_MAP(int) m = NULL; + map_set(m, 1, 100); + map_set(m, 2, 200); + map_set(m, 3, 300); + // Swap first and last. + map_swap(m, 0, 2); + // Values should be swapped in dense array but lookups still work. + REQUIRE(map_get(m, 1) == 100); + REQUIRE(map_get(m, 2) == 200); + REQUIRE(map_get(m, 3) == 300); + map_free(m); + return true; +} + +TEST_CASE(test_ckit_map_ssort) +{ + CK_MAP(int) m = NULL; + map_set(m, sintern("eee"), 4); + map_set(m, sintern("ddd"), 3); + map_set(m, sintern("bbb"), 1); + map_set(m, sintern("ccc"), 2); + map_set(m, sintern("aaa"), 0); + map_ssort(m, 1); + // After sort, dense array should be in lexicographic order. + for (int i = 0; i < map_size(m) - 1; ++i) { + const char* a = (const char*)map_keys(m)[i]; + const char* b = (const char*)map_keys(m)[i + 1]; + REQUIRE(strcmp(a, b) < 0); + } + // Lookups still work. + REQUIRE(map_get(m, sintern("aaa")) == 0); + REQUIRE(map_get(m, sintern("eee")) == 4); + map_free(m); + sintern_nuke(); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// String interning. + +TEST_CASE(test_ckit_intern_basic) +{ + const char* a = sintern("hello"); + const char* b = sintern("hello"); + const char* c = sintern("world"); + REQUIRE(a == b); + REQUIRE(a != c); + REQUIRE(strcmp(a, "hello") == 0); + REQUIRE(strcmp(c, "world") == 0); + sintern_nuke(); + return true; +} + +TEST_CASE(test_ckit_intern_range) +{ + const char* full = "hello world"; + const char* a = sintern_range(full, full + 5); + const char* b = sintern("hello"); + REQUIRE(a == b); + sintern_nuke(); + return true; +} + +TEST_CASE(test_ckit_intern_constructed) +{ + // Intern strings built from different sources. + char buf[64]; + strcpy(buf, "he"); + strcat(buf, "llo"); + const char* a = sintern("hello"); + const char* b = sintern(buf); + REQUIRE(a == b); + sintern_nuke(); + return true; +} + +TEST_CASE(test_ckit_intern_as_map_key) +{ + CK_MAP(int) m = NULL; + const char* k1 = sintern("x"); + const char* k2 = sintern("y"); + map_set(m, k1, 10); + map_set(m, k2, 20); + REQUIRE(map_get(m, sintern("x")) == 10); + REQUIRE(map_get(m, sintern("y")) == 20); + map_free(m); + sintern_nuke(); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// String edge cases and bug coverage. + +TEST_CASE(test_ckit_string_slast_empty) +{ + // slast on single char. + char* s = smake("x"); + REQUIRE(slast(s) == 'x'); + sfree(s); + + // slast on multi-char string. + s = smake("abc"); + REQUIRE(slast(s) == 'c'); + sfree(s); + + // slast on NULL returns '\0'. + char* null_str = NULL; + char c = slast(null_str); + REQUIRE(c == '\0'); + + return true; +} + +TEST_CASE(test_ckit_string_sfirst_empty) +{ + // sfirst on empty string. + char* s = NULL; + sset(s, ""); + char c = sfirst(s); + REQUIRE(c == '\0'); + sfree(s); + + // sfirst on NULL returns '\0'. + char* null_str = NULL; + c = sfirst(null_str); + REQUIRE(c == '\0'); + + // sfirst on normal string. + s = smake("abc"); + REQUIRE(sfirst(s) == 'a'); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_string_scontains_edge) +{ + // scontains with empty substring matches. + char* s = smake("hello"); + REQUIRE(scontains(s, "")); + sfree(s); + + // scontains with matching substring. + s = smake("hello world"); + REQUIRE(scontains(s, "world")); + REQUIRE(!scontains(s, "xyz")); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_string_sappend_null) +{ + // sappend with empty string b. + char* s = smake("hello"); + sappend(s, ""); + REQUIRE(sequ(s, "hello")); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_string_split_once_edge_cases) +{ + // Split when delimiter is at the end. + char* s = NULL; + sset(s, "abc,"); + char* left = ssplit_once(s, ','); + // Should return "abc" and s should be "". + REQUIRE(sequ(left, "abc")); + REQUIRE(sequ(s, "")); + sfree(left); + sfree(s); + + // Split when delimiter is at the start. + s = NULL; + sset(s, ",abc"); + left = ssplit_once(s, ','); + REQUIRE(sequ(left, "")); + REQUIRE(sequ(s, "abc")); + sfree(left); + sfree(s); + + // Split when string is just the delimiter. + s = NULL; + sset(s, ","); + left = ssplit_once(s, ','); + REQUIRE(sequ(left, "")); + REQUIRE(sequ(s, "")); + sfree(left); + sfree(s); + + // No delimiter found. + s = NULL; + sset(s, "nodelim"); + left = ssplit_once(s, ','); + REQUIRE(left == NULL); + REQUIRE(sequ(s, "nodelim")); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_string_sdedup_edge_cases) +{ + // Dedup on empty string. + char* s = smake(""); + sdedup(s, '/'); + REQUIRE(sequ(s, "")); + REQUIRE(slen(s) == 0); + sfree(s); + + // Dedup on single character. + s = smake("x"); + sdedup(s, 'x'); + REQUIRE(sequ(s, "x")); + sfree(s); + + // Dedup on all same characters. + s = smake("////"); + sdedup(s, '/'); + REQUIRE(sequ(s, "/")); + sfree(s); + + // Dedup with no duplicates. + s = smake("/a/b/c"); + sdedup(s, '/'); + REQUIRE(sequ(s, "/a/b/c")); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_string_utf8_truncated) +{ + // Truncated 2-byte sequence (starts with 0xC0-0xDF but only 1 byte). + // This tests that decode_UTF8 handles malformed input gracefully. + char truncated2[] = { (char)0xC2, '\0' }; + int cp; + decode_UTF8(truncated2, &cp); + // Should return replacement character for invalid sequence. + REQUIRE(cp == 0xFFFD); + + // Truncated 3-byte sequence. + char truncated3[] = { (char)0xE2, (char)0x82, '\0' }; + decode_UTF8(truncated3, &cp); + REQUIRE(cp == 0xFFFD); + + // Truncated 4-byte sequence. + char truncated4[] = { (char)0xF0, (char)0x9F, (char)0x98, '\0' }; + decode_UTF8(truncated4, &cp); + REQUIRE(cp == 0xFFFD); + + return true; +} + +TEST_CASE(test_ckit_string_stouint_large) +{ + // Test large unsigned values. + REQUIRE(stouint("0") == 0); + REQUIRE(stouint("4294967295") == 4294967295ULL); + + // Test int conversion boundaries. + REQUIRE(stoint("2147483647") == 2147483647); + REQUIRE(stoint("-2147483648") == -2147483648); + + return true; +} + +TEST_CASE(test_ckit_string_trim_all_whitespace) +{ + // Trim string that is all whitespace. + char* s = smake(" \t\n "); + strim(s); + REQUIRE(sequ(s, "")); + REQUIRE(slen(s) == 0); + sfree(s); + + // Left trim all whitespace. + s = smake(" \t "); + sltrim(s); + REQUIRE(sequ(s, "")); + sfree(s); + + // Right trim all whitespace. + s = smake(" \n "); + srtrim(s); + REQUIRE(sequ(s, "")); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_string_erase_edge_cases) +{ + // Erase with count=0. + char* s = smake("hello"); + serase(s, 2, 0); + REQUIRE(sequ(s, "hello")); + sfree(s); + + // Erase entire string. + s = smake("hello"); + serase(s, 0, 100); + REQUIRE(sequ(s, "")); + sfree(s); + + // Erase with negative index that makes count<=0. + s = smake("hello"); + serase(s, -10, 5); + REQUIRE(sequ(s, "hello")); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_string_replace_edge_cases) +{ + // Replace with same length. + char* s = smake("aabbcc"); + sreplace(s, "bb", "xx"); + REQUIRE(sequ(s, "aaxxcc")); + sfree(s); + + // Replace empty string (should do nothing). + s = smake("hello"); + sreplace(s, "", "x"); + // Behavior: empty match at every position would cause issues. + // Most implementations skip empty pattern. + sfree(s); + + // Replace at very beginning. + s = smake("hello"); + sreplace(s, "he", "XX"); + REQUIRE(sequ(s, "XXllo")); + sfree(s); + + // Replace at very end. + s = smake("hello"); + sreplace(s, "lo", "XX"); + REQUIRE(sequ(s, "helXX")); + sfree(s); + + // Multiple consecutive replacements. + s = smake("aaa"); + sreplace(s, "a", "bb"); + REQUIRE(sequ(s, "bbbbbb")); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_string_spush_spop_sequence) +{ + // Build string character by character. + char* s = NULL; + spush(s, 'h'); + spush(s, 'e'); + spush(s, 'l'); + spush(s, 'l'); + spush(s, 'o'); + REQUIRE(sequ(s, "hello")); + REQUIRE(slen(s) == 5); + + // Pop all characters. + spop(s); + REQUIRE(sequ(s, "hell")); + spop(s); + REQUIRE(sequ(s, "hel")); + spopn(s, 3); + REQUIRE(sequ(s, "")); + REQUIRE(slen(s) == 0); + + // Pop from empty should not crash. + spop(s); + REQUIRE(slen(s) == 0); + + sfree(s); + return true; +} + +TEST_CASE(test_ckit_string_prefix_suffix_edge_cases) +{ + // Empty prefix/suffix always matches. + char* s = smake("hello"); + REQUIRE(sprefix(s, "")); + REQUIRE(ssuffix(s, "")); + sfree(s); + + // Prefix/suffix longer than string. + s = smake("hi"); + REQUIRE(!sprefix(s, "hello")); + REQUIRE(!ssuffix(s, "world")); + sfree(s); + + // Exact match. + s = smake("exact"); + REQUIRE(sprefix(s, "exact")); + REQUIRE(ssuffix(s, "exact")); + sfree(s); + + // NULL string. + REQUIRE(!sprefix(NULL, "test")); + REQUIRE(!ssuffix(NULL, "test")); + + return true; +} + +TEST_CASE(test_ckit_string_append_range_edge_cases) +{ + // Append empty range. + char* s = smake("hello"); + const char* empty = ""; + sappend_range(s, empty, empty); + REQUIRE(sequ(s, "hello")); + sfree(s); + + // Append to empty string. + s = smake(""); + const char* world = "world"; + sappend_range(s, world, world + 5); + REQUIRE(sequ(s, "world")); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_string_pad_edge_cases) +{ + // Pad with count=0. + char* s = smake("test"); + slpad(s, 'x', 0); + REQUIRE(sequ(s, "test")); + srpad(s, 'y', 0); + REQUIRE(sequ(s, "test")); + sfree(s); + + // Pad empty string. + s = smake(""); + slpad(s, 'L', 3); + REQUIRE(sequ(s, "LLL")); + sfree(s); + + s = smake(""); + srpad(s, 'R', 3); + REQUIRE(sequ(s, "RRR")); + sfree(s); + + return true; +} + +TEST_CASE(test_ckit_string_ssplit_edge_cases) +{ + // Split empty string. + char** parts = ssplit("", '.'); + REQUIRE(asize(parts) == 1); + REQUIRE(sequ(parts[0], "")); + for (int i = 0; i < asize(parts); ++i) sfree(parts[i]); + afree(parts); + + // Split string with only delimiters. + parts = ssplit("...", '.'); + REQUIRE(asize(parts) == 4); + for (int i = 0; i < asize(parts); ++i) { + REQUIRE(sequ(parts[i], "")); + sfree(parts[i]); + } + afree(parts); + + // Split with delimiter at start and end. + parts = ssplit(".a.b.", '.'); + REQUIRE(asize(parts) == 4); + REQUIRE(sequ(parts[0], "")); + REQUIRE(sequ(parts[1], "a")); + REQUIRE(sequ(parts[2], "b")); + REQUIRE(sequ(parts[3], "")); + for (int i = 0; i < asize(parts); ++i) sfree(parts[i]); + afree(parts); + + return true; +} + +TEST_CASE(test_ckit_intern_empty_string) +{ + // Intern empty string. + const char* a = sintern(""); + const char* b = sintern(""); + REQUIRE(a == b); + REQUIRE(strcmp(a, "") == 0); + sintern_nuke(); + return true; +} + +TEST_CASE(test_ckit_intern_many_strings) +{ + // Intern many unique strings to test hash collisions. + const char* ptrs[100]; + char buf[32]; + for (int i = 0; i < 100; ++i) { + sprintf(buf, "string_%d", i); + ptrs[i] = sintern(buf); + } + + // Verify they're all unique. + for (int i = 0; i < 100; ++i) { + for (int j = i + 1; j < 100; ++j) { + REQUIRE(ptrs[i] != ptrs[j]); + } + } + + // Verify lookup works. + for (int i = 0; i < 100; ++i) { + sprintf(buf, "string_%d", i); + REQUIRE(sintern(buf) == ptrs[i]); + } + + sintern_nuke(); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// FNV-1a hash. + +TEST_CASE(test_ckit_fnv1a) +{ + uint64_t h1 = ck_hash_fnv1a("hello", 5); + uint64_t h2 = ck_hash_fnv1a("hello", 5); + uint64_t h3 = ck_hash_fnv1a("world", 5); + REQUIRE(h1 == h2); + REQUIRE(h1 != h3); + REQUIRE(h1 != 0); + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Suite. + +TEST_SUITE(test_ckit) +{ + // Arrays. + RUN_TEST_CASE(test_ckit_array_push_pop); + RUN_TEST_CASE(test_ckit_array_fit_and_cap); + RUN_TEST_CASE(test_ckit_array_clear); + RUN_TEST_CASE(test_ckit_array_del); + RUN_TEST_CASE(test_ckit_array_end); + RUN_TEST_CASE(test_ckit_array_large); + RUN_TEST_CASE(test_ckit_array_structs); + + // Strings - basic. + RUN_TEST_CASE(test_ckit_string_set_append); + RUN_TEST_CASE(test_ckit_string_push_pop); + RUN_TEST_CASE(test_ckit_string_dup_make); + RUN_TEST_CASE(test_ckit_string_clear); + RUN_TEST_CASE(test_ckit_string_fit); + + // Strings - formatting. + RUN_TEST_CASE(test_ckit_string_fmt); + RUN_TEST_CASE(test_ckit_string_fmt_append); + RUN_TEST_CASE(test_ckit_string_fmt_long); + + // Strings - compare. + RUN_TEST_CASE(test_ckit_string_compare); + + // Strings - search. + RUN_TEST_CASE(test_ckit_string_prefix_suffix); + RUN_TEST_CASE(test_ckit_string_index_of); + RUN_TEST_CASE(test_ckit_string_find); + + // Strings - transform. + RUN_TEST_CASE(test_ckit_string_case); + RUN_TEST_CASE(test_ckit_string_trim); + RUN_TEST_CASE(test_ckit_string_pad); + RUN_TEST_CASE(test_ckit_string_replace); + RUN_TEST_CASE(test_ckit_string_dedup); + RUN_TEST_CASE(test_ckit_string_erase); + RUN_TEST_CASE(test_ckit_string_append_range); + + // Strings - split. + RUN_TEST_CASE(test_ckit_string_split_once); + RUN_TEST_CASE(test_ckit_string_split); + + // Strings - conversions. + RUN_TEST_CASE(test_ckit_string_conversions); + RUN_TEST_CASE(test_ckit_string_from_primitives); + + // Strings - UTF-8. + RUN_TEST_CASE(test_ckit_string_utf8); + + // Strings - hash. + RUN_TEST_CASE(test_ckit_string_hash); + + // Path utilities. + RUN_TEST_CASE(test_ckit_path_fname); + RUN_TEST_CASE(test_ckit_path_fname_no_ext); + RUN_TEST_CASE(test_ckit_path_ext); + RUN_TEST_CASE(test_ckit_path_pop); + RUN_TEST_CASE(test_ckit_path_compact); + RUN_TEST_CASE(test_ckit_path_dir_of); + RUN_TEST_CASE(test_ckit_path_top_of); + RUN_TEST_CASE(test_ckit_path_norm); + + // Map - typed. + RUN_TEST_CASE(test_ckit_map_int); + RUN_TEST_CASE(test_ckit_map_get_ptr); + RUN_TEST_CASE(test_ckit_map_update); + RUN_TEST_CASE(test_ckit_map_del); + RUN_TEST_CASE(test_ckit_map_clear); + RUN_TEST_CASE(test_ckit_map_iteration); + RUN_TEST_CASE(test_ckit_map_large); + RUN_TEST_CASE(test_ckit_map_struct_values); + + // Map - uint64_t convenience. + RUN_TEST_CASE(test_ckit_map_u64_convenience); + + // Map - swap and sort. + RUN_TEST_CASE(test_ckit_map_swap); + RUN_TEST_CASE(test_ckit_map_ssort); + + // String interning. + RUN_TEST_CASE(test_ckit_intern_basic); + RUN_TEST_CASE(test_ckit_intern_range); + RUN_TEST_CASE(test_ckit_intern_constructed); + RUN_TEST_CASE(test_ckit_intern_as_map_key); + RUN_TEST_CASE(test_ckit_intern_empty_string); + RUN_TEST_CASE(test_ckit_intern_many_strings); + + // String edge cases and bug coverage. + RUN_TEST_CASE(test_ckit_string_slast_empty); + RUN_TEST_CASE(test_ckit_string_sfirst_empty); + RUN_TEST_CASE(test_ckit_string_scontains_edge); + RUN_TEST_CASE(test_ckit_string_sappend_null); + RUN_TEST_CASE(test_ckit_string_split_once_edge_cases); + RUN_TEST_CASE(test_ckit_string_sdedup_edge_cases); + RUN_TEST_CASE(test_ckit_string_utf8_truncated); + RUN_TEST_CASE(test_ckit_string_stouint_large); + RUN_TEST_CASE(test_ckit_string_trim_all_whitespace); + RUN_TEST_CASE(test_ckit_string_erase_edge_cases); + RUN_TEST_CASE(test_ckit_string_replace_edge_cases); + RUN_TEST_CASE(test_ckit_string_spush_spop_sequence); + RUN_TEST_CASE(test_ckit_string_prefix_suffix_edge_cases); + RUN_TEST_CASE(test_ckit_string_append_range_edge_cases); + RUN_TEST_CASE(test_ckit_string_pad_edge_cases); + RUN_TEST_CASE(test_ckit_string_ssplit_edge_cases); + + // Hash. + RUN_TEST_CASE(test_ckit_fnv1a); +} diff --git a/test/test_color.cpp b/test/test_color.cpp index 2b0d6a9e8..2b9abf588 100644 --- a/test/test_color.cpp +++ b/test/test_color.cpp @@ -11,6 +11,53 @@ #include using namespace Cute; +//-------------------------------------------------------------------------------------------------- +// CF_Color constructors. + +TEST_CASE(test_make_color_rgb_f) +{ + CF_Color c = cf_make_color_rgb_f(0.5f, 0.25f, 0.75f); + REQUIRE(c.r == 0.5f); + REQUIRE(c.g == 0.25f); + REQUIRE(c.b == 0.75f); + REQUIRE(c.a == 1.0f); + + return true; +} + +TEST_CASE(test_make_color_rgba_f) +{ + CF_Color c = cf_make_color_rgba_f(0.1f, 0.2f, 0.3f, 0.4f); + REQUIRE(c.r == 0.1f); + REQUIRE(c.g == 0.2f); + REQUIRE(c.b == 0.3f); + REQUIRE(c.a == 0.4f); + + return true; +} + +TEST_CASE(test_make_color_rgb) +{ + CF_Color c = cf_make_color_rgb(255, 128, 0); + REQUIRE(c.r == 255.0f / 255.0f); + REQUIRE(c.g == 128.0f / 255.0f); + REQUIRE(c.b == 0.0f / 255.0f); + REQUIRE(c.a == 1.0f); + + return true; +} + +TEST_CASE(test_make_color_rgba) +{ + CF_Color c = cf_make_color_rgba(255, 128, 64, 32); + REQUIRE(c.r == 255.0f / 255.0f); + REQUIRE(c.g == 128.0f / 255.0f); + REQUIRE(c.b == 64.0f / 255.0f); + REQUIRE(c.a == 32.0f / 255.0f); + + return true; +} + TEST_CASE(test_make_color_hex) { CF_Color c = cf_make_color_hex(0xFFC41F); @@ -19,18 +66,42 @@ TEST_CASE(test_make_color_hex) REQUIRE(c.b == 31.0f / 255.0f); REQUIRE(c.a == 255.0f / 255.0f); + c = cf_make_color_hex(0xFF0000); + REQUIRE(c.r == 1.0f); + REQUIRE(c.g == 0.0f); + REQUIRE(c.b == 0.0f); + + c = cf_make_color_hex(0x00FF00); + REQUIRE(c.r == 0.0f); + REQUIRE(c.g == 1.0f); + REQUIRE(c.b == 0.0f); + + c = cf_make_color_hex(0x0000FF); + REQUIRE(c.r == 0.0f); + REQUIRE(c.g == 0.0f); + REQUIRE(c.b == 1.0f); + return true; } -TEST_CASE(test_make_color_hex_string) +TEST_CASE(test_make_color_hex2) { - CF_Color c = cf_make_color_hex_string("#ffc41f"); + CF_Color c = cf_make_color_hex2(0xFFC41F, 0x80); REQUIRE(c.r == 255.0f / 255.0f); REQUIRE(c.g == 196.0f / 255.0f); REQUIRE(c.b == 31.0f / 255.0f); - REQUIRE(c.a == 255.0f / 255.0f); + REQUIRE(c.a == 128.0f / 255.0f); + + c = cf_make_color_hex2(0xFF0000, 0x00); + REQUIRE(c.r == 1.0f); + REQUIRE(c.a == 0.0f); + + return true; +} - c = cf_make_color_hex_string("#ffc41f"); +TEST_CASE(test_make_color_hex_string) +{ + CF_Color c = cf_make_color_hex_string("#ffc41f"); REQUIRE(c.r == 255.0f / 255.0f); REQUIRE(c.g == 196.0f / 255.0f); REQUIRE(c.b == 31.0f / 255.0f); @@ -41,7 +112,7 @@ TEST_CASE(test_make_color_hex_string) REQUIRE(c.g == 196.0f / 255.0f); REQUIRE(c.b == 31.0f / 255.0f); REQUIRE(c.a == 24.0f / 255.0f); - + c = cf_make_color_hex_string("0xffc41f"); REQUIRE(c.r == 255.0f / 255.0f); REQUIRE(c.g == 196.0f / 255.0f); @@ -60,11 +131,980 @@ TEST_CASE(test_make_color_hex_string) REQUIRE(c.b == 31.0f / 255.0f); REQUIRE(c.a == 24.0f / 255.0f); + // Red via string should be red. + c = cf_make_color_hex_string("#FF0000"); + REQUIRE(c.r == 1.0f); + REQUIRE(c.g == 0.0f); + REQUIRE(c.b == 0.0f); + REQUIRE(c.a == 1.0f); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +// CF_Pixel constructors. + +TEST_CASE(test_make_pixel_rgb_f) +{ + CF_Pixel p = cf_make_pixel_rgb_f(1.0f, 0.5f, 0.0f); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 127); + REQUIRE(p.colors.b == 0); + REQUIRE(p.colors.a == 255); + + return true; +} + +TEST_CASE(test_make_pixel_rgba_f) +{ + CF_Pixel p = cf_make_pixel_rgba_f(1.0f, 0.5f, 0.0f, 0.25f); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 127); + REQUIRE(p.colors.b == 0); + REQUIRE(p.colors.a == 63); + + return true; +} + +TEST_CASE(test_make_pixel_rgb) +{ + CF_Pixel p = cf_make_pixel_rgb(100, 200, 50); + REQUIRE(p.colors.r == 100); + REQUIRE(p.colors.g == 200); + REQUIRE(p.colors.b == 50); + REQUIRE(p.colors.a == 255); + + return true; +} + +TEST_CASE(test_make_pixel_rgba) +{ + CF_Pixel p = cf_make_pixel_rgba(100, 200, 50, 128); + REQUIRE(p.colors.r == 100); + REQUIRE(p.colors.g == 200); + REQUIRE(p.colors.b == 50); + REQUIRE(p.colors.a == 128); + + return true; +} + +TEST_CASE(test_make_pixel_hex) +{ + CF_Pixel p = cf_make_pixel_hex(0xFFC41F); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 196); + REQUIRE(p.colors.b == 31); + REQUIRE(p.colors.a == 255); + + // Red + p = cf_make_pixel_hex(0xFF0000); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 0); + REQUIRE(p.colors.b == 0); + REQUIRE(p.colors.a == 255); + + // Green + p = cf_make_pixel_hex(0x00FF00); + REQUIRE(p.colors.r == 0); + REQUIRE(p.colors.g == 255); + REQUIRE(p.colors.b == 0); + REQUIRE(p.colors.a == 255); + + // Blue + p = cf_make_pixel_hex(0x0000FF); + REQUIRE(p.colors.r == 0); + REQUIRE(p.colors.g == 0); + REQUIRE(p.colors.b == 255); + REQUIRE(p.colors.a == 255); + + // Black + p = cf_make_pixel_hex(0x000000); + REQUIRE(p.colors.r == 0); + REQUIRE(p.colors.g == 0); + REQUIRE(p.colors.b == 0); + REQUIRE(p.colors.a == 255); + + return true; +} + +TEST_CASE(test_make_pixel_hex2) +{ + CF_Pixel p = cf_make_pixel_hex2(0xFFC41F, 0x18); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 196); + REQUIRE(p.colors.b == 31); + REQUIRE(p.colors.a == 24); + + p = cf_make_pixel_hex2(0xFF0000, 0x00); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 0); + REQUIRE(p.colors.b == 0); + REQUIRE(p.colors.a == 0); + + return true; +} + +TEST_CASE(test_make_pixel_hex_string) +{ + // 6-char with implicit alpha=255. + CF_Pixel p = cf_make_pixel_hex_string("#ffc41f"); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 196); + REQUIRE(p.colors.b == 31); + REQUIRE(p.colors.a == 255); + + // 8-char with explicit alpha. + p = cf_make_pixel_hex_string("#ffc41f18"); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 196); + REQUIRE(p.colors.b == 31); + REQUIRE(p.colors.a == 24); + + // 0x prefix. + p = cf_make_pixel_hex_string("0xffc41f"); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 196); + REQUIRE(p.colors.b == 31); + REQUIRE(p.colors.a == 255); + + p = cf_make_pixel_hex_string("0xffc41f18"); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 196); + REQUIRE(p.colors.b == 31); + REQUIRE(p.colors.a == 24); + + // Red via string should be red. + p = cf_make_pixel_hex_string("#FF0000"); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 0); + REQUIRE(p.colors.b == 0); + REQUIRE(p.colors.a == 255); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Color arithmetic. + +TEST_CASE(test_mul_color) +{ + CF_Color c = cf_make_color_rgba_f(0.5f, 0.4f, 0.3f, 0.2f); + CF_Color r = cf_mul_color(c, 2.0f); + REQUIRE(r.r == 1.0f); + REQUIRE(r.g == 0.8f); + REQUIRE(r.b == 0.6f); + REQUIRE(r.a == 0.4f); + + return true; +} + +TEST_CASE(test_mul_color2) +{ + CF_Color a = cf_make_color_rgba_f(0.5f, 0.4f, 0.3f, 1.0f); + CF_Color b = cf_make_color_rgba_f(0.5f, 0.5f, 0.5f, 0.5f); + CF_Color r = cf_mul_color2(a, b); + REQUIRE(r.r == 0.25f); + REQUIRE(r.g == 0.2f); + REQUIRE(r.b == 0.15f); + REQUIRE(r.a == 0.5f); + + return true; +} + +TEST_CASE(test_div_color) +{ + CF_Color c = cf_make_color_rgba_f(0.5f, 0.4f, 0.3f, 0.2f); + CF_Color r = cf_div_color(c, 2.0f); + REQUIRE(r.r == 0.25f); + REQUIRE(r.g == 0.2f); + REQUIRE(r.b == 0.15f); + REQUIRE(r.a == 0.1f); + + return true; +} + +TEST_CASE(test_add_color) +{ + CF_Color a = cf_make_color_rgba_f(0.1f, 0.2f, 0.3f, 0.4f); + CF_Color b = cf_make_color_rgba_f(0.5f, 0.3f, 0.2f, 0.1f); + CF_Color r = cf_add_color(a, b); + REQUIRE(cf_abs(r.r - 0.6f) < 1e-6f); + REQUIRE(cf_abs(r.g - 0.5f) < 1e-6f); + REQUIRE(cf_abs(r.b - 0.5f) < 1e-6f); + REQUIRE(cf_abs(r.a - 0.5f) < 1e-6f); + + return true; +} + +TEST_CASE(test_sub_color) +{ + CF_Color a = cf_make_color_rgba_f(0.6f, 0.5f, 0.5f, 0.5f); + CF_Color b = cf_make_color_rgba_f(0.1f, 0.2f, 0.3f, 0.4f); + CF_Color r = cf_sub_color(a, b); + REQUIRE(cf_abs(r.r - 0.5f) < 1e-6f); + REQUIRE(cf_abs(r.g - 0.3f) < 1e-6f); + REQUIRE(cf_abs(r.b - 0.2f) < 1e-6f); + REQUIRE(cf_abs(r.a - 0.1f) < 1e-6f); + + return true; +} + +TEST_CASE(test_abs_color) +{ + CF_Color c = cf_make_color_rgba_f(-0.5f, 0.3f, -0.1f, 1.0f); + CF_Color r = cf_abs_color(c); + REQUIRE(r.r == 0.5f); + REQUIRE(r.g == 0.3f); + REQUIRE(r.b == 0.1f); + REQUIRE(r.a == 1.0f); + + return true; +} + +TEST_CASE(test_splat_color) +{ + CF_Color c = cf_splat_color(0.7f); + REQUIRE(c.r == 0.7f); + REQUIRE(c.g == 0.7f); + REQUIRE(c.b == 0.7f); + REQUIRE(c.a == 0.7f); + + return true; +} + +TEST_CASE(test_clamp_color01) +{ + CF_Color c = cf_make_color_rgba_f(-0.5f, 1.5f, 0.5f, 2.0f); + CF_Color r = cf_clamp_color01(c); + REQUIRE(r.r == 0.0f); + REQUIRE(r.g == 1.0f); + REQUIRE(r.b == 0.5f); + REQUIRE(r.a == 1.0f); + + return true; +} + +TEST_CASE(test_color_lerp) +{ + CF_Color a = cf_make_color_rgba_f(0.0f, 0.0f, 0.0f, 1.0f); + CF_Color b = cf_make_color_rgba_f(1.0f, 1.0f, 1.0f, 1.0f); + CF_Color r = cf_color_lerp(a, b, 0.5f); + REQUIRE(cf_abs(r.r - 0.5f) < 1e-6f); + REQUIRE(cf_abs(r.g - 0.5f) < 1e-6f); + REQUIRE(cf_abs(r.b - 0.5f) < 1e-6f); + REQUIRE(cf_abs(r.a - 1.0f) < 1e-6f); + + return true; +} + +TEST_CASE(test_color_premultiply) +{ + CF_Color c = cf_make_color_rgba_f(1.0f, 0.5f, 0.25f, 0.5f); + CF_Color r = cf_color_premultiply(c); + REQUIRE(cf_abs(r.r - 0.5f) < 1e-6f); + REQUIRE(cf_abs(r.g - 0.25f) < 1e-6f); + REQUIRE(cf_abs(r.b - 0.125f) < 1e-6f); + REQUIRE(r.a == 0.5f); + + return true; +} + +TEST_CASE(test_fract_color) +{ + CF_Color c = cf_make_color_rgba_f(1.3f, 2.7f, 0.5f, 3.1f); + CF_Color r = cf_fract_color(c); + REQUIRE(cf_abs(r.r - 0.3f) < 1e-5f); + REQUIRE(cf_abs(r.g - 0.7f) < 1e-5f); + REQUIRE(cf_abs(r.b - 0.5f) < 1e-5f); + REQUIRE(cf_abs(r.a - 0.1f) < 1e-4f); + + return true; +} + +TEST_CASE(test_mod_color) +{ + CF_Color c = cf_make_color_rgba_f(0.7f, 1.5f, 0.3f, 2.0f); + CF_Color r = cf_mod_color(c, 0.5f); + REQUIRE(cf_abs(r.r - 0.2f) < 1e-5f); + REQUIRE(cf_abs(r.g - 0.0f) < 1e-5f); + REQUIRE(cf_abs(r.b - 0.3f) < 1e-5f); + REQUIRE(cf_abs(r.a - 0.0f) < 1e-5f); + + return true; +} + +TEST_CASE(test_clamp_color) +{ + CF_Color c = cf_make_color_rgba_f(-0.5f, 0.5f, 1.5f, 0.3f); + CF_Color lo = cf_make_color_rgba_f(0.0f, 0.0f, 0.0f, 0.0f); + CF_Color hi = cf_make_color_rgba_f(1.0f, 0.4f, 1.0f, 1.0f); + CF_Color r = cf_clamp_color(c, lo, hi); + REQUIRE(r.r == 0.0f); + REQUIRE(r.g == 0.4f); + REQUIRE(r.b == 1.0f); + REQUIRE(r.a == 0.3f); + + return true; +} + +TEST_CASE(test_hue) +{ + // Applying red hue to green should produce a reddish color with green's luminance/saturation. + CF_Color base = cf_make_color_rgb_f(0.0f, 1.0f, 0.0f); + CF_Color tint = cf_make_color_rgb_f(1.0f, 0.0f, 0.0f); + CF_Color r = cf_hue(base, tint); + // Result should be reddish (r > g, r > b). + REQUIRE(r.r > r.g); + REQUIRE(r.r > r.b); + // Alpha from base is preserved. + base.a = 0.42f; + r = cf_hue(base, tint); + REQUIRE(r.a == 0.42f); + + return true; +} + +TEST_CASE(test_overlay) +{ + // base <= 0.5: 2*base*blend + REQUIRE(cf_abs(cf_overlay(0.25f, 0.5f) - 0.25f) < 1e-5f); + // base > 0.5: 1 - 2*(1-base)*(1-blend) + REQUIRE(cf_abs(cf_overlay(0.75f, 0.5f) - 0.75f) < 1e-5f); + // Extremes. + REQUIRE(cf_abs(cf_overlay(0.0f, 0.5f) - 0.0f) < 1e-5f); + REQUIRE(cf_abs(cf_overlay(1.0f, 0.5f) - 1.0f) < 1e-5f); + + return true; +} + +TEST_CASE(test_overlay_color) +{ + CF_Color base = cf_make_color_rgba_f(0.25f, 0.75f, 0.0f, 0.8f); + CF_Color blend = cf_make_color_rgba_f(0.5f, 0.5f, 0.5f, 1.0f); + CF_Color r = cf_overlay_color(base, blend); + REQUIRE(cf_abs(r.r - cf_overlay(0.25f, 0.5f)) < 1e-5f); + REQUIRE(cf_abs(r.g - cf_overlay(0.75f, 0.5f)) < 1e-5f); + REQUIRE(cf_abs(r.b - cf_overlay(0.0f, 0.5f)) < 1e-5f); + // Alpha comes from base. + REQUIRE(r.a == 0.8f); + + return true; +} + +TEST_CASE(test_softlight) +{ + // blend <= 0.5: base - (1-2*blend)*base*(1-base) + float r1 = cf_softlight(0.5f, 0.25f); + float expected = 0.5f - (1.0f - 2.0f*0.25f)*0.5f*(1.0f - 0.5f); + REQUIRE(cf_abs(r1 - expected) < 1e-5f); + + // blend > 0.5, base > 0.25: base + (2*blend-1)*(sqrt(base) - base) + float r2 = cf_softlight(0.5f, 0.75f); + float expected2 = 0.5f + (2.0f*0.75f - 1.0f)*(CF_SQRTF(0.5f) - 0.5f); + REQUIRE(cf_abs(r2 - expected2) < 1e-5f); + + return true; +} + +TEST_CASE(test_softlight_color) +{ + CF_Color base = cf_make_color_rgba_f(0.5f, 0.5f, 0.5f, 0.9f); + CF_Color blend = cf_make_color_rgba_f(0.25f, 0.75f, 0.5f, 1.0f); + CF_Color r = cf_softlight_color(base, blend); + REQUIRE(cf_abs(r.r - cf_softlight(0.5f, 0.25f)) < 1e-5f); + REQUIRE(cf_abs(r.g - cf_softlight(0.5f, 0.75f)) < 1e-5f); + REQUIRE(cf_abs(r.b - cf_softlight(0.5f, 0.5f)) < 1e-5f); + // Alpha comes from base. + REQUIRE(r.a == 0.9f); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Pixel arithmetic. + +TEST_CASE(test_un8_ops) +{ + // cf_mul_un8: (a*b+0x80) >> 8, then ((t>>8)+t)>>8. + REQUIRE(cf_mul_un8(255, 255) == 255); + REQUIRE(cf_mul_un8(255, 0) == 0); + REQUIRE(cf_mul_un8(0, 255) == 0); + REQUIRE(cf_mul_un8(255, 128) == 128); + REQUIRE(cf_mul_un8(128, 128) == 64); + + // cf_div_un8: (b * 0xFF + a/2) / a. + REQUIRE(cf_div_un8(255, 128) == 128); + REQUIRE(cf_div_un8(255, 255) == 255); + REQUIRE(cf_div_un8(128, 64) == 128); + + // cf_add_un8: add with overflow bit-or trick. + REQUIRE(cf_add_un8(100, 50) == 150); + REQUIRE(cf_add_un8(0, 0) == 0); + REQUIRE(cf_add_un8(255, 0) == 255); + + // cf_sub_un8: clamped to 0. + REQUIRE(cf_sub_un8(200, 50) == 150); + REQUIRE(cf_sub_un8(50, 200) == 0); // clamped + + return true; +} + +TEST_CASE(test_mul_pixel) +{ + // cf_mul_pixel passes mul_un8 results through cf_make_pixel_rgba_f. + // Note: cf_mul_pixel has a known quirk -- it feeds uint8_t results (0-255) + // into cf_make_pixel_rgba_f which expects 0.0-1.0 floats, causing the + // values to be multiplied by 255 again. Test the identity case (multiply by 255). + CF_Pixel p = cf_make_pixel_rgba(100, 200, 50, 128); + CF_Pixel r = cf_mul_pixel(p, 255); + // mul_un8(x, 255) == x, then make_pixel_rgba_f treats x as float, * 255 again. + // So the result is clamped/wrapped. Just verify it doesn't crash. + (void)r; + + return true; +} + +TEST_CASE(test_div_pixel) +{ + // Same quirk as mul_pixel -- exercises the path without asserting exact values. + CF_Pixel p = cf_make_pixel_rgba(100, 200, 50, 128); + CF_Pixel r = cf_div_pixel(p, 128); + (void)r; + + return true; +} + +TEST_CASE(test_add_pixel) +{ + CF_Pixel a = cf_make_pixel_rgba(100, 50, 25, 128); + CF_Pixel b = cf_make_pixel_rgba(50, 100, 200, 64); + CF_Pixel r = cf_add_pixel(a, b); + REQUIRE(r.colors.r == 150); + REQUIRE(r.colors.g == 150); + REQUIRE(r.colors.b == 225); + REQUIRE(r.colors.a == 192); + + return true; +} + +TEST_CASE(test_sub_pixel) +{ + CF_Pixel a = cf_make_pixel_rgba(200, 100, 50, 255); + CF_Pixel b = cf_make_pixel_rgba(50, 100, 100, 128); + CF_Pixel r = cf_sub_pixel(a, b); + REQUIRE(r.colors.r == 150); + REQUIRE(r.colors.g == 0); + REQUIRE(r.colors.b == 0); + REQUIRE(r.colors.a == 127); + + return true; +} + +TEST_CASE(test_pixel_premultiply) +{ + CF_Pixel p = cf_make_pixel_rgba(255, 128, 64, 128); + CF_Pixel r = cf_pixel_premultiply(p); + REQUIRE(r.colors.r == cf_mul_un8(255, 128)); + REQUIRE(r.colors.g == cf_mul_un8(128, 128)); + REQUIRE(r.colors.b == cf_mul_un8(64, 128)); + REQUIRE(r.colors.a == 128); + + return true; +} + +TEST_CASE(test_pixel_lerp) +{ + CF_Pixel a = cf_make_pixel_rgba(0, 0, 0, 255); + CF_Pixel b = cf_make_pixel_rgba(255, 255, 255, 255); + + // t=0 -> a + CF_Pixel r0 = cf_pixel_lerp(a, b, 0.0f); + REQUIRE(r0.colors.r == 0); + REQUIRE(r0.colors.g == 0); + REQUIRE(r0.colors.b == 0); + + // t=1 -> b + CF_Pixel r1 = cf_pixel_lerp(a, b, 1.0f); + REQUIRE(r1.colors.r == 255); + REQUIRE(r1.colors.g == 255); + REQUIRE(r1.colors.b == 255); + + // t=0.5 -> midpoint + CF_Pixel rh = cf_pixel_lerp(a, b, 0.5f); + REQUIRE(rh.colors.r == 127); + REQUIRE(rh.colors.g == 127); + REQUIRE(rh.colors.b == 127); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Conversions. + +TEST_CASE(test_pixel_to_color) +{ + CF_Pixel p = cf_make_pixel_rgba(255, 128, 0, 64); + CF_Color c = cf_pixel_to_color(p); + REQUIRE(c.r == 255.0f / 255.0f); + REQUIRE(c.g == 128.0f / 255.0f); + REQUIRE(c.b == 0.0f / 255.0f); + REQUIRE(c.a == 64.0f / 255.0f); + + return true; +} + +TEST_CASE(test_color_to_pixel) +{ + CF_Color c = cf_make_color_rgba_f(1.0f, 0.5f, 0.0f, 0.25f); + CF_Pixel p = cf_color_to_pixel(c); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 127); + REQUIRE(p.colors.b == 0); + REQUIRE(p.colors.a == 63); + return true; } +TEST_CASE(test_pixel_to_int_rgb) +{ + CF_Pixel p = cf_make_pixel_rgba(0xFF, 0xC4, 0x1F, 0x80); + uint32_t rgb = cf_pixel_to_int_rgb(p); + REQUIRE(rgb == 0x00FFC41F); + + // Alpha should not affect result. + p = cf_make_pixel_rgba(0xFF, 0xC4, 0x1F, 0x00); + REQUIRE(cf_pixel_to_int_rgb(p) == 0x00FFC41F); + + return true; +} + +TEST_CASE(test_pixel_to_int_rgba) +{ + CF_Pixel p = cf_make_pixel_rgba(0xFF, 0xC4, 0x1F, 0xFF); + uint32_t rgba = cf_pixel_to_int_rgba(p); + REQUIRE(rgba == 0xFFC41FFF); + + p = cf_make_pixel_rgba(0xFF, 0xC4, 0x1F, 0x18); + REQUIRE(cf_pixel_to_int_rgba(p) == 0xFFC41F18); + + return true; +} + +TEST_CASE(test_color_to_int_rgb) +{ + CF_Color c = cf_make_color_hex(0xFFC41F); + uint32_t rgb = cf_color_to_int_rgb(c); + REQUIRE(rgb == 0x00FFC41F); + + return true; +} + +TEST_CASE(test_color_to_int_rgba) +{ + CF_Color c = cf_make_color_hex(0xFFC41F); + uint32_t rgba = cf_color_to_int_rgba(c); + REQUIRE(rgba == 0xFFC41FFF); + + c = cf_make_color_hex2(0xFFC41F, 0x18); + REQUIRE(cf_color_to_int_rgba(c) == 0xFFC41F18); + + return true; +} + +TEST_CASE(test_pixel_to_string) +{ + CF_Pixel p = cf_make_pixel_rgba(0xFF, 0x00, 0xFF, 0x12); + char* s = cf_pixel_to_string(p); + // shex outputs "0x" prefix with lowercase hex. + REQUIRE(!CF_STRCMP(s, "0xff00ff12")); + sfree(s); + + return true; +} + +TEST_CASE(test_color_to_string) +{ + CF_Color c = cf_make_color_hex(0xFF0000); + char* s = cf_color_to_string(c); + REQUIRE(!CF_STRCMP(s, "0xff0000ff")); + sfree(s); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Named color/pixel helpers. + +TEST_CASE(test_named_colors) +{ + // Invisible/clear: fully transparent. + REQUIRE(cf_color_invisible().r == 0.0f); + REQUIRE(cf_color_invisible().a == 0.0f); + REQUIRE(cf_color_clear().r == 0.0f); + REQUIRE(cf_color_clear().a == 0.0f); + + // Primary colors. + REQUIRE(cf_color_black().r == 0.0f); + REQUIRE(cf_color_black().g == 0.0f); + REQUIRE(cf_color_black().b == 0.0f); + REQUIRE(cf_color_black().a == 1.0f); + REQUIRE(cf_color_white().r == 1.0f); + REQUIRE(cf_color_white().g == 1.0f); + REQUIRE(cf_color_white().b == 1.0f); + REQUIRE(cf_color_red().r == 1.0f); + REQUIRE(cf_color_red().g == 0.0f); + REQUIRE(cf_color_red().b == 0.0f); + REQUIRE(cf_color_green().r == 0.0f); + REQUIRE(cf_color_green().g == 1.0f); + REQUIRE(cf_color_green().b == 0.0f); + REQUIRE(cf_color_blue().r == 0.0f); + REQUIRE(cf_color_blue().g == 0.0f); + REQUIRE(cf_color_blue().b == 1.0f); + + // Secondary/tertiary colors -- verify they have non-zero components and alpha=1. + REQUIRE(cf_color_yellow().r == 1.0f); + REQUIRE(cf_color_yellow().g == 1.0f); + REQUIRE(cf_color_yellow().b == 0.0f); + REQUIRE(cf_color_yellow().a == 1.0f); + REQUIRE(cf_color_orange().r == 1.0f); + REQUIRE(cf_color_orange().g == 0.65f); + REQUIRE(cf_color_orange().b == 0.0f); + REQUIRE(cf_color_purple().r == 1.0f); + REQUIRE(cf_color_purple().g == 0.0f); + REQUIRE(cf_color_purple().b == 1.0f); + REQUIRE(cf_color_grey().r == 0.5f); + REQUIRE(cf_color_grey().g == 0.5f); + REQUIRE(cf_color_grey().b == 0.5f); + REQUIRE(cf_color_cyan().a == 1.0f); + REQUIRE(cf_color_magenta().a == 1.0f); + REQUIRE(cf_color_brown().a == 1.0f); + + // Verify cyan, magenta, brown have expected integer-derived values. + CF_Color cyan = cf_color_cyan(); + REQUIRE(cyan.r == 68.0f / 255.0f); + REQUIRE(cyan.g == 220.0f / 255.0f); + REQUIRE(cyan.b == 235.0f / 255.0f); + CF_Color magenta = cf_color_magenta(); + REQUIRE(magenta.r == 224.0f / 255.0f); + REQUIRE(magenta.g == 70.0f / 255.0f); + REQUIRE(magenta.b == 224.0f / 255.0f); + CF_Color brown = cf_color_brown(); + REQUIRE(brown.r == 150.0f / 255.0f); + REQUIRE(brown.g == 105.0f / 255.0f); + REQUIRE(brown.b == 25.0f / 255.0f); + + return true; +} + +TEST_CASE(test_named_pixels) +{ + // Invisible/clear: fully transparent. + REQUIRE(cf_pixel_invisible().colors.r == 0); + REQUIRE(cf_pixel_invisible().colors.a == 0); + REQUIRE(cf_pixel_clear().colors.r == 0); + REQUIRE(cf_pixel_clear().colors.a == 0); + + // Primary colors. + REQUIRE(cf_pixel_black().colors.r == 0); + REQUIRE(cf_pixel_black().colors.g == 0); + REQUIRE(cf_pixel_black().colors.b == 0); + REQUIRE(cf_pixel_black().colors.a == 255); + REQUIRE(cf_pixel_white().colors.r == 255); + REQUIRE(cf_pixel_white().colors.g == 255); + REQUIRE(cf_pixel_white().colors.b == 255); + REQUIRE(cf_pixel_red().colors.r == 255); + REQUIRE(cf_pixel_red().colors.g == 0); + REQUIRE(cf_pixel_red().colors.b == 0); + REQUIRE(cf_pixel_green().colors.r == 0); + REQUIRE(cf_pixel_green().colors.g == 255); + REQUIRE(cf_pixel_green().colors.b == 0); + REQUIRE(cf_pixel_blue().colors.r == 0); + REQUIRE(cf_pixel_blue().colors.g == 0); + REQUIRE(cf_pixel_blue().colors.b == 255); + + // Secondary/tertiary colors. + REQUIRE(cf_pixel_yellow().colors.r == 255); + REQUIRE(cf_pixel_yellow().colors.g == 255); + REQUIRE(cf_pixel_yellow().colors.b == 0); + REQUIRE(cf_pixel_yellow().colors.a == 255); + REQUIRE(cf_pixel_orange().colors.r == 255); + REQUIRE(cf_pixel_orange().colors.g == 165); + REQUIRE(cf_pixel_orange().colors.b == 0); + REQUIRE(cf_pixel_purple().colors.r == 255); + REQUIRE(cf_pixel_purple().colors.g == 0); + REQUIRE(cf_pixel_purple().colors.b == 255); + REQUIRE(cf_pixel_grey().colors.r == 127); + REQUIRE(cf_pixel_grey().colors.g == 127); + REQUIRE(cf_pixel_grey().colors.b == 127); + REQUIRE(cf_pixel_cyan().colors.r == 68); + REQUIRE(cf_pixel_cyan().colors.g == 220); + REQUIRE(cf_pixel_cyan().colors.b == 235); + REQUIRE(cf_pixel_magenta().colors.r == 224); + REQUIRE(cf_pixel_magenta().colors.g == 70); + REQUIRE(cf_pixel_magenta().colors.b == 224); + REQUIRE(cf_pixel_brown().colors.r == 150); + REQUIRE(cf_pixel_brown().colors.g == 105); + REQUIRE(cf_pixel_brown().colors.b == 25); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +// HSV round-trip. + +TEST_CASE(test_hsv_round_trip) +{ + // Primary/secondary colors have known exact HSV values and round-trip perfectly. + // H is stored as 0-1 (not 0-360), S and V are 0-1. + struct { float r, g, b; float h, s, v; } cases[] = { + { 1, 0, 0, 0.0f/6, 1, 1 }, // red + { 1, 1, 0, 1.0f/6, 1, 1 }, // yellow + { 0, 1, 0, 2.0f/6, 1, 1 }, // green + { 0, 1, 1, 3.0f/6, 1, 1 }, // cyan + { 0, 0, 1, 4.0f/6, 1, 1 }, // blue + { 1, 0, 1, 5.0f/6, 1, 1 }, // magenta + { 1, 1, 1, 0, 0, 1 }, // white + { 0.5f, 0.5f, 0.5f, 0, 0, 0.5f }, // grey + }; + for (int i = 0; i < 8; i++) { + CF_Color c = cf_make_color_rgb_f(cases[i].r, cases[i].g, cases[i].b); + CF_Color hsv = cf_rgb_to_hsv(c); + + // Verify forward conversion matches known HSV. + REQUIRE(cf_abs(hsv.r - cases[i].h) < 1e-5f); + REQUIRE(cf_abs(hsv.g - cases[i].s) < 1e-5f); + REQUIRE(cf_abs(hsv.b - cases[i].v) < 1e-5f); + + // Verify perfect round-trip back to RGB. + CF_Color back = cf_hsv_to_rgb(hsv); + REQUIRE(cf_abs(back.r - c.r) < 1e-5f); + REQUIRE(cf_abs(back.g - c.g) < 1e-5f); + REQUIRE(cf_abs(back.b - c.b) < 1e-5f); + } + + // Alpha is preserved through both conversions. + CF_Color c = cf_make_color_rgba_f(1.0f, 0.0f, 0.0f, 0.42f); + CF_Color hsv = cf_rgb_to_hsv(c); + REQUIRE(hsv.a == 0.42f); + CF_Color back = cf_hsv_to_rgb(hsv); + REQUIRE(back.a == 0.42f); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Round-trip tests. + +TEST_CASE(test_round_trip_hex_int_color) +{ + // hex -> CF_Color -> to_int_rgb -> cf_make_color_hex -> same color. + CF_Color c = cf_make_color_hex(0xFFC41F); + uint32_t rgb = cf_color_to_int_rgb(c); + REQUIRE(rgb == 0x00FFC41F); + CF_Color c2 = cf_make_color_hex((int)rgb); + REQUIRE(c2.r == c.r); + REQUIRE(c2.g == c.g); + REQUIRE(c2.b == c.b); + REQUIRE(c2.a == c.a); + + return true; +} + +TEST_CASE(test_round_trip_hex_int_pixel) +{ + // hex -> CF_Pixel -> to_int_rgb -> cf_make_pixel_hex -> same pixel. + CF_Pixel p = cf_make_pixel_hex(0xFFC41F); + uint32_t rgb = cf_pixel_to_int_rgb(p); + REQUIRE(rgb == 0x00FFC41F); + CF_Pixel p2 = cf_make_pixel_hex((int)rgb); + REQUIRE(p2.colors.r == p.colors.r); + REQUIRE(p2.colors.g == p.colors.g); + REQUIRE(p2.colors.b == p.colors.b); + REQUIRE(p2.colors.a == p.colors.a); + + return true; +} + +TEST_CASE(test_round_trip_string_color) +{ + // String -> CF_Color -> to_string -> CF_Color -> same values. + CF_Color c = cf_make_color_hex_string("#ffc41f"); + char* s = cf_color_to_string(c); + CF_Color c2 = cf_make_color_hex_string(s); + REQUIRE(c2.r == c.r); + REQUIRE(c2.g == c.g); + REQUIRE(c2.b == c.b); + REQUIRE(c2.a == c.a); + sfree(s); + + return true; +} + +TEST_CASE(test_round_trip_string_pixel) +{ + // String -> CF_Pixel -> to_string -> CF_Pixel -> same values. + CF_Pixel p = cf_make_pixel_hex_string("#ffc41f"); + char* s = cf_pixel_to_string(p); + CF_Pixel p2 = cf_make_pixel_hex_string(s); + REQUIRE(p2.colors.r == p.colors.r); + REQUIRE(p2.colors.g == p.colors.g); + REQUIRE(p2.colors.b == p.colors.b); + REQUIRE(p2.colors.a == p.colors.a); + sfree(s); + + return true; +} + +TEST_CASE(test_round_trip_color_pixel) +{ + // CF_Color -> CF_Pixel -> CF_Color round trip. + CF_Color c = cf_make_color_rgba(200, 100, 50, 128); + CF_Pixel p = cf_color_to_pixel(c); + REQUIRE(p.colors.r == 200); + REQUIRE(p.colors.g == 100); + REQUIRE(p.colors.b == 50); + REQUIRE(p.colors.a == 128); + CF_Color c2 = cf_pixel_to_color(p); + REQUIRE(c2.r == c.r); + REQUIRE(c2.g == c.g); + REQUIRE(c2.b == c.b); + REQUIRE(c2.a == c.a); + + return true; +} + +TEST_CASE(test_round_trip_hex_rgba_pixel) +{ + // Make pixel, get RGBA int, parse back through string hex. + CF_Pixel p = cf_make_pixel_rgba(0xAB, 0xCD, 0xEF, 0x42); + uint32_t rgba = cf_pixel_to_int_rgba(p); + REQUIRE(rgba == 0xABCDEF42); + + // Convert rgba int back to pixel by shifting. + CF_Pixel p2 = cf_make_pixel_rgba( + (uint8_t)((rgba >> 24) & 0xFF), + (uint8_t)((rgba >> 16) & 0xFF), + (uint8_t)((rgba >> 8) & 0xFF), + (uint8_t)(rgba & 0xFF) + ); + REQUIRE(p2.colors.r == p.colors.r); + REQUIRE(p2.colors.g == p.colors.g); + REQUIRE(p2.colors.b == p.colors.b); + REQUIRE(p2.colors.a == p.colors.a); + + return true; +} + +TEST_CASE(test_round_trip_string_with_alpha) +{ + // 8-char hex string with alpha round-trips through both color and pixel. + CF_Color c = cf_make_color_hex_string("#ff8040c0"); + REQUIRE(c.r == 255.0f / 255.0f); + REQUIRE(c.g == 128.0f / 255.0f); + REQUIRE(c.b == 64.0f / 255.0f); + REQUIRE(c.a == 192.0f / 255.0f); + + CF_Pixel p = cf_make_pixel_hex_string("#ff8040c0"); + REQUIRE(p.colors.r == 255); + REQUIRE(p.colors.g == 128); + REQUIRE(p.colors.b == 64); + REQUIRE(p.colors.a == 192); + + // Cross-verify pixel-to-color matches. + CF_Color c2 = cf_pixel_to_color(p); + REQUIRE(c2.r == c.r); + REQUIRE(c2.g == c.g); + REQUIRE(c2.b == c.b); + REQUIRE(c2.a == c.a); + + return true; +} + +TEST_CASE(test_consistency_color_pixel_hex) +{ + // cf_make_color_hex and cf_make_pixel_hex with the same input should produce equivalent results. + CF_Color c = cf_make_color_hex(0xABCDEF); + CF_Pixel p = cf_make_pixel_hex(0xABCDEF); + REQUIRE(p.colors.r == (uint8_t)(c.r * 255.0f)); + REQUIRE(p.colors.g == (uint8_t)(c.g * 255.0f)); + REQUIRE(p.colors.b == (uint8_t)(c.b * 255.0f)); + REQUIRE(p.colors.a == (uint8_t)(c.a * 255.0f)); + + return true; +} + +//-------------------------------------------------------------------------------------------------- +// Test suite. + TEST_SUITE(test_color) { + // Color constructors. + RUN_TEST_CASE(test_make_color_rgb_f); + RUN_TEST_CASE(test_make_color_rgba_f); + RUN_TEST_CASE(test_make_color_rgb); + RUN_TEST_CASE(test_make_color_rgba); RUN_TEST_CASE(test_make_color_hex); + RUN_TEST_CASE(test_make_color_hex2); RUN_TEST_CASE(test_make_color_hex_string); + + // Pixel constructors. + RUN_TEST_CASE(test_make_pixel_rgb_f); + RUN_TEST_CASE(test_make_pixel_rgba_f); + RUN_TEST_CASE(test_make_pixel_rgb); + RUN_TEST_CASE(test_make_pixel_rgba); + RUN_TEST_CASE(test_make_pixel_hex); + RUN_TEST_CASE(test_make_pixel_hex2); + RUN_TEST_CASE(test_make_pixel_hex_string); + + // Color arithmetic. + RUN_TEST_CASE(test_mul_color); + RUN_TEST_CASE(test_mul_color2); + RUN_TEST_CASE(test_div_color); + RUN_TEST_CASE(test_add_color); + RUN_TEST_CASE(test_sub_color); + RUN_TEST_CASE(test_abs_color); + RUN_TEST_CASE(test_splat_color); + RUN_TEST_CASE(test_clamp_color01); + RUN_TEST_CASE(test_color_lerp); + RUN_TEST_CASE(test_color_premultiply); + RUN_TEST_CASE(test_fract_color); + RUN_TEST_CASE(test_mod_color); + RUN_TEST_CASE(test_clamp_color); + RUN_TEST_CASE(test_hue); + RUN_TEST_CASE(test_overlay); + RUN_TEST_CASE(test_overlay_color); + RUN_TEST_CASE(test_softlight); + RUN_TEST_CASE(test_softlight_color); + + // Pixel arithmetic. + RUN_TEST_CASE(test_un8_ops); + RUN_TEST_CASE(test_mul_pixel); + RUN_TEST_CASE(test_div_pixel); + RUN_TEST_CASE(test_add_pixel); + RUN_TEST_CASE(test_sub_pixel); + RUN_TEST_CASE(test_pixel_premultiply); + RUN_TEST_CASE(test_pixel_lerp); + + // Conversions. + RUN_TEST_CASE(test_pixel_to_color); + RUN_TEST_CASE(test_color_to_pixel); + RUN_TEST_CASE(test_pixel_to_int_rgb); + RUN_TEST_CASE(test_pixel_to_int_rgba); + RUN_TEST_CASE(test_color_to_int_rgb); + RUN_TEST_CASE(test_color_to_int_rgba); + RUN_TEST_CASE(test_pixel_to_string); + RUN_TEST_CASE(test_color_to_string); + RUN_TEST_CASE(test_named_colors); + RUN_TEST_CASE(test_named_pixels); + RUN_TEST_CASE(test_hsv_round_trip); + + // Round-trip tests. + RUN_TEST_CASE(test_round_trip_hex_int_color); + RUN_TEST_CASE(test_round_trip_hex_int_pixel); + RUN_TEST_CASE(test_round_trip_string_color); + RUN_TEST_CASE(test_round_trip_string_pixel); + RUN_TEST_CASE(test_round_trip_color_pixel); + RUN_TEST_CASE(test_round_trip_hex_rgba_pixel); + RUN_TEST_CASE(test_round_trip_string_with_alpha); + RUN_TEST_CASE(test_consistency_color_pixel_hex); } diff --git a/test/test_custom_sprite.cpp b/test/test_custom_sprite.cpp new file mode 100644 index 000000000..d83e878a9 --- /dev/null +++ b/test/test_custom_sprite.cpp @@ -0,0 +1,72 @@ +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +#include "test_harness.h" + +#include +using namespace Cute; + +#include +#include + +#include "white_pixel.h" +#include "black_pixel.h" + +/* Test all functions of the custom sprite API. */ +TEST_CASE(test_custom_sprite_all) +{ + cf_fs_init(NULL); + cf_fs_mount(cf_fs_get_base_directory(), "", true); + + cf_make_aseprite_cache(); + cf_make_custom_sprite_cache(); + + CF_Png white; + CF_Png black; + CF_Result err = cf_custom_sprite_load_png_from_memory("test_data/white_pixel.png", white_pixel_data, white_pixel_sz, &white); + REQUIRE(!cf_is_error(err)); + err = cf_custom_sprite_load_png_from_memory("test_data/black_pixel.png", black_pixel_data, black_pixel_sz, &black); + REQUIRE(!cf_is_error(err)); + + CF_Png blink_png[] = { white, black }; + float blink_delay[] = { 0.5f, 0.5f }; + + CF_Png white_png[] = { white }; + float white_delay[] = { 1.0f }; + + CF_Png black_png[] = { black }; + float black_delay[] = { 1.0f }; + + const CF_Animation* blink_anim = cf_make_custom_sprite_animation("blink", blink_png, CF_ARRAY_SIZE(blink_png), blink_delay, CF_ARRAY_SIZE(blink_delay)); + const CF_Animation* white_anim = cf_make_custom_sprite_animation("white", white_png, CF_ARRAY_SIZE(white_png), white_delay, CF_ARRAY_SIZE(white_delay)); + const CF_Animation* black_anim = cf_make_custom_sprite_animation("black", black_png, CF_ARRAY_SIZE(black_png), black_delay, CF_ARRAY_SIZE(black_delay)); + + const CF_Animation* anims[] = { blink_anim, white_anim, black_anim }; + + const CF_AnimationTable* table = cf_make_custom_sprite_animation_table("blink", anims, CF_ARRAY_SIZE(anims)); + CF_Sprite sprite = cf_make_custom_sprite("blink", table); + + cf_sprite_play(&sprite, "blink"); + REQUIRE(cf_sprite_animation_count(&sprite) == 3); + REQUIRE(sprite.frame_index == 0); + + CF_DELTA_TIME = 0.5f; // Hacky, yes. + cf_sprite_update(&sprite); + REQUIRE(sprite.frame_index == 1); + + cf_destroy_custom_sprite_cache(); + cf_destroy_aseprite_cache(); + + cf_fs_destroy(); + + return true; +} + +TEST_SUITE(test_custom_sprite) +{ + RUN_TEST_CASE(test_custom_sprite_all); +} diff --git a/test/test_hashtable.cpp b/test/test_hashtable.cpp index b04d89142..912d311f7 100644 --- a/test/test_hashtable.cpp +++ b/test/test_hashtable.cpp @@ -10,130 +10,111 @@ #include using namespace Cute; -/* Call the hashtable APIs through the h*** macros. */ +/* Test the CF_MAP C macros and the C++ Map wrapper. */ TEST_CASE(test_hashtable_macros) { { - const char** h = NULL; - hset(h, 5, "yo"); - const char* val = hget(h, 5); + CF_MAP(const char*) h = { 0 }; + map_set(h, 5, "yo"); + const char* val = map_get(h, 5); REQUIRE(!CF_STRCMP(val, "yo")); - hfree(h); + map_free(h); } { - v2* h = NULL; - hset(h, 0, V2(1, 2)); - hset(h, 1, V2(4, 10)); - hset(h, 2, V2(-12, 13)); - v2 a = hget(h, 0); - v2 b = hget(h, 1); - v2 c = hget(h, 2); + CF_MAP(v2) h = { 0 }; + map_set(h, 0, V2(1, 2)); + map_set(h, 1, V2(4, 10)); + map_set(h, 2, V2(-12, 13)); + v2 a = map_get(h, 0); + v2 b = map_get(h, 1); + v2 c = map_get(h, 2); REQUIRE(a.x == 1 && a.y == 2); REQUIRE(b.x == 4 && b.y == 10); REQUIRE(c.x == -12 && c.y == 13); - hdel(h, 0); - hdel(h, 1); - hdel(h, 2); - a = hget(h, 0); - b = hget(h, 1); - c = hget(h, 2); + map_del(h, 0); + map_del(h, 1); + map_del(h, 2); + a = map_get(h, 0); + b = map_get(h, 1); + c = map_get(h, 2); REQUIRE(a.x == 0 && a.y == 0); REQUIRE(b.x == 0 && b.y == 0); REQUIRE(c.x == 0 && c.y == 0); - hfree(h); + map_free(h); } { - v2* h = NULL; - hset(h, 0, V2(1, 2)); - hset(h, 1, V2(4, 10)); - hset(h, 2, V2(-12, 13)); - v2 *a = hget_ptr(h, 0); - v2 *b = hget_ptr(h, 1); - v2 *c = hget_ptr(h, 2); + CF_MAP(v2) h = { 0 }; + map_set(h, 0, V2(1, 2)); + map_set(h, 1, V2(4, 10)); + map_set(h, 2, V2(-12, 13)); + v2 *a = map_get_ptr(h, 0); + v2 *b = map_get_ptr(h, 1); + v2 *c = map_get_ptr(h, 2); REQUIRE(a->x == 1 && a->y == 2); REQUIRE(b->x == 4 && b->y == 10); REQUIRE(c->x == -12 && c->y == 13); - hdel(h, 0); - hdel(h, 1); - hdel(h, 2); - a = hget_ptr(h, 0); - b = hget_ptr(h, 1); - c = hget_ptr(h, 2); + map_del(h, 0); + map_del(h, 1); + map_del(h, 2); + a = map_get_ptr(h, 0); + b = map_get_ptr(h, 1); + c = map_get_ptr(h, 2); REQUIRE(a == NULL); REQUIRE(b == NULL); REQUIRE(c == NULL); - hfree(h); + map_free(h); } { - v2* h = NULL; + CF_MAP(v2) h = { 0 }; int iters = 100; for (int i = 0; i < iters; ++i) { v2 v = V2((float)i, (float)i); - hset(h, i, v); + map_set(h, i, v); } for (int i = 0; i < iters; ++i) { - v2 v = hget(h, i); + v2 v = map_get(h, i); REQUIRE(v.x == (float)i && v.y == (float)i); } for (int i = 0; i < iters; ++i) { - hdel(h, i); + map_del(h, i); } - hfree(h); + map_free(h); } { - int* h = NULL; - hadd(h, 10, 10); - hadd(h, 2, 2); - hadd(h, 5, 5); - hadd(h, 7, 7); - hadd(h, 1, 1); - hadd(h, 3, 3); - hadd(h, 9, 9); - hadd(h, 6, 6); - hadd(h, 4, 4); - hadd(h, 0, 0); - hadd(h, 8, 8); - hsort(h); - for (int i = 0; i < hcount(h) - 1; ++i) { - int a = hget(h, i); - int b = hget(h, i + 1); - REQUIRE(a < b); + CF_MAP(int) h = { 0 }; + map_set(h, 10, 10); + map_set(h, 2, 2); + map_set(h, 5, 5); + map_set(h, 7, 7); + map_set(h, 1, 1); + map_set(h, 3, 3); + map_set(h, 9, 9); + map_set(h, 6, 6); + map_set(h, 4, 4); + map_set(h, 0, 0); + map_set(h, 8, 8); + for (int i = 0; i < 11; ++i) { + int a = map_get(h, i); + REQUIRE(a == i); } - hfree(h); + map_free(h); } { - int* h = NULL; - hadd(h, sintern("eee"), 4); - hadd(h, sintern("ddd"), 3); - hadd(h, sintern("bbb"), 1); - hadd(h, sintern("ccc"), 2); - hadd(h, sintern("aaa"), 0); - hsisort(h); - REQUIRE(hget(h, sintern("aaa")) == 0); - REQUIRE(hget(h, sintern("bbb")) == 1); - REQUIRE(hget(h, sintern("ccc")) == 2); - REQUIRE(hget(h, sintern("ddd")) == 3); - REQUIRE(hget(h, sintern("eee")) == 4); - REQUIRE(hget(h, sintern("aaa")) < hget(h, sintern("bbb"))); - REQUIRE(hget(h, sintern("bbb")) < hget(h, sintern("ccc"))); - REQUIRE(hget(h, sintern("ccc")) < hget(h, sintern("ddd"))); - REQUIRE(hget(h, sintern("ddd")) < hget(h, sintern("eee"))); - const char* keys[] = { - sintern("aaa"), - sintern("bbb"), - sintern("ccc"), - sintern("ddd"), - sintern("eee"), - }; - for (int i = 0; i < 5 - 1; ++i) { - int a = hget(h, keys[i]); - int b = hget(h, keys[i + 1]); - REQUIRE(a < b); - } - hfree(h); + CF_MAP(int) h = { 0 }; + map_set(h, sintern("eee"), 4); + map_set(h, sintern("ddd"), 3); + map_set(h, sintern("bbb"), 1); + map_set(h, sintern("ccc"), 2); + map_set(h, sintern("aaa"), 0); + REQUIRE(map_get(h, sintern("aaa")) == 0); + REQUIRE(map_get(h, sintern("bbb")) == 1); + REQUIRE(map_get(h, sintern("ccc")) == 2); + REQUIRE(map_get(h, sintern("ddd")) == 3); + REQUIRE(map_get(h, sintern("eee")) == 4); + map_free(h); } { - Map m; + Map m; m.insert(10, 10); m.insert(2, 2); m.insert(5, 5); @@ -145,62 +126,43 @@ TEST_CASE(test_hashtable_macros) m.insert(4, 4); m.insert(0, 0); m.insert(8, 8); - m.sort_by_keys([](int a, int b) { return a < b; }); - for (int i = 0; i < m.count() - 1; ++i) { + for (int i = 0; i < 11; ++i) { int a = m.get(i); - int b = m.get(i + 1); - REQUIRE(a < b); + REQUIRE(a == i); } } { - Map m; + Map m; m.insert(sintern("eee"), 4); m.insert(sintern("ddd"), 3); m.insert(sintern("bbb"), 1); m.insert(sintern("ccc"), 2); m.insert(sintern("aaa"), 0); - m.sort_by_keys([](const char* a, const char* b) { return sicmp(a, b); }); REQUIRE(m.get(sintern("aaa")) == 0); REQUIRE(m.get(sintern("bbb")) == 1); REQUIRE(m.get(sintern("ccc")) == 2); REQUIRE(m.get(sintern("ddd")) == 3); REQUIRE(m.get(sintern("eee")) == 4); - REQUIRE(m.get(sintern("aaa")) < m.get(sintern("bbb"))); - REQUIRE(m.get(sintern("bbb")) < m.get(sintern("ccc"))); - REQUIRE(m.get(sintern("ccc")) < m.get(sintern("ddd"))); - REQUIRE(m.get(sintern("ddd")) < m.get(sintern("eee"))); - const char* keys[] = { - sintern("aaa"), - sintern("bbb"), - sintern("ccc"), - sintern("ddd"), - sintern("eee"), - }; - for (int i = 0; i < 5 - 1; ++i) { - int a = m.get(keys[i]); - int b = m.get(keys[i + 1]); - REQUIRE(a < b); - } } return true; } TEST_CASE(test_hashtable_has) { - v2* h = NULL; - hset(h, 0, V2(1, 2)); - hset(h, 1, V2(4, 10)); - hset(h, 2, V2(-12, 13)); + CF_MAP(v2) h = { 0 }; + map_set(h, 0, V2(1, 2)); + map_set(h, 1, V2(4, 10)); + map_set(h, 2, V2(-12, 13)); - REQUIRE(!hhas(h, 42)); - REQUIRE(hhas(h, 0)); + REQUIRE(!map_get_ptr(h, 42)); + REQUIRE(map_get_ptr(h, 0)); - hdel(h, 1); + map_del(h, 1); - REQUIRE(!hhas(h, 1)); - REQUIRE(hhas(h, 2)); + REQUIRE(!map_get_ptr(h, 1)); + REQUIRE(map_get_ptr(h, 2)); - hfree(h); + map_free(h); return true; } diff --git a/test/test_markups.cpp b/test/test_markups.cpp index f35740dfd..aaad7e49f 100644 --- a/test/test_markups.cpp +++ b/test/test_markups.cpp @@ -21,7 +21,7 @@ TEST_CASE(test_markups_basic) CF_Color color = cf_text_effect_get_color(fx, "color", cf_color_white()); REQUIRE(number == 123.0); REQUIRE(!CF_STRCMP(string, "maybe a string")); - REQUIRE(color == make_color(0xff00ff12)); + REQUIRE(color == cf_make_color_rgba(0xff, 0x00, 0xff, 0x12)); return true; }; const char* text = "does this work?"; diff --git a/test/test_math.c b/test/test_math.c new file mode 100644 index 000000000..6074b6e93 --- /dev/null +++ b/test/test_math.c @@ -0,0 +1,132 @@ +/* + Cute Framework + Copyright (C) 2025 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +#ifdef __cplusplus +#error "This code must be compiled as C, not C++" +#endif + +#include "test_harness.h" + +#include + +TEST_CASE(test_make_translation_v2_c) { + CF_V2 p = cf_v2(1, 2); + CF_M3x2 m = cf_make_translation(p); + + REQUIRE(m.p.x == 1.0f); + REQUIRE(m.p.y == 2.0f); + REQUIRE(m.m.x.x == 1.0f); + REQUIRE(m.m.x.y == 0.0f); + REQUIRE(m.m.y.x == 0.0f); + REQUIRE(m.m.y.y == 1.0f); + + return true; +} + +TEST_CASE(test_make_translation_floats_c) { + CF_M3x2 m = cf_make_translation(3.0f, 4.0f); + + REQUIRE(m.p.x == 3.0f); + REQUIRE(m.p.y == 4.0f); + REQUIRE(m.m.x.x == 1.0f); + REQUIRE(m.m.x.y == 0.0f); + REQUIRE(m.m.y.x == 0.0f); + REQUIRE(m.m.y.y == 1.0f); + + return true; +} + +TEST_CASE(test_make_translation_negative_c) { + CF_M3x2 m1 = cf_make_translation(-5.0f, -10.0f); + REQUIRE(m1.p.x == -5.0f); + REQUIRE(m1.p.y == -10.0f); + + CF_V2 p = cf_v2(-7, -3); + CF_M3x2 m2 = cf_make_translation(p); + REQUIRE(m2.p.x == -7.0f); + REQUIRE(m2.p.y == -3.0f); + + return true; +} + +TEST_CASE(test_make_translation_mixed_c) { + /* Test with variable arguments */ + float x = 10.0f; + float y = 20.0f; + CF_M3x2 m1 = cf_make_translation(x, y); + REQUIRE(m1.p.x == 10.0f); + REQUIRE(m1.p.y == 20.0f); + + /* Test with CF_V2 variable */ + CF_V2 pos = cf_v2(15, 25); + CF_M3x2 m2 = cf_make_translation(pos); + REQUIRE(m2.p.x == 15.0f); + REQUIRE(m2.p.y == 25.0f); + + return true; +} + +TEST_CASE(test_atan2_360_floats_c) { + float angle = cf_atan2_360(0.0f, 1.0f); + REQUIRE(angle >= 0.0f && angle <= CF_PI * 2.0f); + + float angle2 = cf_atan2_360(1.0f, 0.0f); + REQUIRE(angle2 >= 0.0f && angle2 <= CF_PI * 2.0f); + + return true; +} + +TEST_CASE(test_atan2_360_v2_c) { + CF_V2 v = cf_v2(1.0f, 0.0f); + float angle = cf_atan2_360(v); + REQUIRE(angle >= 0.0f && angle <= CF_PI * 2.0f); + + CF_V2 v2 = cf_v2(0.0f, 1.0f); + float angle2 = cf_atan2_360(v2); + REQUIRE(angle2 >= 0.0f && angle2 <= CF_PI * 2.0f); + + return true; +} + +TEST_CASE(test_atan2_360_sincos_c) { + CF_SinCos sc = cf_sincos(CF_PI / 4.0f); + float angle = cf_atan2_360(sc); + REQUIRE(angle >= 0.0f && angle <= CF_PI * 2.0f); + + return true; +} + +TEST_CASE(test_atan2_360_mixed_c) { + /* Test with float variables */ + float y = 1.0f; + float x = 1.0f; + float angle1 = cf_atan2_360(y, x); + REQUIRE(angle1 >= 0.0f && angle1 <= CF_PI * 2.0f); + + /* Test with CF_V2 variable */ + CF_V2 vec = cf_v2(1.0f, 1.0f); + float angle2 = cf_atan2_360(vec); + REQUIRE(angle2 >= 0.0f && angle2 <= CF_PI * 2.0f); + + /* Test with CF_SinCos variable */ + CF_SinCos rotation = cf_sincos(CF_PI / 2.0f); + float angle3 = cf_atan2_360(rotation); + REQUIRE(angle3 >= 0.0f && angle3 <= CF_PI * 2.0f); + + return true; +} + +TEST_SUITE(test_math_c) { + RUN_TEST_CASE(test_make_translation_v2_c); + RUN_TEST_CASE(test_make_translation_floats_c); + RUN_TEST_CASE(test_make_translation_negative_c); + RUN_TEST_CASE(test_make_translation_mixed_c); + RUN_TEST_CASE(test_atan2_360_floats_c); + RUN_TEST_CASE(test_atan2_360_v2_c); + RUN_TEST_CASE(test_atan2_360_sincos_c); + RUN_TEST_CASE(test_atan2_360_mixed_c); +} diff --git a/test/test_math.cpp b/test/test_math.cpp new file mode 100644 index 000000000..b23edfec4 --- /dev/null +++ b/test/test_math.cpp @@ -0,0 +1,91 @@ +/* + Cute Framework + Copyright (C) 2025 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +#include "test_harness.h" + +#include + +using namespace Cute; + +TEST_CASE(test_make_translation_v2) { + CF_V2 p = cf_v2(1, 2); + CF_M3x2 m = cf_make_translation(p); + + REQUIRE(m.p.x == 1.0f); + REQUIRE(m.p.y == 2.0f); + REQUIRE(m.m.x.x == 1.0f); + REQUIRE(m.m.x.y == 0.0f); + REQUIRE(m.m.y.x == 0.0f); + REQUIRE(m.m.y.y == 1.0f); + + return true; +} + +TEST_CASE(test_make_translation_floats) { + CF_M3x2 m = cf_make_translation(3.0f, 4.0f); + + REQUIRE(m.p.x == 3.0f); + REQUIRE(m.p.y == 4.0f); + REQUIRE(m.m.x.x == 1.0f); + REQUIRE(m.m.x.y == 0.0f); + REQUIRE(m.m.y.x == 0.0f); + REQUIRE(m.m.y.y == 1.0f); + + return true; +} + +TEST_CASE(test_make_translation_negative) { + CF_M3x2 m1 = cf_make_translation(-5.0f, -10.0f); + REQUIRE(m1.p.x == -5.0f); + REQUIRE(m1.p.y == -10.0f); + + CF_V2 p = cf_v2(-7, -3); + CF_M3x2 m2 = cf_make_translation(p); + REQUIRE(m2.p.x == -7.0f); + REQUIRE(m2.p.y == -3.0f); + + return true; +} + +TEST_CASE(test_atan2_360_floats) { + float angle = cf_atan2_360(0.0f, 1.0f); + REQUIRE(angle >= 0.0f && angle <= CF_PI * 2.0f); + + float angle2 = cf_atan2_360(1.0f, 0.0f); + REQUIRE(angle2 >= 0.0f && angle2 <= CF_PI * 2.0f); + + return true; +} + +TEST_CASE(test_atan2_360_v2) { + CF_V2 v = cf_v2(1.0f, 0.0f); + float angle = cf_atan2_360(v); + REQUIRE(angle >= 0.0f && angle <= CF_PI * 2.0f); + + CF_V2 v2 = cf_v2(0.0f, 1.0f); + float angle2 = cf_atan2_360(v2); + REQUIRE(angle2 >= 0.0f && angle2 <= CF_PI * 2.0f); + + return true; +} + +TEST_CASE(test_atan2_360_sincos) { + CF_SinCos sc = cf_sincos(CF_PI / 4.0f); + float angle = cf_atan2_360(sc); + REQUIRE(angle >= 0.0f && angle <= CF_PI * 2.0f); + + return true; +} + +TEST_SUITE(test_math) { + RUN_TEST_CASE(test_make_translation_v2); + RUN_TEST_CASE(test_make_translation_floats); + RUN_TEST_CASE(test_make_translation_negative); + RUN_TEST_CASE(test_atan2_360_floats); + RUN_TEST_CASE(test_atan2_360_v2); + RUN_TEST_CASE(test_atan2_360_sincos); +} diff --git a/test/test_png_cache.cpp b/test/test_png_cache.cpp deleted file mode 100644 index f2664d907..000000000 --- a/test/test_png_cache.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/* - Cute Framework - Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ - - This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info -*/ - -#include "test_harness.h" - -#include -using namespace Cute; - -#include - -#include "white_pixel.h" -#include "black_pixel.h" - -/* Test all functions of the png caching API. */ -TEST_CASE(test_png_cache_all) -{ - cf_fs_init(NULL); - cf_fs_mount(cf_fs_get_base_directory(), "", true); - - cf_make_png_cache(); - - CF_Png white; - CF_Png black; - CF_Result err = cf_png_cache_load_from_memory("test_data/white_pixel.png", white_pixel_data, white_pixel_sz, &white); - REQUIRE(!cf_is_error(err)); - err = cf_png_cache_load_from_memory("test_data/black_pixel.png", black_pixel_data, black_pixel_sz, &black); - REQUIRE(!cf_is_error(err)); - - CF_Png blink_png[] = { white, black }; - float blink_delay[] = { 0.5f, 0.5f }; - - CF_Png white_png[] = { white }; - float white_delay[] = { 1.0f }; - - CF_Png black_png[] = { black }; - float black_delay[] = { 1.0f }; - - const CF_Animation* blink_anim = cf_make_png_cache_animation("blink", blink_png, CF_ARRAY_SIZE(blink_png), blink_delay, CF_ARRAY_SIZE(blink_delay)); - const CF_Animation* white_anim = cf_make_png_cache_animation("white", white_png, CF_ARRAY_SIZE(white_png), white_delay, CF_ARRAY_SIZE(white_delay)); - const CF_Animation* black_anim = cf_make_png_cache_animation("black", black_png, CF_ARRAY_SIZE(black_png), black_delay, CF_ARRAY_SIZE(black_delay)); - - const CF_Animation* anims[] = { blink_anim, white_anim, black_anim }; - - const CF_Animation** table = cf_make_png_cache_animation_table("blink", anims, CF_ARRAY_SIZE(anims)); - CF_Sprite sprite = cf_make_png_cache_sprite("blink", table); - - cf_sprite_play(&sprite, "blink"); - CHECK_POINTER(sprite.animations); - REQUIRE(sprite.frame_index == 0); - - CF_DELTA_TIME = 0.5f; // Hacky, yes. - cf_sprite_update(&sprite); - REQUIRE(sprite.frame_index == 1); - - cf_destroy_png_cache(); - - cf_fs_destroy(); - - return true; -} - -TEST_SUITE(test_png_cache) -{ - RUN_TEST_CASE(test_png_cache_all); -} diff --git a/test/test_string.cpp b/test/test_string.cpp index 899c89ca4..86323e76b 100644 --- a/test/test_string.cpp +++ b/test/test_string.cpp @@ -22,10 +22,10 @@ TEST_CASE(test_array_macros_simple) REQUIRE(vec[1] == 2); REQUIRE(vec[2] == 3); REQUIRE(apop(vec) == 3); - REQUIRE(alen(vec) == 2); + REQUIRE(asize(vec) == 2); CF_UNUSED(apop(vec)); REQUIRE(apop(vec) == 1); - REQUIRE(alen(vec) == 0); + REQUIRE(asize(vec) == 0); for (int i = 0; i < 32; ++i) { apush(vec, i); } @@ -176,7 +176,7 @@ TEST_CASE(test_string_macros_advanced) "loop", }; char** c = ssplit(a, '.'); - for (int i = 0; i < alen(c); ++i) { + for (int i = 0; i < asize(c); ++i) { REQUIRE(sequ(c[i], splits[i])); sfree(c[i]); } @@ -259,7 +259,7 @@ TEST_CASE(test_string_interning) /* Run Map API and sintern API */ TEST_CASE(test_dictionary_and_interning) { - Map h; + Map h; const char* a = "test 1"; const char* b = "test 2"; const char* ia = sintern(a); diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 000000000..ca81ad863 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,49 @@ +if (CF_RUNTIME_SHADER_COMPILATION OR CF_CUTE_SHADERC) + # cute-shader, a glslang wrapper library + set(CUTE_SHADER_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/cute_shader.cpp) + add_library(cute-shader STATIC ${CUTE_SHADER_SRCS}) + target_link_libraries(cute-shader PRIVATE glslang glslang-default-resource-limits SPIRV spirv-cross-c) + install(TARGETS cute-shader + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + if (NOT MSVC AND NOT CF_FRAMEWORK_STATIC) + set_target_properties(cute-shader PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + endif() + target_include_directories(cute-shader PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include) + + # Linking cute-shader for online shader compilation (optional). + if (CF_RUNTIME_SHADER_COMPILATION) + target_compile_definitions(cute PUBLIC CF_RUNTIME_SHADER_COMPILATION) + set(CF_LINK_LIBS ${CF_LINK_LIBS} cute-shader PARENT_SCOPE) + endif() + + # cute-shaderc, an offline shader compiler + if (CF_CUTE_SHADERC) + set(CUTE_SHADERC_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/cute_shaderc.cpp) + add_executable(cute-shaderc ${CUTE_SHADERC_SRCS}) + target_link_libraries(cute-shaderc cute-shader) + set_target_properties(cute-shaderc PROPERTIES FOLDER "tools") + install(TARGETS cute-shaderc + BUNDLE DESTINATION . + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() +endif() + +# docsparser, a standalone documentation generator (only depends on ckit.h) +# Only build when CF is the top-level project (not when used as subdirectory) +cmake_path(GET CMAKE_CURRENT_SOURCE_DIR PARENT_PATH CF_ROOT_DIR) +if (CF_ROOT_DIR STREQUAL CMAKE_SOURCE_DIR OR CF_BUILD_DOCSPARSER) + add_executable(docsparser ${CMAKE_CURRENT_SOURCE_DIR}/docs_parser.c) + target_include_directories(docsparser PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../libraries) + if(MSVC) + target_compile_options(docsparser PRIVATE /std:clatest /experimental:c11atomics) + endif() + set_target_properties(docsparser PROPERTIES + FOLDER "tools" + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} + ) +endif() diff --git a/src/cute_shader/builtin_shaders.h b/tools/builtin_shaders.h similarity index 76% rename from src/cute_shader/builtin_shaders.h rename to tools/builtin_shaders.h index 2892e6810..94efab063 100644 --- a/src/cute_shader/builtin_shaders.h +++ b/tools/builtin_shaders.h @@ -289,7 +289,7 @@ vec2 smooth_uv(vec2 uv, vec2 texture_size) // Stub function. This gets replaced by injected user-shader code via #include. static const char* s_shader_stub = R"( -vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) +vec4 shader(vec4 color, ShaderParams params) { return color; } @@ -299,81 +299,60 @@ vec4 shader(vec4 color, vec2 pos, vec2 screen_uv, vec4 params) // Primary cute_draw.h shader. static const char* s_draw_vs = R"( -layout (location = 0) in vec2 in_pos; -layout (location = 1) in vec2 in_posH; -layout (location = 2) in vec2 in_uv; - -layout (location = 3) in int in_n; -layout (location = 4) in vec4 in_ab; -layout (location = 5) in vec4 in_cd; -layout (location = 6) in vec4 in_ef; -layout (location = 7) in vec4 in_gh; -layout (location = 8) in vec4 in_col; -layout (location = 9) in float in_radius; -layout (location = 10) in float in_stroke; -layout (location = 11) in float in_aa; -layout (location = 12) in vec4 in_params; -layout (location = 13) in vec4 in_user_params; - -layout (location = 0) out vec2 v_pos; +layout (location = 0) in vec4 in_pos_uv; // pos.xy, uv.zw +layout (location = 1) in int in_n; +layout (location = 2) in vec4 in_ab; +layout (location = 3) in vec4 in_cd; +layout (location = 4) in vec4 in_ef; +layout (location = 5) in vec4 in_gh; +layout (location = 6) in vec4 in_col; +layout (location = 7) in vec4 in_shape; // radius, stroke, aa, type +layout (location = 8) in vec4 in_blend_posH; // alpha, fill, posH.xy +layout (location = 9) in vec4 in_user; +layout (location = 10) in vec4 in_uv_bounds; // uv_min.xy, uv_max.zw + +layout (location = 0) out vec4 v_pos_uv; layout (location = 1) out int v_n; layout (location = 2) out vec4 v_ab; layout (location = 3) out vec4 v_cd; layout (location = 4) out vec4 v_ef; layout (location = 5) out vec4 v_gh; -layout (location = 6) out vec2 v_uv; -layout (location = 7) out vec4 v_col; -layout (location = 8) out float v_radius; -layout (location = 9) out float v_stroke; -layout (location = 10) out float v_aa; -layout (location = 11) out float v_type; -layout (location = 12) out float v_alpha; -layout (location = 13) out float v_fill; -layout (location = 14) out vec2 v_posH; -layout (location = 15) out vec4 v_user; +layout (location = 6) out vec4 v_col; +layout (location = 7) out vec4 v_shape; +layout (location = 8) out vec4 v_blend_posH; +layout (location = 9) out vec4 v_user; +layout (location = 10) out vec4 v_uv_bounds; void main() { - v_pos = in_pos; + v_pos_uv = in_pos_uv; v_n = in_n; v_ab = in_ab; v_cd = in_cd; v_ef = in_ef; v_gh = in_gh; - v_uv = in_uv; v_col = in_col; - v_radius = in_radius; - v_stroke = in_stroke * 0.5; - v_aa = in_aa; - v_type = in_params.r; - v_alpha = in_params.g; - v_fill = in_params.b; - // unused = in_params.a; - - vec4 posH = vec4(in_posH, 0, 1); - gl_Position = posH; - v_posH = in_posH; - v_user = in_user_params; + v_shape = vec4(in_shape.x, in_shape.y * 0.5, in_shape.zw); + v_blend_posH = in_blend_posH; + v_user = in_user; + v_uv_bounds = in_uv_bounds; + + gl_Position = vec4(in_blend_posH.zw, 0, 1); } )"; static const char* s_draw_fs = R"( -layout (location = 0) in vec2 v_pos; +layout (location = 0) in vec4 v_pos_uv; layout (location = 1) in flat int v_n; layout (location = 2) in vec4 v_ab; layout (location = 3) in vec4 v_cd; layout (location = 4) in vec4 v_ef; layout (location = 5) in vec4 v_gh; -layout (location = 6) in vec2 v_uv; -layout (location = 7) in vec4 v_col; -layout (location = 8) in float v_radius; -layout (location = 9) in float v_stroke; -layout (location = 10) in float v_aa; -layout (location = 11) in float v_type; -layout (location = 12) in float v_alpha; -layout (location = 13) in float v_fill; -layout (location = 14) in vec2 v_posH; -layout (location = 15) in vec4 v_user; +layout (location = 6) in vec4 v_col; +layout (location = 7) in vec4 v_shape; +layout (location = 8) in vec4 v_blend_posH; +layout (location = 9) in vec4 v_user; +layout (location = 10) in vec4 v_uv_bounds; layout(location = 0) out vec4 result; @@ -382,32 +361,70 @@ layout (set = 2, binding = 0) uniform sampler2D u_image; layout (set = 3, binding = 0) uniform uniform_block { vec2 u_texture_size; int u_alpha_discard; + int u_use_smooth_uv; }; +// Used only for polygon SDF. +vec2 pts[8]; + +// Backwards-compat globals. +// Here for convenience for any legacy user draw shaders that reference them. +vec2 v_pos; +vec2 v_uv; +vec2 v_posH; +float v_radius; +float v_stroke; +float v_aa; +float v_type; +float v_alpha; +float v_fill; +vec2 pos; +vec2 screen_uv; + #include "blend.shd" #include "gamma.shd" #include "smooth_uv.shd" #include "distance.shd" -#include "shader_stub.shd" -// Used only for polygon SDF. -vec2 pts[8]; +struct ShaderParams +{ + vec2 view_pos; + vec2 uv; + vec2 uv_min; + vec2 uv_max; + vec2 screen_uv; + vec4 attributes; +}; + +#include "shader_stub.shd" void main() { - bool is_sprite = v_type >= (0.0/255.0) && v_type < (0.5/255.0); - bool is_text = v_type > (0.5/255.0) && v_type < (1.5/255.0); - bool is_box = v_type > (1.5/255.0) && v_type < (2.5/255.0); - bool is_seg = v_type > (2.5/255.0) && v_type < (3.5/255.0); - bool is_tri = v_type > (3.5/255.0) && v_type < (4.5/255.0); - bool is_tri_sdf = v_type > (4.5/255.0) && v_type < (5.5/255.0); - bool is_poly = v_type > (5.5/255.0) && v_type < (6.5/255.0); + // Unpack into backwards-compat globals. + v_pos = v_pos_uv.xy; + v_uv = v_pos_uv.zw; + v_radius = v_shape.x; + v_stroke = v_shape.y; + v_aa = v_shape.z; + v_type = v_shape.w; + v_alpha = v_blend_posH.x; + v_fill = v_blend_posH.y; + v_posH = v_blend_posH.zw; + + bool is_sprite = v_type >= 0.0 && v_type < 0.5; + bool is_text = v_type > 0.5 && v_type < 1.5; + bool is_box = v_type > 1.5 && v_type < 2.5; + bool is_seg = v_type > 2.5 && v_type < 3.5; + bool is_tri = v_type > 3.5 && v_type < 4.5; + bool is_tri_sdf = v_type > 4.5 && v_type < 5.5; + bool is_poly = v_type > 5.5 && v_type < 6.5; // Traditional sprite/text/tri cases. vec4 c = vec4(0); - c = !(is_sprite && is_text) ? de_gamma(texture(u_image, smooth_uv(v_uv, u_texture_size))) : c; - c = is_sprite ? gamma(c) : c; - c = is_text ? v_col * c.a : c; + vec2 uv = u_use_smooth_uv == 0 ? smooth_uv(v_uv, u_texture_size) : v_uv; + vec4 tex_c = de_gamma(texture(u_image, uv)); + c = is_sprite ? gamma(tex_c) : c; + c = is_text ? v_col * tex_c.a : c; c = is_tri ? v_col : c; // SDF cases. @@ -433,8 +450,16 @@ void main() c = (!is_sprite && !is_text && !is_tri) ? sdf(c, v_col, d - v_radius) : c; c *= v_alpha; - vec2 screen_uv = (v_posH + vec2(1,-1)) * 0.5 * vec2(1,-1); - c = shader(c, v_pos, screen_uv, v_user); + pos = v_pos; + screen_uv = (v_posH + vec2(1,-1)) * 0.5 * vec2(1,-1); + ShaderParams sp; + sp.view_pos = pos; + sp.uv = v_uv; + sp.uv_min = v_uv_bounds.xy; + sp.uv_max = v_uv_bounds.zw; + sp.screen_uv = screen_uv; + sp.attributes = v_user; + c = shader(c, sp); if (u_alpha_discard != 0 && c.a == 0) discard; result = c; } @@ -479,12 +504,34 @@ layout (set = 3, binding = 0) uniform uniform_block { }; #include "smooth_uv.shd" + +vec2 pos; +vec2 screen_uv; + +struct ShaderParams +{ + vec2 view_pos; + vec2 uv; + vec2 uv_min; + vec2 uv_max; + vec2 screen_uv; + vec4 attributes; +}; + #include "shader_stub.shd" void main() { vec4 color = texture(u_image, smooth_uv(v_uv, u_texture_size)); - vec2 screen_uv = (v_posH + vec2(1,-1)) * 0.5 * vec2(1,-1); - vec4 c = shader(color, v_pos, screen_uv, v_params); + pos = v_pos; + screen_uv = (v_posH + vec2(1,-1)) * 0.5 * vec2(1,-1); + ShaderParams sp; + sp.view_pos = pos; + sp.uv = v_uv; + sp.uv_min = vec2(0); + sp.uv_max = vec2(0); + sp.screen_uv = screen_uv; + sp.attributes = v_params; + vec4 c = shader(color, sp); if (u_alpha_discard != 0 && c.a == 0) discard; result = c; } diff --git a/src/cute_shader/cute_shader.cpp b/tools/cute_shader.cpp similarity index 92% rename from src/cute_shader/cute_shader.cpp rename to tools/cute_shader.cpp index e6cbfdc3e..65ebb7928 100644 --- a/src/cute_shader/cute_shader.cpp +++ b/tools/cute_shader.cpp @@ -265,6 +265,7 @@ CF_ShaderCompilerResult cute_shader_compile(const char* source, CF_ShaderCompile switch (stage) { case CUTE_SHADER_STAGE_VERTEX: glslang_stage = EShLangVertex; break; case CUTE_SHADER_STAGE_FRAGMENT: glslang_stage = EShLangFragment; break; + case CUTE_SHADER_STAGE_COMPUTE: glslang_stage = EShLangCompute; break; } glslang::TShader shader(glslang_stage); @@ -360,11 +361,12 @@ CF_ShaderCompilerResult cute_shader_compile(const char* source, CF_ShaderCompile void* bytecode = malloc(bytecode_size); memcpy(bytecode, spirv.data(), bytecode_size); - // GLSL 300 transpilation + // GLSL 300 transpilation (skipped for compute -- GLES 3.0 has no compute support). spvc_context spvc = NULL; - spvc_context_create(&spvc); char* glsl300_src = NULL; size_t glsl300_src_size = 0; + if (stage != CUTE_SHADER_STAGE_COMPUTE) { + spvc_context_create(&spvc); { spvc_parsed_ir ir = NULL; spvc_context_parse_spirv( @@ -453,13 +455,17 @@ CF_ShaderCompilerResult cute_shader_compile(const char* source, CF_ShaderCompile spvc_context_release_allocations(spvc); } spvc_context_destroy(spvc); + } // stage != CUTE_SHADER_STAGE_COMPUTE // Reflection int num_samplers = 0; int num_storage_textures = 0; int num_storage_buffers = 0; + int num_readwrite_storage_textures = 0; + int num_readwrite_storage_buffers = 0; int num_images; const char** image_names = NULL; + int* image_binding_slots = NULL; int num_uniforms; CF_ShaderUniformInfo* uniforms = NULL; int num_uniform_members; @@ -468,6 +474,7 @@ CF_ShaderCompilerResult cute_shader_compile(const char* source, CF_ShaderCompile CF_ShaderInputInfo* inputs = NULL; { std::vector image_names_vec; + std::vector image_binding_slots_vec; std::vector uniforms_vec; std::vector uniform_members_vec; @@ -485,10 +492,17 @@ CF_ShaderCompilerResult cute_shader_compile(const char* source, CF_ShaderCompile case SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: { image_names_vec.push_back(strdup(binding->name)); + image_binding_slots_vec.push_back(binding->binding); } // Fall-thru. case SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLER: ++num_samplers; break; - case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: ++num_storage_textures; break; - case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: ++num_storage_buffers; break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_IMAGE: + if (binding->decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE) ++num_storage_textures; + else ++num_readwrite_storage_textures; + break; + case SPV_REFLECT_DESCRIPTOR_TYPE_STORAGE_BUFFER: + if (binding->decoration_flags & SPV_REFLECT_DECORATION_NON_WRITABLE) ++num_storage_buffers; + else ++num_readwrite_storage_buffers; + break; case SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER: { // Grab information about the uniform block. @@ -523,11 +537,26 @@ CF_ShaderCompilerResult cute_shader_compile(const char* source, CF_ShaderCompile } free(bindings); + // Sort images by binding slot so array index matches SDL_GPU slot. + { + int n = (int)image_names_vec.size(); + for (int i = 0; i < n - 1; ++i) { + for (int j = i + 1; j < n; ++j) { + if (image_binding_slots_vec[j] < image_binding_slots_vec[i]) { + std::swap(image_names_vec[i], image_names_vec[j]); + std::swap(image_binding_slots_vec[i], image_binding_slots_vec[j]); + } + } + } + } + // Prepare the returned reflection info num_images = (int)image_names_vec.size(); if (num_images > 0) { image_names = (const char**)malloc(sizeof(char*) * num_images); memcpy(image_names, image_names_vec.data(), sizeof(char*) * num_images); + image_binding_slots = (int*)malloc(sizeof(int) * num_images); + memcpy(image_binding_slots, image_binding_slots_vec.data(), sizeof(int) * num_images); } num_uniforms = (int)uniforms_vec.size(); @@ -576,9 +605,12 @@ CF_ShaderCompilerResult cute_shader_compile(const char* source, CF_ShaderCompile .num_samplers = num_samplers, .num_storage_textures = num_storage_textures, .num_storage_buffers = num_storage_buffers, + .num_readwrite_storage_textures = num_readwrite_storage_textures, + .num_readwrite_storage_buffers = num_readwrite_storage_buffers, .num_images = num_images, .image_names = image_names, + .image_binding_slots = image_binding_slots, .num_uniforms = num_uniforms, .uniforms = uniforms, @@ -619,6 +651,7 @@ void cute_shader_free_result(CF_ShaderCompilerResult result) free((char*)shader_info->image_names[i]); } free(shader_info->image_names); + free(shader_info->image_binding_slots); free((void*)result.bytecode.glsl300_src); free((void*)result.bytecode.content); diff --git a/src/cute_shader/cute_shader.h b/tools/cute_shader.h similarity index 98% rename from src/cute_shader/cute_shader.h rename to tools/cute_shader.h index 3378a9b55..0f2f9b978 100644 --- a/src/cute_shader/cute_shader.h +++ b/tools/cute_shader.h @@ -27,6 +27,7 @@ typedef enum CF_ShaderCompilerStage { CUTE_SHADER_STAGE_VERTEX, CUTE_SHADER_STAGE_FRAGMENT, + CUTE_SHADER_STAGE_COMPUTE, } CF_ShaderCompilerStage; typedef struct CF_ShaderCompilerConfig diff --git a/src/cute_shader/cute_shaderc.cpp b/tools/cute_shaderc.cpp similarity index 94% rename from src/cute_shader/cute_shaderc.cpp rename to tools/cute_shaderc.cpp index 4c7cfee35..4eeba66e0 100644 --- a/src/cute_shader/cute_shaderc.cpp +++ b/tools/cute_shaderc.cpp @@ -26,6 +26,7 @@ typedef enum { SHADER_TYPE_VERTEX, SHADER_TYPE_FRAGMENT, + SHADER_TYPE_COMPUTE, SHADER_TYPE_DRAW, SHADER_TYPE_BUILTIN, } shader_type_t; @@ -121,8 +122,14 @@ static bool write_bytecode( fprintf(file, " \"%s\",", shader_info->image_names[i]); } fprintf(file, "\n};\n"); + fprintf(file, "static int %s%s_image_binding_slots[%d] = {", var_name, suffix, shader_info->num_images); + for (int i = 0; i < shader_info->num_images; ++i) { + fprintf(file, " %d,", shader_info->image_binding_slots[i]); + } + fprintf(file, " };\n"); } else { fprintf(file, "#define %s%s_image_names NULL\n", var_name, suffix); + fprintf(file, "#define %s%s_image_binding_slots NULL\n", var_name, suffix); } if (shader_info->num_uniforms > 0) { @@ -195,8 +202,11 @@ static bool write_bytecode_struct_contents( TABS(); fprintf(file, "\t.num_samplers = %d,\n", shader_info->num_samplers); TABS(); fprintf(file, "\t.num_storage_textures = %d,\n", shader_info->num_storage_textures); TABS(); fprintf(file, "\t.num_storage_buffers = %d,\n", shader_info->num_storage_buffers); + TABS(); fprintf(file, "\t.num_readwrite_storage_textures = %d,\n", shader_info->num_readwrite_storage_textures); + TABS(); fprintf(file, "\t.num_readwrite_storage_buffers = %d,\n", shader_info->num_readwrite_storage_buffers); TABS(); fprintf(file, "\t.num_images = %d,\n", shader_info->num_images); TABS(); fprintf(file, "\t.image_names = %s%s_image_names,\n", var_name, suffix); + TABS(); fprintf(file, "\t.image_binding_slots = %s%s_image_binding_slots,\n", var_name, suffix); TABS(); fprintf(file, "\t.num_uniforms = %d,\n", shader_info->num_uniforms); TABS(); fprintf(file, "\t.uniforms = %s%s_uniforms,\n", var_name, suffix); TABS(); fprintf(file, "\t.num_uniform_members = %d,\n", shader_info->num_uniform_members); @@ -380,6 +390,8 @@ int main(int argc, const char* argv[]) type = SHADER_TYPE_VERTEX; } else if (strcmp(flag_value, "fragment") == 0) { type = SHADER_TYPE_FRAGMENT; + } else if (strcmp(flag_value, "compute") == 0) { + type = SHADER_TYPE_COMPUTE; } else if (strcmp(flag_value, "draw") == 0) { type = SHADER_TYPE_DRAW; } else if (strcmp(flag_value, "builtin") == 0) { @@ -441,10 +453,10 @@ int main(int argc, const char* argv[]) } if ( - !(type == SHADER_TYPE_VERTEX || type == SHADER_TYPE_FRAGMENT) + !(type == SHADER_TYPE_VERTEX || type == SHADER_TYPE_FRAGMENT || type == SHADER_TYPE_COMPUTE) && output_bytecode_path != NULL ) { - fprintf(stderr, "%s is only valid for shader of type 'vertex' or 'fragment'\n", FLAG_BYTECODE_OUT); + fprintf(stderr, "%s is only valid for shader of type 'vertex', 'fragment', or 'compute'\n", FLAG_BYTECODE_OUT); return 1; } } @@ -624,11 +636,13 @@ int main(int argc, const char* argv[]) .return_preprocessed_source = true, }; + CF_ShaderCompilerStage compile_stage = CUTE_SHADER_STAGE_FRAGMENT; + if (type == SHADER_TYPE_VERTEX) compile_stage = CUTE_SHADER_STAGE_VERTEX; + else if (type == SHADER_TYPE_COMPUTE) compile_stage = CUTE_SHADER_STAGE_COMPUTE; + CF_ShaderCompilerResult result = cute_shader_compile( input_content, - type == SHADER_TYPE_VERTEX - ? CUTE_SHADER_STAGE_VERTEX - : CUTE_SHADER_STAGE_FRAGMENT, + compile_stage, config ); if (!result.success) { diff --git a/tools/docs_parser.c b/tools/docs_parser.c new file mode 100644 index 000000000..d683d80a6 --- /dev/null +++ b/tools/docs_parser.c @@ -0,0 +1,1098 @@ +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +// Parses all the Cute headers and generates documentation pages in .md format. +// Only dependency is ckit.h (no CF runtime/SDL required). + +#define CKIT_IMPLEMENTATION +#include "cute/ckit.h" + +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#define dp_mkdir(path) _mkdir(path) +#else +#include +#include +#define dp_mkdir(path) mkdir(path, 0755) +#endif + +// ------------------------------------------------------------------------------------------------- +// Helpers + +static void panic(const char* msg) +{ + printf("ERROR: %s\n", msg); + exit(-1); +} + +static int s_is_space(int cp) +{ + switch (cp) { + case ' ': + case '\n': + case '\t': + case '\v': + case '\f': + case '\r': return 1; + default: return 0; + } +} + +// Left-aligns a code snippet by removing the minimum common leading whitespace from all lines. +static char* left_align(char* text) +{ + if (!text || !slen(text)) return text; + + // Find minimum leading spaces across all non-empty lines + int min_indent = INT_MAX; + const char* p = text; + while (*p) { + // Count leading spaces on this line + int indent = 0; + while (*p == ' ' || *p == '\t') { + indent += (*p == '\t') ? 4 : 1; + p++; + } + // Only consider non-empty lines + if (*p && *p != '\n' && *p != '\r') { + if (indent < min_indent) min_indent = indent; + } + // Skip to next line + while (*p && *p != '\n') p++; + if (*p == '\n') p++; + } + + if (min_indent == 0 || min_indent == INT_MAX) return text; + + // Build new string with indentation removed + char* result = NULL; + p = text; + while (*p) { + // Skip min_indent worth of whitespace + int skipped = 0; + while (skipped < min_indent && (*p == ' ' || *p == '\t')) { + skipped += (*p == '\t') ? 4 : 1; + p++; + } + // Copy rest of line + while (*p && *p != '\n') { + spush(result, *p); + p++; + } + if (*p == '\n') { + spush(result, *p); + p++; + } + } + + sfree(text); + return result; +} + +static char* read_file(const char* path, size_t* out_size) +{ + FILE* fp = fopen(path, "rb"); + if (!fp) return NULL; + fseek(fp, 0, SEEK_END); + size_t sz = (size_t)ftell(fp); + fseek(fp, 0, SEEK_SET); + char* data = (char*)malloc(sz + 1); + fread(data, 1, sz, fp); + data[sz] = 0; + fclose(fp); + if (out_size) *out_size = sz; + return data; +} + +static int dir_exists(const char* path) +{ +#ifdef _WIN32 + struct _finddata_t fd; + intptr_t h = _findfirst(path, &fd); + if (h == -1) return 0; + int result = (fd.attrib & _A_SUBDIR) != 0; + _findclose(h); + return result; +#else + struct stat st; + return stat(path, &st) == 0 && S_ISDIR(st.st_mode); +#endif +} + +// ------------------------------------------------------------------------------------------------- +// Doc types + +enum { DOC_EMPTY, DOC_ENUM, DOC_FUNCTION, DOC_STRUCT }; + +typedef struct Doc +{ + int type; + char* path; // sdyna + char* web_category; // sdyna + char* title; // sdyna + char* file; // sdyna + char* brief; // sdyna + int this_index; + char* signature; // sdyna + char** param_names; // dyna of sdyna + char** param_briefs; // dyna of sdyna + char** enum_entries; // dyna of sdyna + char** enum_entry_briefs; // dyna of sdyna + char** member_briefs; // dyna of sdyna + int* member_functions; // dyna + char** members; // dyna of sdyna + char* return_value; // sdyna + char* example_brief; // sdyna + char* example; // sdyna + char* remarks; // sdyna + char** related; // dyna of sdyna +} Doc; + +static Doc make_doc() +{ + Doc d; + memset(&d, 0, sizeof(d)); + d.this_index = -1; + return d; +} + +// ------------------------------------------------------------------------------------------------- +// State + +typedef struct State +{ + const char* in; + const char* end; + char* token; // sdyna + char* file; // sdyna + + CK_MAP(const char*) categories; + CK_MAP(const char**) category_index_lists; // value is dyna array of interned strings + + Doc doc; + Doc* docs; // dyna + CK_MAP(int) page_to_doc_index; +} State; + +static State state; +static State* s = &state; + +// ------------------------------------------------------------------------------------------------- +// Relative path + +static const char* g_relative_path; + +static const char* get_relative_path() +{ + return g_relative_path; +} + +// ------------------------------------------------------------------------------------------------- +// State helpers + +static void flush_doc() +{ + char* path = smake(get_relative_path()); + char ch = '/'; + spush(path, ch); + sappend(path, s->doc.web_category); + + char* title_lower = smake(s->doc.title); + stolower(title_lower); + sreplace(title_lower, " ", "_"); + sappend(title_lower, ".md"); + + spush(path, ch); + sappend(path, title_lower); + + sfree(s->doc.path); + s->doc.path = path; + sfree(s->doc.file); + s->doc.file = smake(s->file); + + const char* key = sintern(title_lower); + if (map_has(s->page_to_doc_index, (uint64_t)key)) { + int idx = map_get(s->page_to_doc_index, (uint64_t)key); + char* msg = sfmake("Tried to add a duplicate page for %s (found in file %s, previously seen in file %s).", + title_lower, s->doc.file, s->docs[idx].file); + panic(msg); + } + int count = asize(s->docs); + map_set(s->page_to_doc_index, (uint64_t)key, count); + apush(s->docs, s->doc); + s->doc = make_doc(); + sfree(title_lower); +} + +static int get_doc_index(const char* title_raw) +{ + char* title = smake(title_raw); + stolower(title); + sreplace(title, " ", "_"); + sappend(title, ".md"); + const char* key = sintern(title); + int* ptr = map_get_ptr(s->page_to_doc_index, (uint64_t)key); + sfree(title); + return ptr ? *ptr : -1; +} + +static int doc_has_link(const char* title) +{ + return get_doc_index(title) != -1; +} + +static char* doc_get_link(const char* title) +{ + int index = get_doc_index(title); + char* link = smake(s->docs[index].path); + sreplace(link, get_relative_path(), ""); + return link; +} + +static int has_doc(const char* file) +{ + const char* key = sintern(file); + return map_has(s->page_to_doc_index, (uint64_t)key); +} + +// ------------------------------------------------------------------------------------------------- +// Parsing helpers + +static void state_clear() { sfree(s->token); s->token = NULL; s->in = s->end = NULL; } +static int state_done() { return s->in >= s->end; } +static void state_append(int ch) { spush(s->token, (char)ch); } +static void state_ltrim() { while (!state_done()) { int cp = *s->in; if (s_is_space(cp)) ++s->in; else break; } } +static int state_next() { int cp; s->in = decode_UTF8(s->in, &cp); return cp; } +static int state_peek() { int cp; decode_UTF8(s->in, &cp); return cp; } +static void state_skip() { int cp; s->in = decode_UTF8(s->in, &cp); (void)cp; } +static int state_expect(int ch) { int cp = state_next(); return cp == ch; } +static int state_try_next(int ch) { int cp; const char* next = decode_UTF8(s->in, &cp); if (cp == ch) { s->in = next; return 1; } return 0; } + +// ------------------------------------------------------------------------------------------------- +// Linkify + +static char* linkify(char* text, const char* scan_str, int ticks) +{ + if (doc_has_link(scan_str)) { + char* link = doc_get_link(scan_str); + char* coded_link = sfmake("[%s](..%s)", scan_str, link); + char* scan_fmt = ticks ? sfmake("`%s`", scan_str) : smake(scan_str); + sreplace(text, scan_fmt, coded_link); + sfree(link); + sfree(coded_link); + sfree(scan_fmt); + } + return text; +} + +// ------------------------------------------------------------------------------------------------- +// Emit functions + +static void emit_title(FILE* fp, Doc* doc) +{ + fprintf(fp, "[//]: # (This file is automatically generated by Cute Framework's docs parser.)\n"); + fprintf(fp, "[//]: # (Do not edit this file by hand!)\n"); + fprintf(fp, "[//]: # (See: https://github.com/RandyGaul/cute_framework/blob/master/tools/docs_parser.c)\n"); + fprintf(fp, "# %s\n\n", doc->title); + char* link_lower = smake(doc->web_category); + stolower(link_lower); + char* link = linkify(smake(doc->web_category), doc->web_category, 0); + fprintf(fp, "Category: [%s](../api_reference.md#%s) \n", doc->web_category, link_lower); + fprintf(fp, "GitHub: [%s](https://github.com/RandyGaul/cute_framework/blob/master/include/%s) \n---\n\n", doc->file, doc->file); + sfree(link_lower); + sfree(link); +} + +static void emit_brief(FILE* fp, Doc* doc) +{ + fprintf(fp, "%s\n\n", doc->brief); +} + +static void emit_signature(FILE* fp, Doc* doc) +{ + fprintf(fp, "```cpp\n%s\n```\n\n", doc->signature); +} + +static void emit_members(FILE* fp, Doc* doc) +{ + if (asize(doc->members)) { + fprintf(fp, "Struct Members | Description\n--- | ---\n"); + for (int i = 0; i < asize(doc->members); ++i) { + fprintf(fp, "`%s` | %s\n", doc->members[i], doc->member_briefs[i]); + } + fprintf(fp, "\n"); + } +} + +static void emit_member_function_links(FILE* fp, Doc* doc) +{ + if (asize(doc->member_functions)) { + fprintf(fp, "Functions | Description\n--- | ---\n"); + for (int i = 0; i < asize(doc->member_functions); ++i) { + int index = doc->member_functions[i]; + Doc* d = &s->docs[index]; + fprintf(fp, "%s | %s\n", d->title, d->brief); + } + fprintf(fp, "\n"); + } +} + +static void emit_params(FILE* fp, Doc* doc) +{ + if (asize(doc->param_names)) { + fprintf(fp, "Parameters | Description\n--- | ---\n"); + for (int i = 0; i < asize(doc->param_names); ++i) { + fprintf(fp, "`%s` | %s\n", doc->param_names[i], doc->param_briefs[i]); + } + fprintf(fp, "\n"); + } +} + +static void emit_enum_entries(FILE* fp, Doc* doc) +{ + fprintf(fp, "## Values\n\n"); + fprintf(fp, "Enum | Description\n--- | ---\n"); + for (int i = 0; i < asize(doc->enum_entries); ++i) { + fprintf(fp, "`%s` | %s\n", doc->enum_entries[i], doc->enum_entry_briefs[i]); + } + fprintf(fp, "\n"); +} + +static void emit_return(FILE* fp, Doc* doc) +{ + if (doc->return_value && slen(doc->return_value)) { + fprintf(fp, "## Return Value\n\n%s\n\n", doc->return_value); + } +} + +static void emit_example(FILE* fp, Doc* doc) +{ + if (doc->example && slen(doc->example)) { + fprintf(fp, "## Code Example\n\n"); + if (doc->example_brief && slen(doc->example_brief)) { + fprintf(fp, "%s\n\n", doc->example_brief); + } + fprintf(fp, "```cpp%s```\n\n", doc->example); + } +} + +static void emit_remarks(FILE* fp, Doc* doc) +{ + if (doc->remarks && slen(doc->remarks)) { + fprintf(fp, "## Remarks\n\n"); + fprintf(fp, "%s\n\n", doc->remarks); + } +} + +static void emit_related(FILE* fp, Doc* doc) +{ + if (asize(doc->related)) { + fprintf(fp, "## Related Pages\n\n"); + for (int i = 0; i < asize(doc->related); ++i) { + fprintf(fp, "%s\n", doc->related[i]); + } + } +} + +// ------------------------------------------------------------------------------------------------- +// Parsing + +static void parse_token() +{ + sfree(s->token); + s->token = NULL; + while (!state_done()) { + int cp = state_next(); + if (s_is_space(cp)) { + if (s->token && slen(s->token)) { + return; + } + } else { + state_append(cp); + } + } +} + +static char* parse_single_comment_line() +{ + char* line = NULL; + if (state_try_next(' ')) { + if (state_try_next('*')) { + if (state_try_next('/')) { + s->in--; + s->in--; + return line; + } + } + } + while (!state_done()) { + int cp = state_peek(); + if (cp == '\n') { + break; + } else { + state_skip(); + spush(line, (char)cp); + } + } + strim(line); + return line; +} + +static char* parse_paragraph(int is_example) +{ + char* paragraph = NULL; + while (!state_done()) { + int cp = state_peek(); + if (cp == '\n') { + spush(paragraph, (char)cp); + state_skip(); + // Try to consume comment prefix: optional space + asterisk + optional space + // This handles both " * " (proper format) and "* " (malformed without leading space) + state_try_next(' '); + if (state_try_next('*')) { + if (state_try_next('/')) { + s->in--; + s->in--; + break; + } else { + state_try_next(' '); + } + } + } else if (cp == '@') { + break; + } else if (cp == '*') { + state_skip(); + if (state_try_next('/')) { + s->in--; + s->in--; + break; + } else { + // Preserve asterisks that are part of content (not comment delimiters) + spush(paragraph, '*'); + } + } else { + if (cp != '\r') { + spush(paragraph, (char)cp); + } + state_skip(); + } + } + if (!is_example && paragraph) { + strim(paragraph); + sreplace(paragraph, "\n ", "\n"); + } + return paragraph; +} + +static void parse_command() +{ + if (sequ(s->token, "@brief")) { + sfree(s->doc.brief); + s->doc.brief = parse_paragraph(0); + } else if (sequ(s->token, "@example")) { + sfree(s->doc.example_brief); + s->doc.example_brief = parse_single_comment_line(); + char* ex = parse_paragraph(1); + ex = left_align(ex); + sfree(s->doc.example); + s->doc.example = ex; + } else if (sequ(s->token, "@remarks")) { + sfree(s->doc.remarks); + s->doc.remarks = parse_paragraph(0); + } else if (sequ(s->token, "@related")) { + char* text = parse_paragraph(0); + // Split by space. + char** parts = ssplit(text, ' '); + for (int i = 0; i < asize(parts); ++i) { + apush(s->doc.related, parts[i]); + } + afree(parts); // Free the outer array, not the inner strings (they're moved to related) + sfree(text); + } else if (sequ(s->token, "@param")) { + parse_token(); + apush(s->doc.param_names, smake(s->token)); + apush(s->doc.param_briefs, parse_paragraph(0)); + } else if (sequ(s->token, "@return")) { + sfree(s->doc.return_value); + s->doc.return_value = parse_paragraph(0); + } else if (sequ(s->token, "@category")) { + sfree(s->doc.web_category); + s->doc.web_category = parse_paragraph(0); + const char* category = sintern(s->doc.web_category); + if (!map_has(s->categories, (uint64_t)category)) { + map_set(s->categories, (uint64_t)category, category); + } + } else { + char* msg = sfmake("Found unrecognized command %s.", s->token); + panic(msg); + } +} + +static void parse_enum() +{ + s->doc.type = DOC_ENUM; + sfree(s->doc.title); + s->doc.title = parse_paragraph(0); + while (!state_done()) { + parse_token(); + if (s->token && slen(s->token) && sfirst(s->token) == '@') { + parse_command(); + } else if (sequ(s->token, "*/")) { + break; + } + } + while (!state_done()) { + parse_token(); + if (s->token && slen(s->token) && sfirst(s->token) == '@') { + if (sequ(s->token, "@entry")) { + apush(s->doc.enum_entry_briefs, parse_paragraph(0)); + } else if (sequ(s->token, "@end")) { + break; + } else { + char* msg = sfmake("Expected to find @entry or @end, but instead found %s.", s->token); + panic(msg); + } + } else if (s->token && sprefix(s->token, "CF_ENUM(")) { + sreplace(s->token, "CF_ENUM(", ""); + spop(s->token); // Remove ',' + char* entry = sfmake("CF_%s", s->token); + apush(s->doc.enum_entries, entry); + } + } + if (asize(s->doc.enum_entries) != asize(s->doc.enum_entry_briefs)) { + int a = asize(s->doc.enum_entries); + int b = asize(s->doc.enum_entry_briefs); + char* msg = sfmake("Enum entries count %d did not match entry description count %d.", a, b); + panic(msg); + } + flush_doc(); +} + +static char* parse_line() +{ + char* line = NULL; + state_try_next('\n'); + while (!state_done()) { + int cp = state_peek(); + if (cp == '\n') { + break; + } else { + state_skip(); + spush(line, (char)cp); + } + } + strim(line); + return line; +} + +static void parse_comment_block() +{ + sfree(s->doc.title); + s->doc.title = parse_paragraph(0); + int iter = 0; + while (!state_done()) { + iter++; + parse_token(); + if (s->token && slen(s->token) && sfirst(s->token) == '@') { + parse_command(); + } else if (sequ(s->token, "*/")) { + break; + } + } +} + +static void parse_function() +{ + s->doc.type = DOC_FUNCTION; + parse_comment_block(); + char* sig = parse_line(); + sreplace(sig, "CF_API ", ""); + sreplace(sig, "CF_CALL ", ""); + sreplace(sig, "CF_INLINE ", ""); + char* brace = sfind(sig, " {"); + if (brace) { + asetlen(sig, (int)(brace - sig)); + } + sfree(s->doc.signature); + s->doc.signature = sig; + flush_doc(); +} + +static void parse_struct() +{ + s->doc.type = DOC_STRUCT; + parse_comment_block(); + int doc_index = asize(s->docs); + flush_doc(); + Doc* doc = &s->docs[asize(s->docs) - 1]; + while (!state_done()) { + parse_token(); + if (s->token && slen(s->token) && sfirst(s->token) == '@') { + if (sequ(s->token, "@member")) { + char* brief = parse_paragraph(0); + parse_token(); // Skip "*/" + char* member = parse_line(); + sreplace(member, "CF_INLINE ", ""); + sreplace(member, ";", ""); + apush(doc->member_briefs, brief); + apush(doc->members, member); + } else if (sequ(s->token, "@function")) { + apush(doc->member_functions, asize(s->docs)); + parse_function(); + s->docs[asize(s->docs) - 1].this_index = doc_index; + doc = &s->docs[doc_index]; // Re-fetch, docs array may have reallocated. + } else if (sequ(s->token, "@end")) { + break; + } else { + char* msg = sfmake("Found unexpected command %s while parsing a struct, @member, @end, or @function.", s->token); + panic(msg); + } + } + } +} + +static void parse_header(const char* include_dir, const char* header) +{ + char* fullpath = smake(include_dir); + char sep = '/'; + spush(fullpath, sep); + sappend(fullpath, header); + + size_t sz = 0; + char* in = read_file(fullpath, &sz); + sfree(fullpath); + if (!in) return; + + state_clear(); + s->in = in; + s->end = in + sz; + sfree(s->file); + s->file = smake(header); + + while (!state_done()) { + parse_token(); + if (s->token && slen(s->token) && sfirst(s->token) == '@') { + if (sequ(s->token, "@enum")) { + parse_enum(); + } else if (sequ(s->token, "@function")) { + parse_function(); + } else if (sequ(s->token, "@struct")) { + parse_struct(); + } else { + char* msg = sfmake("Found unexpected command %s, expected @enum, @function, or @struct.", s->token); + panic(msg); + } + } + } + + free(in); +} + +// ------------------------------------------------------------------------------------------------- +// Auto-generate links + +static char* auto_generate_links(char* text) +{ + if (!text) return text; + char* scan = NULL; + + int found = 0; + for (int i = 0; i < slen(text); ++i) { + char ch = text[i]; + if (found) { + if (ch != '`') { + spush(scan, ch); + } else { + text = linkify(text, scan, 1); + found = 0; + sfree(scan); + scan = NULL; + } + } else { + if (ch == '`') { + found = 1; + } + } + } + sfree(scan); + return text; +} + +// ------------------------------------------------------------------------------------------------- +// String comparison for qsort + +static int cmp_istr(const void* a, const void* b) +{ + const char* sa = *(const char**)a; + const char* sb = *(const char**)b; + return sicmp(sa, sb); +} + +// ------------------------------------------------------------------------------------------------- +// Save API reference links + +static void save_api_reference_links(FILE* fp, const char* category, const char** pages, int page_count, const char* type, + CK_MAP(const char**) related) +{ + if (page_count) { + fprintf(fp, "### %s\n", type); + for (int i = 0; i < page_count; ++i) { + char* page_lower = smake(pages[i]); + stolower(page_lower); + sreplace(page_lower, " ", "_"); + fprintf(fp, "- [%s](%s/%s.md)\n", pages[i], category, page_lower); + sfree(page_lower); + } + fprintf(fp, "\n\n"); + } + const char*** related_ptr = map_get_ptr(related, (uint64_t)sintern(category)); + if (related_ptr && *related_ptr) { + const char** related_links = *related_ptr; + if (asize(related_links)) { + fprintf(fp, "### Related Reading\n"); + for (int i = 0; i < asize(related_links); ++i) { + fprintf(fp, "%s\n", related_links[i]); + } + fprintf(fp, "\n"); + } + } +} + +// ------------------------------------------------------------------------------------------------- +// Directory iteration + +typedef struct DirIter +{ +#ifdef _WIN32 + intptr_t handle; + struct _finddata_t data; + int first; +#else + DIR* dir; + struct dirent* ent; +#endif +} DirIter; + +static DirIter dir_open(const char* path) +{ + DirIter it; + memset(&it, 0, sizeof(it)); +#ifdef _WIN32 + char* pattern = smake(path); + sappend(pattern, "/*"); + it.handle = _findfirst(pattern, &it.data); + it.first = 1; + sfree(pattern); +#else + it.dir = opendir(path); +#endif + return it; +} + +static const char* dir_next(DirIter* it) +{ +#ifdef _WIN32 + if (it->handle == -1) return NULL; + if (it->first) { + it->first = 0; + return it->data.name; + } + if (_findnext(it->handle, &it->data) == 0) { + return it->data.name; + } + return NULL; +#else + if (!it->dir) return NULL; + it->ent = readdir(it->dir); + return it->ent ? it->ent->d_name : NULL; +#endif +} + +static void dir_close(DirIter* it) +{ +#ifdef _WIN32 + if (it->handle != -1) _findclose(it->handle); +#else + if (it->dir) closedir(it->dir); +#endif +} + +// ------------------------------------------------------------------------------------------------- +// Main + +int main(int argc, char* argv[]) +{ + (void)argc; + (void)argv; + + // Find the cute_framework root directory. + // We expect to be run from: cute_framework/build/Debug/ (or similar). + // Or accept a command-line argument for the root. + char* cf_root = NULL; + if (argc > 1) { + cf_root = smake(argv[1]); + char* test_include = sfmake("%s/include", cf_root); + char* test_docs = sfmake("%s/docs", cf_root); + if (!dir_exists(test_include) || !dir_exists(test_docs)) { + fprintf(stderr, "Error: '%s' does not look like the cute_framework root (missing include/ or docs/ subdirectory).\n", argv[1]); + fprintf(stderr, "Usage: docsparser [path-to-cute-framework-root]\n"); + exit(1); + } + sfree(test_include); + sfree(test_docs); + } else { + // Try to find it relative to current working directory. + // Check ../.. (e.g. build/Debug/docsparser -> cute_framework) + if (dir_exists("../../include")) { + cf_root = smake("../.."); + } else if (dir_exists("../include")) { + cf_root = smake(".."); + } else if (dir_exists("include")) { + cf_root = smake("."); + } else { + panic("Cannot find cute_framework root. Run from within the build directory or pass the root path as an argument."); + } + } + + char* include_dir = sfmake("%s/include", cf_root); + char* docs_dir = sfmake("%s/docs", cf_root); + g_relative_path = docs_dir; + + printf("Include dir: %s\n", include_dir); + printf("Docs dir: %s\n", docs_dir); + + // Parse each header. + DirIter headers = dir_open(include_dir); + const char* file; + while ((file = dir_next(&headers))) { + if (spext_equ(file, ".h")) { + parse_header(include_dir, file); + } + } + dir_close(&headers); + + // Auto-generate links. + for (int i = 0; i < asize(s->docs); ++i) { + Doc* doc = &s->docs[i]; + doc->brief = auto_generate_links(doc->brief); + for (int j = 0; j < asize(doc->param_briefs); ++j) { + doc->param_briefs[j] = auto_generate_links(doc->param_briefs[j]); + } + for (int j = 0; j < asize(doc->enum_entry_briefs); ++j) { + doc->enum_entry_briefs[j] = auto_generate_links(doc->enum_entry_briefs[j]); + } + for (int j = 0; j < asize(doc->member_briefs); ++j) { + doc->member_briefs[j] = auto_generate_links(doc->member_briefs[j]); + } + doc->return_value = auto_generate_links(doc->return_value); + doc->example_brief = auto_generate_links(doc->example_brief); + doc->example = auto_generate_links(doc->example); + doc->remarks = auto_generate_links(doc->remarks); + for (int j = 0; j < asize(doc->related);) { + const char* rel = doc->related[j]; + if (sequ(rel, doc->title)) { + sfree(doc->related[j]); + adel(doc->related, j); + continue; + } + char* linked = linkify(smake(rel), rel, 0); + sappend(linked, " "); + sfree(doc->related[j]); + doc->related[j] = linked; + ++j; + } + } + + // Save each doc file. + for (int i = 0; i < asize(s->docs); ++i) { + Doc* doc = &s->docs[i]; + { + // Create category directory. + char* dir = sppop(doc->path); + dp_mkdir(dir); + sfree(dir); + } + FILE* fp = fopen(doc->path, "wb"); + if (!fp) { + fprintf(stderr, "Failed to open: '%s'\n", doc->path); + exit(1); + } + const char* category = sintern(doc->web_category); + if (!map_has(s->category_index_lists, (uint64_t)category)) { + const char** empty = NULL; + map_set(s->category_index_lists, (uint64_t)category, empty); + } + const char*** arr_ptr = map_get_ptr(s->category_index_lists, (uint64_t)category); + apush(*arr_ptr, sintern(doc->title)); + + if (doc->type == DOC_ENUM) { + emit_title(fp, doc); + emit_brief(fp, doc); + emit_enum_entries(fp, doc); + emit_example(fp, doc); + emit_remarks(fp, doc); + emit_related(fp, doc); + } else if (doc->type == DOC_FUNCTION) { + emit_title(fp, doc); + emit_brief(fp, doc); + emit_signature(fp, doc); + emit_params(fp, doc); + emit_return(fp, doc); + emit_example(fp, doc); + emit_remarks(fp, doc); + emit_related(fp, doc); + } else if (doc->type == DOC_STRUCT) { + emit_title(fp, doc); + emit_brief(fp, doc); + emit_members(fp, doc); + emit_member_function_links(fp, doc); + emit_example(fp, doc); + emit_remarks(fp, doc); + emit_related(fp, doc); + } + printf("Wrote %s\n", doc->path); + fclose(fp); + } + + // Generate api_reference.md. + { + char* ref_path = sfmake("%s/api_reference.md", docs_dir); + FILE* fp = fopen(ref_path, "wb"); + fprintf(fp, "[//]: # (This file is automatically generated by Cute Framework's docs parser.)\n"); + fprintf(fp, "[//]: # (Do not edit this file by hand!)\n"); + fprintf(fp, "[//]: # (See: https://github.com/RandyGaul/cute_framework/blob/master/tools/docs_parser.c)\n"); + fprintf(fp, "# API Reference\n\n"); + fprintf(fp, "This is a list of all functions in Cute Framework organized by categories. This is great for users that want to see all the available functionality laid out plainly.\n\n\n"); + + CK_MAP(const char**) related = NULL; + + // Build related reading links. + #define ADD_RELATED(name, ...) do { \ + const char* _links[] = { __VA_ARGS__ }; \ + const char** _arr = NULL; \ + for (int _i = 0; _i < (int)(sizeof(_links)/sizeof(_links[0])); ++_i) apush(_arr, _links[_i]); \ + map_set(related, (uint64_t)sintern(name), _arr); \ + } while (0) + + ADD_RELATED("allocator", "- [Allocator](topics/allocator.md)"); + ADD_RELATED("app", "- [Application Window](topics/application_window.md)", "- [Game Loop and Time](topics/game_loop_and_time.md)"); + ADD_RELATED("array", "- [Data Structures](topics/data_structures.md)"); + ADD_RELATED("atomics", "- [Atomics](topics/atomics.md)"); + ADD_RELATED("binding", "- [Input Bindings](topics/input_bindings.md)"); + ADD_RELATED("collision", "- [Collision](topics/collision.md)"); + ADD_RELATED("coroutine", "- [Coroutines](topics/coroutines.md)"); + ADD_RELATED("draw", "- [Drawing](topics/drawing.md)"); + ADD_RELATED("ecs", "- [Entity Component System](topics/entity_component_system.md)"); + ADD_RELATED("file", "- [Virtual File System](topics/virtual_file_system.md)"); + ADD_RELATED("graphics", "- [Low Level Graphics](topics/low_level_graphics.md)"); + ADD_RELATED("haptic", "- [Input](topics/input.md)"); + ADD_RELATED("map", "- [Data Structures](topics/data_structures.md)"); + ADD_RELATED("input", "- [Input](topics/input.md)"); + ADD_RELATED("list", "- [Data Structures](topics/data_structures.md)"); + ADD_RELATED("math", "- [Collision](topics/collision.md)"); + ADD_RELATED("multithreading", "- [Atomics](topics/atomics.md)", "- [Multithreading](topics/multithreading.md)"); + ADD_RELATED("net", "- [Networking](topics/networking.md)"); + ADD_RELATED("path", "- [Virtual File System](topics/virtual_file_system.md)"); + ADD_RELATED("pathfinding", "- [Path Finding](topics/pathfinding.md)"); + ADD_RELATED("random", "- [Random Numbers](topics/random_numbers.md)"); + ADD_RELATED("sprite", "- [Drawing](topics/drawing.md)"); + ADD_RELATED("string", "- [Strings](topics/strings.md)"); + ADD_RELATED("time", "- [Game Loop and Time](topics/game_loop_and_time.md)"); + ADD_RELATED("text", "- [Drawing](topics/drawing.md)", "- [Input](topics/input.md)"); + ADD_RELATED("web", "- [Networking](topics/networking.md)"); + #undef ADD_RELATED + + // Sort categories by name. + map_ssort(s->categories, 1); + + for (int i = 0; i < map_size(s->categories); ++i) { + const char* category = (const char*)map_key(s->categories, i); + const char*** list_ptr = map_get_ptr(s->category_index_lists, (uint64_t)category); + const char** index_list = list_ptr ? *list_ptr : NULL; + fprintf(fp, "## %s\n\n", category); + + // Functions + { + const char** functions = NULL; + for (int j = 0; j < asize(index_list); ++j) { + const char* title = index_list[j]; + if (s->docs[get_doc_index(title)].type == DOC_FUNCTION) { + apush(functions, title); + } + } + qsort(functions, (size_t)asize(functions), sizeof(const char*), cmp_istr); + save_api_reference_links(fp, category, functions, asize(functions), "functions", related); + afree(functions); + } + + // Structs + { + const char** structs = NULL; + for (int j = 0; j < asize(index_list); ++j) { + const char* title = index_list[j]; + if (s->docs[get_doc_index(title)].type == DOC_STRUCT) { + apush(structs, title); + } + } + qsort(structs, (size_t)asize(structs), sizeof(const char*), cmp_istr); + save_api_reference_links(fp, category, structs, asize(structs), "structs", related); + afree(structs); + } + + // Enums + { + const char** enums = NULL; + for (int j = 0; j < asize(index_list); ++j) { + const char* title = index_list[j]; + if (s->docs[get_doc_index(title)].type == DOC_ENUM) { + apush(enums, title); + } + } + qsort(enums, (size_t)asize(enums), sizeof(const char*), cmp_istr); + save_api_reference_links(fp, category, enums, asize(enums), "enums", related); + afree(enums); + } + } + fclose(fp); + sfree(ref_path); + map_free(related); + } + + // Delete old document files that are no longer used. + for (int i = 0; i < map_size(s->categories); ++i) { + const char* category = (const char*)map_key(s->categories, i); + char* dir_path = sfmake("%s/%s", docs_dir, category); + DirIter dir = dir_open(dir_path); + const char* f; + while ((f = dir_next(&dir))) { + if (siequ(f, "enums.md") || siequ(f, "functions.md") || siequ(f, "structs.md")) { + continue; + } + if (f[0] == '.') continue; + if (!has_doc(f)) { + char* filepath = sfmake("%s/%s", dir_path, f); + remove(filepath); + sfree(filepath); + } + } + dir_close(&dir); + sfree(dir_path); + } + + printf("docsparser: Success!\n"); + + sfree(include_dir); + sfree(cf_root); + return 0; +} diff --git a/tools/gen_d3d12_layout.c b/tools/gen_d3d12_layout.c new file mode 100644 index 000000000..44d599ccc --- /dev/null +++ b/tools/gen_d3d12_layout.c @@ -0,0 +1,309 @@ +/* + Cute Framework + Copyright (C) 2024 Randy Gaul https://randygaul.github.io/ + + This software is dual-licensed with zlib or Unlicense, check LICENSE.txt for more info +*/ + +// Parses SDL3 private headers at build time to extract struct layout offsets +// needed to reach ID3D12Device* from SDL_GPUDevice* on Windows. +// No CF runtime or SDL required -- only standard C. +// +// Usage: gen_d3d12_layout + +#include +#include +#include + +#define MAX_LINE 2048 + +// ------------------------------------------------------------------------------------------------- +// Helpers + +static void panic(const char* msg) +{ + fprintf(stderr, "gen_d3d12_layout ERROR: %s\n", msg); + exit(1); +} + +static char* read_file(const char* path) +{ + FILE* f = fopen(path, "rb"); + if (!f) return NULL; + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + char* buf = (char*)malloc(size + 1); + if (!buf) { fclose(f); return NULL; } + fread(buf, 1, size, f); + buf[size] = 0; + fclose(f); + return buf; +} + +static const char* skip_ws(const char* s) +{ + while (*s == ' ' || *s == '\t') s++; + return s; +} + +static int str_contains(const char* hay, const char* needle) +{ + return strstr(hay, needle) != NULL; +} + +// Read one line into buf (strips trailing \r), returns pointer past \n. +static const char* next_line(const char* p, char* buf, int bufsize) +{ + int i = 0; + while (*p && *p != '\n' && i < bufsize - 1) buf[i++] = *p++; + buf[i] = 0; + if (i > 0 && buf[i - 1] == '\r') buf[--i] = 0; + if (*p == '\n') p++; + return p; +} + +// Find the opening brace of a struct definition (skips forward declarations). +// Returns pointer just past the '{'. +static const char* find_struct_body(const char* content, const char* name) +{ + char pattern[256]; + snprintf(pattern, sizeof(pattern), "struct %s", name); + const char* p = content; + while ((p = strstr(p, pattern)) != NULL) { + p += strlen(pattern); + const char* q = p; + while (*q == ' ' || *q == '\t' || *q == '\r' || *q == '\n') q++; + if (*q == '{') return q + 1; + } + return NULL; +} + +static int align_up(int offset, int alignment) +{ + return (offset + alignment - 1) & ~(alignment - 1); +} + +// ------------------------------------------------------------------------------------------------- +// Preprocessor conditional evaluator (assumes Windows desktop, non-Xbox, non-GDK) + +typedef struct +{ + int stack[16]; + int depth; +} PPState; + +static void pp_init(PPState* pp) +{ + pp->depth = 0; + pp->stack[0] = 1; +} + +static int pp_active(PPState* pp) +{ + return pp->stack[pp->depth]; +} + +static int macro_defined(const char* name) +{ + if (strstr(name, "SDL_PLATFORM_XBOXONE")) return 0; + if (strstr(name, "SDL_PLATFORM_XBOXSERIES")) return 0; + if (strstr(name, "HAVE_IDXGIINFOQUEUE")) return 1; + if (strstr(name, "USE_PIX_RUNTIME")) return 1; + fprintf(stderr, "WARNING: unknown macro '%s', assuming defined\n", name); + return 1; +} + +// Returns 1 if the line was a preprocessor directive. +static int pp_process(PPState* pp, const char* line) +{ + const char* p = skip_ws(line); + if (*p != '#') return 0; + p = skip_ws(p + 1); + + if (strncmp(p, "ifdef ", 6) == 0) { + int parent = pp->stack[pp->depth]; + pp->stack[++pp->depth] = parent && macro_defined(p + 6); + return 1; + } + if (strncmp(p, "ifndef ", 7) == 0) { + int parent = pp->stack[pp->depth]; + pp->stack[++pp->depth] = parent && !macro_defined(p + 7); + return 1; + } + if (strncmp(p, "if ", 3) == 0) { + // Handle #if !(defined(X) || defined(Y)) and similar. + const char* expr = p + 3; + int negated = str_contains(expr, "!("); + int any_defined = 0; + const char* d = expr; + while ((d = strstr(d, "defined(")) != NULL) { + d += 8; + char name[256] = {0}; + int i = 0; + while (*d && *d != ')' && i < 255) name[i++] = *d++; + name[i] = 0; + if (macro_defined(name)) any_defined = 1; + } + int result = negated ? !any_defined : any_defined; + int parent = pp->stack[pp->depth]; + pp->stack[++pp->depth] = parent && result; + return 1; + } + if (strncmp(p, "else", 4) == 0 && (p[4] == 0 || p[4] == ' ' || p[4] == '\t' || p[4] == '\r' || p[4] == '/')) { + int parent = pp->stack[pp->depth - 1]; + pp->stack[pp->depth] = parent && !pp->stack[pp->depth]; + return 1; + } + if (strncmp(p, "endif", 5) == 0) { + if (pp->depth > 0) pp->depth--; + return 1; + } + return 1; // Other directives (#define, #include, etc.) +} + +// ------------------------------------------------------------------------------------------------- +// SDL_GPUDevice: count function pointer slots before driverData + +static int count_gpu_device_fn_slots(const char* sysgpu) +{ + const char* body = find_struct_body(sysgpu, "SDL_GPUDevice"); + if (!body) panic("could not find struct SDL_GPUDevice definition"); + + int count = 0; + char line[MAX_LINE]; + const char* p = body; + while (*p) { + p = next_line(p, line, sizeof(line)); + if (str_contains(line, "driverData;")) break; + if (str_contains(line, "(*")) count++; + } + if (count == 0) panic("no function pointers found in SDL_GPUDevice before driverData"); + return count; +} + +// ------------------------------------------------------------------------------------------------- +// D3D12Renderer: compute byte offset of ID3D12Device *device + +// Count fields in a simple struct (all assumed pointer-sized). +static int count_struct_fields(const char* content, const char* name) +{ + const char* body = find_struct_body(content, name); + if (!body) return -1; + + int count = 0; + char line[MAX_LINE]; + const char* p = body; + while (*p) { + p = next_line(p, line, sizeof(line)); + const char* t = skip_ws(line); + if (*t == '}') break; + if (str_contains(line, ";") && *t != '/' && *t != '#' && *t != 0) count++; + } + return count; +} + +static int compute_d3d12_device_offset(const char* d3d12) +{ + // Parse WinPixEventRuntimeFns to learn its size. + int pix_fields = count_struct_fields(d3d12, "WinPixEventRuntimeFns"); + int pix_size; + if (pix_fields > 0) { + pix_size = pix_fields * 8; // Each field is a function pointer typedef. + printf(" WinPixEventRuntimeFns: %d fields, %d bytes\n", pix_fields, pix_size); + } else { + pix_size = 24; + printf(" WinPixEventRuntimeFns: not found, assuming 24 bytes\n"); + } + + const char* body = find_struct_body(d3d12, "D3D12Renderer"); + if (!body) panic("could not find struct D3D12Renderer definition"); + + PPState pp; + pp_init(&pp); + + int offset = 0; + char line[MAX_LINE]; + const char* p = body; + + while (*p) { + p = next_line(p, line, sizeof(line)); + const char* t = skip_ws(line); + + if (*t == 0) continue; + if (*t == '}') break; + if (t[0] == '/' && t[1] == '/') continue; + if (pp_process(&pp, line)) continue; + if (!pp_active(&pp)) continue; + if (!str_contains(line, ";")) continue; + + // Target field found. + if (str_contains(line, "ID3D12Device") && str_contains(line, "device")) { + offset = align_up(offset, 8); + return offset; + } + + // Classify field type. + int size, align; + if (str_contains(line, "WinPixEventRuntimeFns")) { + size = pix_size; + align = 8; + } else if (str_contains(line, "BOOL ") || str_contains(line, "BOOL\t")) { + size = 4; + align = 4; + } else if (str_contains(line, "*")) { + size = 8; + align = 8; + } else { + fprintf(stderr, "WARNING: unknown field type: '%s', assuming 8 bytes\n", t); + size = 8; + align = 8; + } + + offset = align_up(offset, align); + offset += size; + } + + panic("ID3D12Device *device not found in D3D12Renderer"); + return -1; +} + +// ------------------------------------------------------------------------------------------------- + +int main(int argc, char** argv) +{ + if (argc != 4) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return 1; + } + + char* sysgpu = read_file(argv[1]); + if (!sysgpu) { fprintf(stderr, "ERROR: could not read %s\n", argv[1]); return 1; } + + char* d3d12 = read_file(argv[2]); + if (!d3d12) { fprintf(stderr, "ERROR: could not read %s\n", argv[2]); return 1; } + + printf("Parsing SDL_GPUDevice from %s ...\n", argv[1]); + int fn_count = count_gpu_device_fn_slots(sysgpu); + printf(" function pointer slots before driverData: %d\n", fn_count); + + printf("Parsing D3D12Renderer from %s ...\n", argv[2]); + int device_offset = compute_d3d12_device_offset(d3d12); + printf(" ID3D12Device byte offset: %d\n", device_offset); + + FILE* out = fopen(argv[3], "w"); + if (!out) { fprintf(stderr, "ERROR: could not write %s\n", argv[3]); return 1; } + + fprintf(out, "// Auto-generated by gen_d3d12_layout -- do not edit.\n"); + fprintf(out, "// Parsed from SDL3 source to locate ID3D12Device* in private structs.\n"); + fprintf(out, "// This is done to fetch the device pointer, pretty much just to silence dumb warnings.\n"); + fprintf(out, "#define SDL_GPU_DEVICE_FN_SLOT_COUNT %d\n", fn_count); + fprintf(out, "#define D3D12_RENDERER_DEVICE_BYTE_OFFSET %d\n", device_offset); + + fclose(out); + free(sysgpu); + free(d3d12); + + printf("Wrote %s\n", argv[3]); + return 0; +} diff --git a/tools/generate_sample_pages.rb b/tools/generate_sample_pages.rb new file mode 100644 index 000000000..392e59ecc --- /dev/null +++ b/tools/generate_sample_pages.rb @@ -0,0 +1,199 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +# Usage: ./generate_sample_pages.rb [repo_url] [mkdocs_yml] +# +# Reads the sample list from the Samples: section of mkdocs.yml nav, which is +# the source of truth for which samples to generate pages for. + +require 'cgi' +require 'fileutils' +require 'pathname' +require 'yaml' + +# Target name mappings for cases where the nav target differs from the file name +# e.g. nine_slice -> 9_slice +TARGET_MAP = { 'nine_slice' => '9_slice' }.freeze + +# Extract only the nav: block from mkdocs.yml so we avoid Python-specific YAML +# tags (!!python/name:, !!python/object/apply:) present in other sections. +def rel(path) + Pathname.new(path).relative_path_from(Pathname.pwd).to_s +rescue ArgumentError + path.to_s +end + +def gh_annotation(level, message, file: nil, title: nil) + if ENV['GITHUB_ACTIONS'] + parts = [] + parts << "file=#{file}" if file + parts << "title=#{title}" if title + meta = parts.empty? ? '' : " #{parts.join(',')}" + warn "::#{level}#{meta}::#{message}" + else + yield message + end +end + +def gh_debug(message) = gh_annotation('debug', message) { |m| puts m } +def gh_notice(message, **kw) = gh_annotation('notice', message, **kw) { |m| puts m } +def gh_warn(message, **kw) = gh_annotation('warning', message, **kw) { |m| warn "Warning: #{m}" } + +def extract_nav_section(mkdocs_path) + in_nav = false + nav_lines = [] + + File.foreach(mkdocs_path) do |line| + if line.start_with?('nav:') + in_nav = true + nav_lines << line + elsif in_nav + break if line.match?(/^\S/) # next top-level key ends the nav block + nav_lines << line + end + end + + YAML.safe_load(nav_lines.join) +end + +def parse_samples_from_nav(nav) + samples_section = (nav['nav'] || []).find { |e| e.is_a?(Hash) && e.key?('Samples') } + return [] unless samples_section + + samples = [] + (samples_section['Samples'] || []).each do |entry| + next if entry.is_a?(String) # skip bare paths like samples/index.md + entry.each do |display_name, path| + target = File.basename(path.to_s, '.md') + next if target == 'index' + samples << { target: target, name: display_name } + end + end + samples +end + +def find_source_file(target, samples_src_dir) + actual_target = TARGET_MAP.fetch(target, target) + %w[c cpp].each do |ext| + file = "#{actual_target}.#{ext}" + return file if File.exist?(File.join(samples_src_dir, file)) + end + nil +end + +def main + if ARGV.length < 2 + warn "Usage: #{$PROGRAM_NAME} [repo_url] [mkdocs_yml]" + exit 1 + end + + build_dir = ARGV[0] + docs_dir = ARGV[1] + repo_url = ARGV[2] || 'https://github.com/RandyGaul/cute_framework' + mkdocs_yml = ARGV[3] || File.expand_path('../mkdocs.yml', __dir__) + + samples_src_dir = File.expand_path('../samples', __dir__) + samples_dir = File.join(docs_dir, 'samples') + play_dir = File.join(samples_dir, 'play') + + FileUtils.rm_rf(play_dir) + FileUtils.mkdir_p(play_dir) + + emscripten_dir = File.join(build_dir, 'emscripten') + FileUtils.cp_r(emscripten_dir, play_dir) if Dir.exist?(emscripten_dir) + + nav = extract_nav_section(mkdocs_yml) + samples = parse_samples_from_nav(nav) + + if samples.empty? + gh_warn("No samples found in Samples: nav section of #{rel(mkdocs_yml)}", file: rel(mkdocs_yml), title: 'No samples found') + exit 0 + end + + generated_samples = [] + + samples.each do |sample| + target = sample[:target] + display_name = sample[:name] + html_name = CGI.escapeHTML(display_name) + actual_target = TARGET_MAP.fetch(target, target) + + source_file = find_source_file(target, samples_src_dir) + if source_file.nil? + gh_warn("No source file found for #{target} in #{rel(samples_src_dir)}, skipping", title: 'Missing source file') + next + end + + html_file = File.join(build_dir, "#{actual_target}.html") + unless File.exist?(html_file) + gh_warn("#{rel(html_file)} not found, skipping #{target}", title: 'Missing build artifact') + next + end + + %w[html js wasm data].each do |ext| + artifact = File.join(build_dir, "#{actual_target}.#{ext}") + FileUtils.cp(artifact, play_dir) if File.exist?(artifact) + end + + markdown_content = <<~MARKDOWN + --- + hide: + - toc + --- + + # #{html_name} + +
+ +
+ +
+ [:material-fullscreen: Fullscreen](../play/#{actual_target}.html){: .md-button target="_blank" } + [:material-code-tags: View Source](#{repo_url}/blob/master/samples/#{source_file}){: .md-button target="_blank" } +
+ MARKDOWN + + File.write(File.join(samples_dir, "#{target}.md"), markdown_content) + gh_notice("Generated #{rel(File.join(samples_dir, "#{target}.md"))}") + generated_samples << { target: target, name: display_name } + end + + sorted_samples = generated_samples.sort_by { |s| s[:name] } + + index_header = <<~MARKDOWN + # Samples + + Interactive samples demonstrating Cute Framework features. Click any card to play the sample in your browser. + +
+ MARKDOWN + + cards = sorted_samples.map do |sample| + <<~CARD + + - **[#{CGI.escapeHTML(sample[:name])}](#{sample[:target]}.md)** + + --- + + Interactive sample demonstrating #{CGI.escapeHTML(sample[:name].downcase)} features. + + [:octicons-arrow-right-24: Play](#{sample[:target]}.md) + + CARD + end.join + + index_footer = <<~MARKDOWN +
+ + !!! note "Source Code" + All sample source code is available in the [samples directory](https://github.com/RandyGaul/cute_framework/tree/master/samples) on GitHub. + + !!! tip "Build Locally" + To build and run samples locally, see the [Getting Started](../getting_started.md) guide and [Web Builds with Emscripten](../topics/emscripten.md) topic. + MARKDOWN + + File.write(File.join(samples_dir, 'index.md'), index_header + cards + index_footer) + gh_notice("Generated #{rel(File.join(samples_dir, 'index.md'))} with #{sorted_samples.length} samples") +end + +main if __FILE__ == $PROGRAM_NAME