[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: [recp]\npatreon: recp\nopen_collective: assetkit\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/__disabled_workflows/cmake.yml",
    "content": "name: CMake\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\nenv:\n  # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)\n  BUILD_TYPE: Release\n\njobs:\n  build:\n    # The CMake configure and build commands are platform agnostic and should work equally\n    # well on Windows or Mac.  You can convert this to a matrix build if you need\n    # cross-platform coverage.\n    # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n\n    - name: Configure CMake\n      # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.\n      # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type\n      run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}\n\n    - name: Build\n      # Build your program with the given configuration\n      run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}\n\n#    - name: Test\n#      working-directory: ${{github.workspace}}/build\n#      # Execute tests defined by the CMake configuration.  \n#      # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail\n#      run: ctest -C ${{env.BUILD_TYPE}}\n      \n"
  },
  {
    "path": ".gitignore",
    "content": "*.xcodeproj\n*.xcworkspace\n*.sln\n*.vcxproj\n*.vcxproj.*\n*.suo\n*.sdf\n*.opensdf\nipch/\nDebug/\nRelease/\n.DS_Store\n.vs\n*.nupkg\n*.opendb\npackages.config\n/aclocal.m4\n/ar-lib\n/autom4te.cache/\n/compile\n/config.guess\n/config.log\n/config.status\n/config.sub\n/configure\n/depcomp\n/install-sh\n/ltmain.sh\n/missing\n/libtool\n.libs/\n.deps/\n*.[oa]\n*.l[oa]\nMakefile\nMakefile.in\nm4/*.m4\n.buildstamp\n.dirstamp\npackages/\n.anjuta/*\n*.anjuta*\nconfig.h.*\nlibxml*\nxml2*\npython/setup.py\nconfig.h\nstamp*\nCOPYING\n.idea/*\n*.VC.db\ntest-driver\n/include/include/\n*.log\ntest/ak-tests.trs\ntest/ak-tests\n*.gcov\n*.gcno\n*.gcda\ntests.trs\ntests\n*.userprefs\n*.orig\ncmake-build-debug\nwin/x64/*\nwin/x86/*\nbuild/\n/deps/draco/\n/deps/meshoptimizer/\n/deps/spz/\n/deps/ktx/\n.cache/\n.scannerwork/\n.project\n.cproject\n.vscode/\nproj/\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"deps/cglm\"]\n\tpath = deps/cglm\n\turl = https://github.com/recp/cglm.git\n[submodule \"deps/json\"]\n\tpath = deps/json\n\turl = https://github.com/recp/json.git\n[submodule \"deps/xml\"]\n\tpath = deps/xml\n\turl = https://github.com/recp/xml.git\n[submodule \"deps/ds\"]\n\tpath = deps/ds\n\turl = https://github.com/recp/ds.git\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: c\n\nos:\n  - linux\n  - osx\n\nsudo: required\ndist: trusty\n\ncompiler:\n  - clang\n  - gcc\n\nmatrix:\n  fast_finish: true\n  exclude:\n    # Skip GCC builds on macOS.\n    - os: osx\n      compiler: gcc\n  include:\n    # Additional GCC builds for code coverage.\n    - os: linux\n      compiler: gcc\n      env: CODE_COVERAGE=ON\n\ncache:\n  apt: true\n\naddons:\n  apt:\n    packages:\n      - lcov\n\nbranches:\n  only:\n    - master\n\nscript:\n  - sh ./build-deps.sh\n  - sh ./autogen.sh\n  - if [[ \"$CC\" == \"gcc\" && \"$CODE_COVERAGE\" == \"ON\" ]]; then\n      ./configure CFLAGS=\"-ftest-coverage -fprofile-arcs\";\n    else\n      ./configure;\n    fi\n  - make -j8\n#  - make check\n\nafter_success:\n  - if [[ \"$CC\" == \"gcc\" && \"$CODE_COVERAGE\" == \"ON\" ]]; then\n      pip install --user cpp-coveralls && \n      coveralls\n        --build-root .\n        --exclude lib\n        --exclude test\n        --gcov-options '\\-lp'\n        --verbose;\n    fi\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.16)\nproject(assetkit VERSION 0.3.2 LANGUAGES C)\n\nif(POLICY CMP0076)\n  cmake_policy(SET CMP0076 NEW)\nendif()\nif(POLICY CMP0169)\n  cmake_policy(SET CMP0169 OLD)\nendif()\n\nset(CMAKE_C_STANDARD 11)\nset(CMAKE_C_STANDARD_REQUIRED YES)\nset(DEFAULT_BUILD_TYPE \"Release\")\n\nget_directory_property(AK_HAS_PARENT PARENT_DIRECTORY)\n\nset(CMAKE_C_FLAGS_DEBUG \"-g\")\nif(MSVC)\n  set(CMAKE_C_FLAGS_RELEASE \"/Ox /DNDEBUG\")\n  set(CMAKE_CXX_FLAGS_RELEASE \"/Ox /DNDEBUG\")\nelse()\n  set(CMAKE_C_FLAGS_RELEASE \"-O3 -DNDEBUG\")\n  set(CMAKE_CXX_FLAGS_RELEASE \"-O3 -DNDEBUG\")\nendif()\n\nset(AK_BUILD)\noption(AK_SHARED \"Shared build\" ON)\noption(AK_STATIC \"Static build\" OFF)\noption(AK_USE_C99 \"\" OFF)\noption(AK_USE_TEST \"Enable Tests\" OFF)\noption(AK_BUILD_GLTF_DRACO_DECODER \"Build optional glTF Draco decoder shim\" ON)\noption(AK_BUILD_GLTF_MESHOPT_DECODER \"Build optional glTF meshoptimizer decoder shim\" ON)\noption(AK_BUILD_GLTF_SPZ_DECODER \"Build optional glTF Gaussian splatting (SPZ) decoder shim\" ON)\noption(AK_BUILD_GLTF_KTX2_DECODER \"Build optional glTF KHR_texture_basisu (KTX2/BasisU) decoder shim\" ON)\noption(AK_FETCH_DEPS \"Fetch optional decoder dependencies into AK_DEPS_ROOT when missing\" ON)\noption(AK_ENABLE_LTO \"Enable link-time optimization for release builds\" OFF)\nset(AK_DRACO_ROOT \"\" CACHE PATH \"Optional Draco install/source root\")\nset(AK_MESHOPT_ROOT \"\" CACHE PATH \"Optional meshoptimizer source root\")\nset(AK_SPZ_ROOT \"\" CACHE PATH \"Optional SPZ install/source root\")\nset(AK_KTX2_ROOT \"\" CACHE PATH \"Optional KTX-Software install/source root\")\nset(AK_DEPS_ROOT \"${PROJECT_SOURCE_DIR}/deps\" CACHE PATH \"AssetKit dependency checkout root\")\n\nset(CMAKE_ARCHIVE_OUTPUT_DIRECTORY \"${PROJECT_BINARY_DIR}\")\nset(CMAKE_LIBRARY_OUTPUT_DIRECTORY \"${PROJECT_BINARY_DIR}\")\nset(CMAKE_RUNTIME_OUTPUT_DIRECTORY \"${PROJECT_BINARY_DIR}\")\n\nif(NOT AK_STATIC AND AK_SHARED)\n  set(AK_BUILD SHARED)\nelse(AK_STATIC)\n  set(AK_BUILD STATIC)\nendif()\n\nif(AK_USE_C99)\n  set(CMAKE_C_STANDARD 99)\nendif()\n\nif(MSVC)\n  # Ref: https://skia.googlesource.com/third_party/sdl/+/refs/heads/master/CMakeLists.txt#225\n  # Make sure /RTC1 is disabled, otherwise it will use functions from the CRT\n  foreach(flag_var\n      CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE\n      CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO)\n    string(REGEX REPLACE \"/RTC(su|[1su])\" \"\" ${flag_var} \"${${flag_var}}\")\n  endforeach(flag_var)\nendif()\n\nif(NOT AK_HAS_PARENT AND NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n  message(STATUS \"Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.\")\n  set(CMAKE_BUILD_TYPE \"${DEFAULT_BUILD_TYPE}\" CACHE STRING \"Choose the type of build.\" FORCE)\n  # Set the possible values of build type for cmake-gui\n  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS \"Debug\" \"Release\" \"MinSizeRel\" \"RelWithDebInfo\")\nendif()\n\nif(AK_ENABLE_LTO)\n  include(CheckIPOSupported)\n  check_ipo_supported(RESULT AK_LTO_OK OUTPUT AK_LTO_MSG)\n  if(AK_LTO_OK)\n    set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)\n    set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON)\n  else()\n    message(WARNING \"LTO was requested but is not supported: ${AK_LTO_MSG}\")\n  endif()\nendif()\n\nfunction(ak_target_common_options target)\n  if(MSVC)\n    target_compile_options(${target}\n        PRIVATE\n            /W3\n            $<$<CONFIG:Release>:/Gy>\n            $<$<CONFIG:Release>:/Oi>\n            $<$<CONFIG:RelWithDebInfo>:/Gy>\n            $<$<CONFIG:RelWithDebInfo>:/Oi>)\n  else()\n    target_compile_options(${target}\n        PRIVATE\n            -Wall\n            -Wextra\n            -Wstrict-aliasing=2\n            -Wno-overlength-strings\n            $<$<COMPILE_LANGUAGE:C>:-Wmissing-declarations>)\n  endif()\nendfunction()\n\nfunction(ak_target_c_options target)\n  ak_target_common_options(${target})\n  if(MSVC)\n    target_compile_options(${target} PRIVATE /TC)\n  endif()\nendfunction()\n\nfunction(ak_target_cxx_options target)\n  ak_target_common_options(${target})\nendfunction()\n\ninclude(GNUInstallDirs)\n\nset(CPACK_PROJECT_NAME ${PROJECT_NAME})\nset(CPACK_PROJECT_VERSION ${PROJECT_VERSION})\n\nif(NOT CPack_CMake_INCLUDED)\n  include(CPack)\nendif()\n\n# Target Start\nadd_library(${PROJECT_NAME} ${AK_BUILD} \"\")\nadd_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})\nak_target_c_options(${PROJECT_NAME})\n\nif(APPLE)\n  set(AK_INSTALL_RPATH \"@loader_path\")\nelseif(UNIX)\n  set(AK_INSTALL_RPATH \"\\$ORIGIN\")\nendif()\n\nif(AK_SHARED)\n  target_compile_definitions(${PROJECT_NAME} PRIVATE AK_EXPORTS)\n  if(MSVC)\n    target_compile_definitions(${PROJECT_NAME}\n        PRIVATE\n            _WINDOWS\n            _USRDLL\n            _assetkit_dll_DLL)\n  endif()\nelse()\n  target_compile_definitions(${PROJECT_NAME} PUBLIC AK_STATIC)\nendif()\n\nset_target_properties(${PROJECT_NAME} PROPERTIES\n                              VERSION ${PROJECT_VERSION} \n                            SOVERSION ${PROJECT_VERSION_MAJOR})\nif(AK_INSTALL_RPATH)\n  set_target_properties(${PROJECT_NAME} PROPERTIES\n                        INSTALL_RPATH \"${AK_INSTALL_RPATH}\")\nendif()\n\nadd_subdirectory(src)\n\n# Dependencies && Submodules\n\nfind_package(Git QUIET)\nif(GIT_FOUND AND EXISTS \"${PROJECT_SOURCE_DIR}/.git\")\n# Update submodules as needed\n    option(GIT_SUBMODULE \"Check submodules during build\" ON)\n    if(GIT_SUBMODULE)\n        message(STATUS \"Submodule update\")\n        execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive\n                        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}\n                        RESULT_VARIABLE GIT_SUBMOD_RESULT)\n        if(NOT GIT_SUBMOD_RESULT EQUAL \"0\")\n            message(FATAL_ERROR \"git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules\")\n        endif()\n    endif()\nendif()\n\nif(NOT EXISTS \"${PROJECT_SOURCE_DIR}/deps/cglm/CMakeLists.txt\")\n    message(FATAL_ERROR \"The submodules were not downloaded! GIT_SUBMODULE was turned off or failed. Please update submodules and try again.\")\nendif()\n\nadd_subdirectory(deps/ds)\nadd_dependencies(${PROJECT_NAME} ds)\n\n# set(CMAKE_MODULE_PATH \"${PROJECT_SOURCE_DIR}/deps\" ${CMAKE_MODULE_PATH})\n# find_package(ds REQUIRED)\n# target_link_libraries(${PROJECT_NAME} ds::ds)\n\n# Include Dirs\ntarget_include_directories(${PROJECT_NAME}\n    PUBLIC \n        $<INSTALL_INTERFACE:include>    \n        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>\n    PRIVATE\n        ${CMAKE_CURRENT_SOURCE_DIR}/src\n\n        ${PROJECT_SOURCE_DIR}/deps/cglm/include\n        ${PROJECT_SOURCE_DIR}/deps/ds/include\n        ${PROJECT_SOURCE_DIR}/deps/json/include\n        ${PROJECT_SOURCE_DIR}/deps/xml/include\n)\n\ntarget_link_libraries(${PROJECT_NAME} PRIVATE ds ${CMAKE_DL_LIBS})\n\nif(AK_BUILD_GLTF_DRACO_DECODER OR AK_BUILD_GLTF_MESHOPT_DECODER\n   OR AK_BUILD_GLTF_SPZ_DECODER OR AK_BUILD_GLTF_KTX2_DECODER)\n  enable_language(CXX)\nendif()\n\nif(AK_BUILD_GLTF_DRACO_DECODER)\n  find_path(AK_DRACO_INCLUDE_DIR\n            NAMES draco/compression/decode.h\n            HINTS ${AK_DRACO_ROOT}\n                  ${AK_DRACO_ROOT}/install\n                  ${AK_DRACO_ROOT}/include\n                  ${AK_DEPS_ROOT}/draco/install\n                  ${AK_DEPS_ROOT}/draco\n                  ${AK_DEPS_ROOT}/draco/include\n            PATH_SUFFIXES include\n            NO_DEFAULT_PATH)\n  find_library(AK_DRACO_LIBRARY\n               NAMES draco\n               HINTS ${AK_DRACO_ROOT}\n                     ${AK_DRACO_ROOT}/install\n                     ${AK_DRACO_ROOT}/lib\n                     ${AK_DEPS_ROOT}/draco/install\n                     ${AK_DEPS_ROOT}/draco\n                     ${AK_DEPS_ROOT}/draco/lib\n               PATH_SUFFIXES lib\n               NO_DEFAULT_PATH)\n\n  if((NOT AK_DRACO_INCLUDE_DIR OR NOT AK_DRACO_LIBRARY) AND AK_FETCH_DEPS)\n    include(ExternalProject)\n\n    set(AK_DRACO_INSTALL_DIR \"${AK_DEPS_ROOT}/draco/install\")\n    set(AK_DRACO_INCLUDE_DIR \"${AK_DRACO_INSTALL_DIR}/include\")\n    if(WIN32)\n      set(AK_DRACO_LIBRARY \"${AK_DRACO_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/draco.lib\")\n    else()\n      set(AK_DRACO_LIBRARY \"${AK_DRACO_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/libdraco.a\")\n    endif()\n\n    ExternalProject_Add(assetkit_draco_dep\n        GIT_REPOSITORY https://github.com/google/draco.git\n        GIT_TAG main\n        GIT_SHALLOW TRUE\n        GIT_SUBMODULES \"\"\n        SOURCE_DIR \"${AK_DEPS_ROOT}/draco\"\n        BINARY_DIR \"${CMAKE_CURRENT_BINARY_DIR}/assetkit_draco-build\"\n        INSTALL_DIR \"${AK_DRACO_INSTALL_DIR}\"\n        BUILD_BYPRODUCTS \"${AK_DRACO_LIBRARY}\"\n        CMAKE_ARGS\n            -Wno-dev\n            -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}\n            -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>\n            -DCMAKE_POSITION_INDEPENDENT_CODE=ON\n            -DDRACO_ANIMATION_ENCODING=OFF\n            -DDRACO_BUILD_EXECUTABLES=OFF\n            -DDRACO_GLTF_BITSTREAM=ON\n            -DDRACO_INSTALL=ON\n            -DDRACO_JS_GLUE=OFF\n            -DDRACO_MAYA_PLUGIN=OFF\n            -DDRACO_TESTS=OFF\n            -DDRACO_TRANSCODER_SUPPORTED=OFF\n            -DDRACO_UNITY_PLUGIN=OFF\n            -DDRACO_WASM=OFF\n            -DBUILD_SHARED_LIBS=OFF)\n  endif()\n\n  if(NOT AK_DRACO_INCLUDE_DIR OR NOT AK_DRACO_LIBRARY)\n    message(FATAL_ERROR\n            \"Draco decoder requested but Draco was not found. \"\n            \"Set AK_DRACO_ROOT, enable AK_FETCH_DEPS, or disable \"\n            \"AK_BUILD_GLTF_DRACO_DECODER.\")\n  endif()\n\n  add_library(assetkit_draco SHARED\n              src/decoders/gltf/draco/assetkit_draco.cc)\n  ak_target_cxx_options(assetkit_draco)\n  if(TARGET assetkit_draco_dep)\n    add_dependencies(assetkit_draco assetkit_draco_dep)\n  endif()\n  target_include_directories(assetkit_draco\n      SYSTEM\n      PRIVATE\n          ${AK_DRACO_INCLUDE_DIR})\n  target_include_directories(assetkit_draco\n      PRIVATE\n          ${CMAKE_CURRENT_SOURCE_DIR}/include\n          ${CMAKE_CURRENT_SOURCE_DIR}/src\n          ${PROJECT_SOURCE_DIR}/deps/cglm/include\n          ${PROJECT_SOURCE_DIR}/deps/ds/include\n          ${PROJECT_SOURCE_DIR}/deps/json/include\n          ${PROJECT_SOURCE_DIR}/deps/xml/include)\n  target_link_libraries(assetkit_draco\n      PRIVATE\n          ${PROJECT_NAME}\n          ds\n          ${AK_DRACO_LIBRARY})\n  set_target_properties(assetkit_draco PROPERTIES\n                        CXX_STANDARD 11\n                        CXX_STANDARD_REQUIRED YES\n                        CXX_VISIBILITY_PRESET hidden\n                        VISIBILITY_INLINES_HIDDEN YES)\n  if(AK_INSTALL_RPATH)\n    set_target_properties(assetkit_draco PROPERTIES\n                          INSTALL_RPATH \"${AK_INSTALL_RPATH}\")\n  endif()\n  if(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n    target_compile_options(assetkit_draco\n        PRIVATE\n            -Wno-deprecated-copy-with-user-provided-copy\n            -Wno-ignored-qualifiers\n            -Wno-implicit-const-int-float-conversion\n            -Wno-sign-compare)\n  endif()\n\n  install(TARGETS assetkit_draco\n          LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}\n          RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})\nendif()\n\nif(AK_BUILD_GLTF_MESHOPT_DECODER)\n  find_path(AK_MESHOPT_INCLUDE_DIR\n            NAMES meshoptimizer.h\n            HINTS ${AK_MESHOPT_ROOT}\n                  ${AK_MESHOPT_ROOT}/src\n                  ${AK_DEPS_ROOT}/meshoptimizer\n                  ${AK_DEPS_ROOT}/meshoptimizer/src\n            PATH_SUFFIXES src\n            NO_DEFAULT_PATH)\n\n  if(NOT AK_MESHOPT_INCLUDE_DIR AND AK_FETCH_DEPS)\n    include(FetchContent)\n\n    FetchContent_Declare(assetkit_meshoptimizer_dep\n        GIT_REPOSITORY https://github.com/zeux/meshoptimizer.git\n        GIT_TAG master\n        GIT_SHALLOW TRUE\n        SOURCE_DIR \"${AK_DEPS_ROOT}/meshoptimizer\")\n    FetchContent_GetProperties(assetkit_meshoptimizer_dep)\n    if(NOT assetkit_meshoptimizer_dep_POPULATED)\n      FetchContent_Populate(assetkit_meshoptimizer_dep)\n    endif()\n\n    set(AK_MESHOPT_INCLUDE_DIR \"${AK_DEPS_ROOT}/meshoptimizer/src\")\n  endif()\n\n  set(AK_MESHOPT_SOURCES\n      ${AK_MESHOPT_INCLUDE_DIR}/indexcodec.cpp\n      ${AK_MESHOPT_INCLUDE_DIR}/vertexcodec.cpp\n      ${AK_MESHOPT_INCLUDE_DIR}/vertexfilter.cpp)\n\n  if(NOT AK_MESHOPT_INCLUDE_DIR\n     OR NOT EXISTS \"${AK_MESHOPT_INCLUDE_DIR}/indexcodec.cpp\"\n     OR NOT EXISTS \"${AK_MESHOPT_INCLUDE_DIR}/vertexcodec.cpp\"\n     OR NOT EXISTS \"${AK_MESHOPT_INCLUDE_DIR}/vertexfilter.cpp\")\n    message(FATAL_ERROR\n            \"meshoptimizer decoder requested but source files were not found. \"\n            \"Set AK_MESHOPT_ROOT, enable AK_FETCH_DEPS, or disable \"\n            \"AK_BUILD_GLTF_MESHOPT_DECODER.\")\n  endif()\n\n  add_library(assetkit_meshoptimizer SHARED\n              src/decoders/gltf/meshopt/assetkit_meshoptimizer.cc\n              ${AK_MESHOPT_SOURCES})\n  ak_target_cxx_options(assetkit_meshoptimizer)\n  target_include_directories(assetkit_meshoptimizer\n      PRIVATE\n          ${AK_MESHOPT_INCLUDE_DIR})\n  set_target_properties(assetkit_meshoptimizer PROPERTIES\n                        CXX_STANDARD 11\n                        CXX_STANDARD_REQUIRED YES\n                        CXX_VISIBILITY_PRESET hidden\n                        VISIBILITY_INLINES_HIDDEN YES)\n  if(AK_INSTALL_RPATH)\n    set_target_properties(assetkit_meshoptimizer PROPERTIES\n                          INSTALL_RPATH \"${AK_INSTALL_RPATH}\")\n  endif()\n\n  install(TARGETS assetkit_meshoptimizer\n          LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}\n          RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})\nendif()\n\n# ---------------------------------------------------------------------------\n# Optional Gaussian splat (SPZ) decoder shim — KHR_gaussian_splatting future\n# compression sub-extension support. Niantic Spatial's libspz handles the\n# format; we link statically and expose the AssetKit decoder API.\n# ---------------------------------------------------------------------------\nif(AK_BUILD_GLTF_SPZ_DECODER)\n  find_path(AK_SPZ_INCLUDE_DIR\n            NAMES spz/spz.h\n            HINTS ${AK_SPZ_ROOT}\n                  ${AK_SPZ_ROOT}/install\n                  ${AK_SPZ_ROOT}/include\n                  ${AK_DEPS_ROOT}/spz/install\n                  ${AK_DEPS_ROOT}/spz\n                  ${AK_DEPS_ROOT}/spz/include\n            PATH_SUFFIXES include\n            NO_DEFAULT_PATH)\n  find_library(AK_SPZ_LIBRARY\n               NAMES spz\n               HINTS ${AK_SPZ_ROOT}\n                     ${AK_SPZ_ROOT}/install\n                     ${AK_SPZ_ROOT}/lib\n                     ${AK_DEPS_ROOT}/spz/install\n                     ${AK_DEPS_ROOT}/spz\n                     ${AK_DEPS_ROOT}/spz/lib\n               PATH_SUFFIXES lib\n               NO_DEFAULT_PATH)\n\n  if((NOT AK_SPZ_INCLUDE_DIR OR NOT AK_SPZ_LIBRARY) AND AK_FETCH_DEPS)\n    include(ExternalProject)\n\n    set(AK_SPZ_INSTALL_DIR \"${AK_DEPS_ROOT}/spz/install\")\n    set(AK_SPZ_INCLUDE_DIR \"${AK_SPZ_INSTALL_DIR}/include\")\n    if(WIN32)\n      set(AK_SPZ_LIBRARY \"${AK_SPZ_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/spz.lib\")\n    else()\n      set(AK_SPZ_LIBRARY \"${AK_SPZ_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/libspz.a\")\n    endif()\n\n    ExternalProject_Add(assetkit_spz_dep\n        GIT_REPOSITORY https://github.com/nianticlabs/spz.git\n        GIT_TAG main\n        GIT_SHALLOW TRUE\n        SOURCE_DIR \"${AK_DEPS_ROOT}/spz\"\n        BINARY_DIR \"${CMAKE_CURRENT_BINARY_DIR}/assetkit_spz-build\"\n        INSTALL_DIR \"${AK_SPZ_INSTALL_DIR}\"\n        BUILD_BYPRODUCTS \"${AK_SPZ_LIBRARY}\"\n        CMAKE_ARGS\n            -Wno-dev\n            -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}\n            -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>\n            -DCMAKE_POSITION_INDEPENDENT_CODE=ON\n            -DBUILD_SHARED_LIBS=OFF)\n  endif()\n\n  if(NOT AK_SPZ_INCLUDE_DIR OR NOT AK_SPZ_LIBRARY)\n    message(WARNING\n            \"SPZ Gaussian-splat decoder requested but libspz was not found. \"\n            \"Set AK_SPZ_ROOT, enable AK_FETCH_DEPS, or disable \"\n            \"AK_BUILD_GLTF_SPZ_DECODER. Skipping decoder build for now.\")\n  else()\n    add_library(assetkit_spz SHARED\n                src/decoders/gltf/spz/assetkit_spz.cc)\n    ak_target_cxx_options(assetkit_spz)\n    if(TARGET assetkit_spz_dep)\n      add_dependencies(assetkit_spz assetkit_spz_dep)\n    endif()\n    target_include_directories(assetkit_spz\n        SYSTEM PRIVATE\n            ${AK_SPZ_INCLUDE_DIR})\n    target_include_directories(assetkit_spz\n        PRIVATE\n            ${CMAKE_CURRENT_SOURCE_DIR}/include\n            ${CMAKE_CURRENT_SOURCE_DIR}/src\n            ${PROJECT_SOURCE_DIR}/deps/cglm/include\n            ${PROJECT_SOURCE_DIR}/deps/ds/include\n            ${PROJECT_SOURCE_DIR}/deps/json/include)\n    # SPZ depends on zstd + zlib (for ngsp/zstd-compressed streams + the gzip\n     # codec). On macOS both ship with the SDK; elsewhere we expect system\n     # packages. find_package handles missing libraries gracefully.\n    find_package(ZLIB)\n    find_library(AK_SPZ_ZSTD_LIB\n                 NAMES zstd\n                 HINTS /opt/homebrew/lib /usr/local/lib /usr/lib)\n\n    target_link_libraries(assetkit_spz\n        PRIVATE\n            ${PROJECT_NAME}\n            ds\n            ${AK_SPZ_LIBRARY})\n\n    if(ZLIB_FOUND)\n      target_link_libraries(assetkit_spz PRIVATE ZLIB::ZLIB)\n    endif()\n    if(AK_SPZ_ZSTD_LIB)\n      target_link_libraries(assetkit_spz PRIVATE ${AK_SPZ_ZSTD_LIB})\n    else()\n      message(WARNING\n              \"libzstd not found — SPZ ngsp/zstd-compressed splat streams \"\n              \"will fail at runtime. Install via 'brew install zstd' on \"\n              \"macOS or your distro's package manager.\")\n    endif()\n    set_target_properties(assetkit_spz PROPERTIES\n                          CXX_STANDARD 17\n                          CXX_STANDARD_REQUIRED YES\n                          CXX_VISIBILITY_PRESET hidden\n                          VISIBILITY_INLINES_HIDDEN YES)\n    if(AK_INSTALL_RPATH)\n      set_target_properties(assetkit_spz PROPERTIES\n                            INSTALL_RPATH \"${AK_INSTALL_RPATH}\")\n    endif()\n\n    install(TARGETS assetkit_spz\n            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}\n            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})\n  endif()\nendif()\n\n# ---------------------------------------------------------------------------\n# Optional KTX2 / BasisU texture decoder shim — KHR_texture_basisu support.\n# Khronos KTX-Software provides libktx with built-in BasisU transcoding\n# (UASTC / ETC1S → RGBA8 / BC7 / ASTC depending on target). The shim\n# decodes a KTX2 buffer into an RGBA8 AkImage so the renderer can consume\n# it like any other Core Graphics-decoded texture.\n# ---------------------------------------------------------------------------\nif(AK_BUILD_GLTF_KTX2_DECODER)\n  find_path(AK_KTX2_INCLUDE_DIR\n            NAMES ktx.h\n            HINTS ${AK_KTX2_ROOT}\n                  ${AK_KTX2_ROOT}/install\n                  ${AK_KTX2_ROOT}/include\n                  ${AK_DEPS_ROOT}/ktx/install\n                  ${AK_DEPS_ROOT}/ktx\n                  ${AK_DEPS_ROOT}/ktx/include\n            PATH_SUFFIXES include\n            NO_DEFAULT_PATH)\n  find_library(AK_KTX2_LIBRARY\n               NAMES ktx ktx_read\n               HINTS ${AK_KTX2_ROOT}\n                     ${AK_KTX2_ROOT}/install\n                     ${AK_KTX2_ROOT}/lib\n                     ${AK_DEPS_ROOT}/ktx/install\n                     ${AK_DEPS_ROOT}/ktx\n                     ${AK_DEPS_ROOT}/ktx/lib\n               PATH_SUFFIXES lib\n               NO_DEFAULT_PATH)\n\n  if((NOT AK_KTX2_INCLUDE_DIR OR NOT AK_KTX2_LIBRARY) AND AK_FETCH_DEPS)\n    include(ExternalProject)\n\n    set(AK_KTX2_INSTALL_DIR \"${AK_DEPS_ROOT}/ktx/install\")\n    set(AK_KTX2_INCLUDE_DIR \"${AK_KTX2_INSTALL_DIR}/include\")\n    if(WIN32)\n      set(AK_KTX2_LIBRARY \"${AK_KTX2_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/ktx.lib\")\n    else()\n      set(AK_KTX2_LIBRARY \"${AK_KTX2_INSTALL_DIR}/${CMAKE_INSTALL_LIBDIR}/libktx.a\")\n    endif()\n\n    ExternalProject_Add(assetkit_ktx2_dep\n        GIT_REPOSITORY https://github.com/KhronosGroup/KTX-Software.git\n        GIT_TAG main\n        GIT_SHALLOW TRUE\n        SOURCE_DIR \"${AK_DEPS_ROOT}/ktx\"\n        BINARY_DIR \"${CMAKE_CURRENT_BINARY_DIR}/assetkit_ktx2-build\"\n        INSTALL_DIR \"${AK_KTX2_INSTALL_DIR}\"\n        BUILD_BYPRODUCTS \"${AK_KTX2_LIBRARY}\"\n        CMAKE_ARGS\n            -Wno-dev\n            -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}\n            -DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>\n            -DCMAKE_POSITION_INDEPENDENT_CODE=ON\n            -DKTX_FEATURE_TESTS=OFF\n            -DKTX_FEATURE_TOOLS=OFF\n            -DKTX_FEATURE_DOC=OFF\n            -DKTX_FEATURE_LOADTEST_APPS=OFF\n            -DKTX_FEATURE_STATIC_LIBRARY=ON\n            -DBASISU_SUPPORT_SSE=ON\n            -DBUILD_SHARED_LIBS=OFF)\n  endif()\n\n  if(NOT AK_KTX2_INCLUDE_DIR OR NOT AK_KTX2_LIBRARY)\n    message(WARNING\n            \"KTX2/BasisU decoder requested but libktx was not found. \"\n            \"Set AK_KTX2_ROOT, enable AK_FETCH_DEPS, or disable \"\n            \"AK_BUILD_GLTF_KTX2_DECODER. Skipping decoder build for now.\")\n  else()\n    add_library(assetkit_ktx2 SHARED\n                src/decoders/gltf/ktx2/assetkit_ktx2.cc)\n    ak_target_cxx_options(assetkit_ktx2)\n    if(TARGET assetkit_ktx2_dep)\n      add_dependencies(assetkit_ktx2 assetkit_ktx2_dep)\n    endif()\n    target_include_directories(assetkit_ktx2\n        SYSTEM PRIVATE\n            ${AK_KTX2_INCLUDE_DIR})\n    target_include_directories(assetkit_ktx2\n        PRIVATE\n            ${CMAKE_CURRENT_SOURCE_DIR}/include\n            ${CMAKE_CURRENT_SOURCE_DIR}/src\n            ${PROJECT_SOURCE_DIR}/deps/cglm/include\n            ${PROJECT_SOURCE_DIR}/deps/ds/include\n            ${PROJECT_SOURCE_DIR}/deps/json/include)\n    target_link_libraries(assetkit_ktx2\n        PRIVATE\n            ${PROJECT_NAME}\n            ds\n            ${AK_KTX2_LIBRARY})\n    set_target_properties(assetkit_ktx2 PROPERTIES\n                          CXX_STANDARD 17\n                          CXX_STANDARD_REQUIRED YES\n                          CXX_VISIBILITY_PRESET hidden\n                          VISIBILITY_INLINES_HIDDEN YES)\n    if(AK_INSTALL_RPATH)\n      set_target_properties(assetkit_ktx2 PROPERTIES\n                            INSTALL_RPATH \"${AK_INSTALL_RPATH}\")\n    endif()\n\n    install(TARGETS assetkit_ktx2\n            LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}\n            RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})\n  endif()\nendif()\n\n# Test Configuration\nif(AK_USE_TEST)\n  include(CTest)\n  enable_testing()\n  add_subdirectory(test)\nendif()\n\n# Install \ninstall(TARGETS ${PROJECT_NAME}\n        EXPORT  ${PROJECT_NAME}\n        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}\n        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}\n        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})\n\ninstall(DIRECTORY include/ak DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}\n        PATTERN \".*\" EXCLUDE)\n\n# Config\nexport(TARGETS ${PROJECT_NAME}\n       NAMESPACE ${PROJECT_NAME}::\n       FILE \"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake\"\n)\n\ninstall(EXPORT      ${PROJECT_NAME}\n        FILE        \"${PROJECT_NAME}Config.cmake\"\n        NAMESPACE   ${PROJECT_NAME}::\n        DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})\n"
  },
  {
    "path": "EXTENSIONS.md",
    "content": "# Extensions\n\nAssetKit handles glTF extensions in three ways:\n\n- Typed support for extensions that affect AssetKit's runtime model.\n- Optional side decoder libraries for compressed payloads.\n- Preserved JSON payload through `ak_extra()` for app/vendor-specific data.\n\n## Required Extensions\n\n`extensionsRequired` is strict. If an extension is required for correct\ngeometry, animation, texture, material, or splat data, AssetKit must implement\nit or reject the asset.\n\n`extensionsUsed` can be more permissive. If an optional extension is not needed\nfor correct loading, AssetKit may preserve the payload in `ak_extra()` so a\nviewer/tool can inspect it.\n\n## Optional Decoder Libraries\n\nCompression and heavy decoders live outside the main C library. AssetKit loads\nthese side libraries only when needed:\n\n- `libassetkit_draco`\n- `libassetkit_meshoptimizer`\n- `libassetkit_spz`\n- `libassetkit_ktx2`\n\nOn Windows they are `assetkit_*.dll`; on macOS `libassetkit_*.dylib`; on Linux\n`libassetkit_*.so`. AssetKit first searches next to the loaded `libassetkit`\nbinary, then falls back to the platform loader search path.\n\nCMake builds the side libraries by default when dependencies are available or\ncan be fetched:\n\n```bash\ncmake -S . -B build -DCMAKE_BUILD_TYPE=Release\ncmake --build build\n```\n\nUseful options:\n\n```cmake\n-DAK_FETCH_DEPS=OFF\n-DAK_BUILD_GLTF_DRACO_DECODER=OFF\n-DAK_BUILD_GLTF_MESHOPT_DECODER=OFF\n-DAK_BUILD_GLTF_SPZ_DECODER=OFF\n-DAK_BUILD_GLTF_KTX2_DECODER=OFF\n```\n\n## Typed Extension Data\n\nSome extensions are represented as normal AssetKit fields:\n\n- `KHR_materials_variants`\n  - `AkDoc.materialVariants`\n  - `AkMeshPrimitive.variantMappings`\n  - `ak_materialVariantByName()`\n\n- `KHR_gaussian_splatting`\n  - `AkMeshPrimitive.gsplat`\n  - splat attributes stay in the primitive input chain\n\n- `KHR_animation_pointer`\n  - maps supported JSON pointer targets to AssetKit animation targets\n\nSPZ is only a decoder format. Public Gaussian splat data is generic and lives\nin `AkGaussianSplat`.\n\nMore detail: [docs/source/extensions.rst](docs/source/extensions.rst)\n"
  },
  {
    "path": "EXTRAS.md",
    "content": "# Extras\n\nAssetKit preserves source-format metadata that it does not need to interpret\ndirectly.\n\n- COLLADA: `<extra>`\n- glTF: `extras` and preserved `extensions` payloads\n\nUse `ak_extra()` to read it:\n\n```c\nAkTree *extra;\n\nextra = ak_extra(object);\nif (extra) {\n  /* Walk extra->chld, node->next and node->attribs. */\n}\n```\n\nUse `ak_extra_set()` when custom loader code wants to attach metadata:\n\n```c\nak_extra_set(object, extraTree);\n```\n\nThe returned `AkTree` is owned by the document heap. Do not free it directly.\n\n## COLLADA\n\nOlder AssetKit code stored COLLADA `<extra>` data directly on struct fields\nsuch as `node->extra`, `mesh->extra`, `geom->extra` or `material->extra`.\nThose fields still work. New code should prefer `ak_extra(object)` so the same\npath works for COLLADA and glTF.\n\n## glTF\n\nglTF metadata is stored under a root tree named `extra`:\n\n```text\nextra\n  extensions\n    KHR_example_extension\n      enabled   type=value val=true\n  extras\n    author     type=value val=...\n```\n\nEach glTF tree node has a `type` attribute:\n\n- `object`\n- `array`\n- `value`\n- `null`\n- `unknown`\n\nArray items are repeated child nodes named `item` and preserve source order.\n\nTyped AssetKit fields are still preferred for extensions AssetKit understands,\nfor example material variants or Gaussian splat metadata. `ak_extra()` is for\nmetadata, vendor payloads, debug payloads, and extension data that higher-level\ntools may want to inspect.\n\nMore detail: [docs/source/extras.rst](docs/source/extras.rst)\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n   "
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n   <img alt=\"\" src=\"assetkit.png\" width=\"550\" />\n</p>\n<br>\n\n<p align=\"center\">\n    <a href=\"https://github.com/recp/AssetKit/actions/workflows/c-cpp.yml\">\n        <img src=\"https://github.com/recp/AssetKit/actions/workflows/c-cpp.yml/badge.svg\"\n             alt=\"C/C++ CI\">\n    </a>\n    <a href=\"https://github.com/recp/AssetKit/actions/workflows/cmake.yml\">\n        <img src=\"https://github.com/recp/AssetKit/actions/workflows/cmake.yml/badge.svg\"\n             alt=\"CMake\">\n    </a>\n    <a href=\"https://www.codacy.com/app/recp/assetkit?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=recp/assetkit&amp;utm_campaign=Badge_Grade\">\n        <img src=\"https://api.codacy.com/project/badge/Grade/6edde2ba446148759437eb0148c799b6\"\n             alt=\"Codacy Badge\"/>\n    </a>\n    <a href=\"https://coveralls.io/github/recp/assetkit?branch=master\">\n        <img src=\"https://coveralls.io/repos/github/recp/assetkit/badge.svg?branch=master\"\n             alt=\"Coverage Status\"/>\n    </a>\n    <img src=\"https://img.shields.io/badge/glTF-2%2E0-green.svg?style=flat\"\n         alt=\"glTF Badge\">\n    <br /><br />\n    <a href=\"https://patreon.com/recp\">\n      <img src=\"https://img.shields.io/badge/Patreon-Become a patron-orange.svg\"\n           alt=\"Patreon: Become a patron\">\n    </a>\n    <a href=\"#sponsors\">\n        <img src=\"https://opencollective.com/assetkit/sponsors/badge.svg\"\n             alt=\"Sponsors on Open Collective\"/>\n    </a>\n    <a href=\"#backers\">\n        <img src=\"https://opencollective.com/assetkit/backers/badge.svg\"\n             alt=\"Backers on Open Collective\"/>\n    </a>\n</p>\n\n<br>\n\n<p align=\"center\">\nBrand-new modern 3D asset importer, exporter library. This library will include common 3D utils funcs. It is written with C99 but C++ wrappers or other language bindings can be written in the future.\n\nThis library will try to full support COLLADA specs and glTF specs, plus well-known other 3D formats e.g .obj, .stl, .ply... \n\n📌 There is also an optional renderer library called [Graphics Kernel (Realtime Rendering)](https://github.com/recp/libgk) and [rays (Path/Ray Tracer)](https://github.com/recp/rays) which can render **AssetKit** contents. You can see how to load **AssetKit** to [Graphics Kernel](https://github.com/recp/libgk) in [AssetKit-GL](https://github.com/recp/assetkit-gl) repo. Both renderers and documentation with samples will be updated regularly...  \n\n</p>\n\n#### 📚 Documentation (In Progress)\n\nAlmost all functions (inline versions) and parameters will be documented inside the corresponding headers. <br />\nComplete documentation: http://assetkit.readthedocs.io\n\nRuntime metadata and extension notes:\n\n- [Extras and extension data](EXTRAS.md)\n- [glTF extensions and optional decoders](EXTENSIONS.md)\n\n## 💪 Supported Formats\n\n* [ ] Asset Exchange (todo) http://github.com/AssetExchange/spec\n* [x] COLLADA 1.4 and COLLADA 1.4.1\n* [x] COLLADA 1.5\n* [x] glTF 2.0 (Embedded or Separated (.gltf), Binary (.glb), Extensions...)\n* [x] Wavefront Obj (.obj + .mtl)\n* [x] STL (ASCII, Binary)\n* [x] PLY (ASCII, Binary)\n* [ ] 3MF (in progress)\n* [ ] FBX (License?, probably need to download FBX SDK externally)\n* [ ] USD and friends (License?)\n* [ ] Alembic (License?)\n* [ ] Draco\n* [ ] X3D\n* [x] in progress for next...\n* [ ] Exporter\n\n## 🚀 Features\n\n- Single interface for glTF 2.0 (with extensions), COLLADA 1.4/1.4.1/1.5, Wavefront Obj and others...\n- Very very small and very fast library\n- Javascript-like API to get URL or ID `obj = ak_getObjectById(doc, objectId)`...\n- Options to Generate Mesh Normals *(Default: enabled)*\n- Option to Triangulate Polygons *(Default: enabled)*\n- Option to change Coordinate System *(Default: enabled)*\n- Option to calculate Bounding Boxes *(Default: enabled)*\n- Unique and Flexible Coordinate System\n  - Support multiple coordinate system\n  - Can convert any coordinate system to another with adding transform or with changing transform, vertex data...\n- Unique and Flexible Memory Management System\n  - Hierarchical unique memory management\n    - When a node is freed then all sub memories will be freed\n  - COLLADA's **sid** and **ID** values are mapped to memory nodes itself to reduce memory size and make it easy to manage things.\n  - Allow attach ID, sid or user data to a memory node\n- Object-based Asset support; resolve asset element for any element\n- Bugfix some DAE files\n- Will be optimized to be fastest, smallest and most flexible, extendible Asset loader.\n- Uses **mmap** to load files, you can disable this if needed\n- [ ] Documentation\n- [x] Cmake support\n- [ ] Tests\n\n## 🔨 Build\n\nAssetKit uses CMake on macOS, Linux and Windows. It can be built as a\nstandalone project, embedded with `add_subdirectory()` or installed and used\nwith `find_package()`.\n\n### Standalone CMake build\n\n```bash\n$ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release\n$ cmake --build build\n$ cmake --install build # [Optional]\n```\n\nOn multi-config generators such as Visual Studio or Xcode:\n\n```bash\n$ cmake -S . -B build\n$ cmake --build build --config Release\n$ cmake --install build --config Release # [Optional]\n```\n\nRelease builds use the platform compiler's optimized release mode. AssetKit\nalso has `AK_ENABLE_LTO=ON` for link-time optimization when the compiler and\ngenerator support it; it is off by default for predictable cross-platform\nbuilds.\n\n##### CMake options with defaults:\n\n```CMake\noption(AK_SHARED \"Shared build\" ON)\noption(AK_STATIC \"Static build\" OFF)\noption(AK_USE_TEST \"Enable Tests\" OFF)\noption(AK_BUILD_GLTF_DRACO_DECODER \"Build optional glTF Draco decoder shim\" ON)\noption(AK_BUILD_GLTF_MESHOPT_DECODER \"Build optional glTF meshoptimizer decoder shim\" ON)\noption(AK_FETCH_DEPS \"Fetch optional decoder dependencies into AK_DEPS_ROOT when missing\" ON)\noption(AK_ENABLE_LTO \"Enable link-time optimization for release builds\" OFF)\n```\n\nOptional glTF compression decoders are side libraries. They are built next to\nthe main C library by default, but not linked into `libassetkit`. CMake fetches\nmissing decoder dependencies into `deps/` by default, so a normal standalone\nbuild produces `libassetkit`, `libassetkit_draco` and\n`libassetkit_meshoptimizer` when network access is available:\n\n```bash\n$ cmake -S . -B build\n$ cmake --build build\n```\n\nUse `-DAK_FETCH_DEPS=OFF` with `AK_DRACO_ROOT` / `AK_MESHOPT_ROOT` for\noffline or packaged builds. Use `-DAK_BUILD_GLTF_DRACO_DECODER=OFF` or\n`-DAK_BUILD_GLTF_MESHOPT_DECODER=OFF` to skip these side libraries.\n\n### Embedded in another CMake project\n\nAssetKit can be used like a submodule:\n\n```cmake\ncmake_minimum_required(VERSION 3.16)\n\nproject(my_app LANGUAGES C)\n\nadd_subdirectory(external/assetkit)\n\nadd_executable(my_app src/main.c)\ntarget_link_libraries(my_app PRIVATE assetkit::assetkit)\n```\n\nWhen embedded, AssetKit does not force a default build type on the parent\nproject. If you do not want CMake to fetch optional decoder dependencies while\nconfiguring your parent project, pass:\n\n```cmake\nset(AK_FETCH_DEPS OFF CACHE BOOL \"\" FORCE)\nset(AK_BUILD_GLTF_DRACO_DECODER OFF CACHE BOOL \"\" FORCE)\nset(AK_BUILD_GLTF_MESHOPT_DECODER OFF CACHE BOOL \"\" FORCE)\nadd_subdirectory(external/assetkit)\n```\n\n### Installed package\n\nAfter `cmake --install`, consumers can use:\n\n```cmake\nfind_package(assetkit CONFIG REQUIRED)\ntarget_link_libraries(my_app PRIVATE assetkit::assetkit)\n```\n\n### Windows\nWindows builds are supported through CMake. `git` must be available when\n`GIT_SUBMODULE=ON` or `AK_FETCH_DEPS=ON`.\n\n```Powershell\n$ cmake -S . -B build\n$ cmake --build build --config Release\n```\n\n## Contributors\n\nThis project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].\n<a href=\"https://github.com/recp/assetkit/graphs/contributors\"><img src=\"https://opencollective.com/assetkit/contributors.svg?width=890&button=false\" /></a>\n\n\n## Backers\n\nThank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/assetkit#backer)]\n\n<a href=\"https://opencollective.com/assetkit#backers\" target=\"_blank\"><img src=\"https://opencollective.com/assetkit/backers.svg?width=890\"></a>\n\n\n## Sponsors\n\nSupport this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/assetkit#sponsor)]\n\n<a href=\"https://opencollective.com/assetkit/sponsor/0/website\" target=\"_blank\"><img src=\"https://opencollective.com/assetkit/sponsor/0/avatar.svg\"></a>\n<a href=\"https://opencollective.com/assetkit/sponsor/1/website\" target=\"_blank\"><img src=\"https://opencollective.com/assetkit/sponsor/1/avatar.svg\"></a>\n<a href=\"https://opencollective.com/assetkit/sponsor/2/website\" target=\"_blank\"><img src=\"https://opencollective.com/assetkit/sponsor/2/avatar.svg\"></a>\n<a href=\"https://opencollective.com/assetkit/sponsor/3/website\" target=\"_blank\"><img src=\"https://opencollective.com/assetkit/sponsor/3/avatar.svg\"></a>\n<a href=\"https://opencollective.com/assetkit/sponsor/4/website\" target=\"_blank\"><img src=\"https://opencollective.com/assetkit/sponsor/4/avatar.svg\"></a>\n<a href=\"https://opencollective.com/assetkit/sponsor/5/website\" target=\"_blank\"><img src=\"https://opencollective.com/assetkit/sponsor/5/avatar.svg\"></a>\n<a href=\"https://opencollective.com/assetkit/sponsor/6/website\" target=\"_blank\"><img src=\"https://opencollective.com/assetkit/sponsor/6/avatar.svg\"></a>\n<a href=\"https://opencollective.com/assetkit/sponsor/7/website\" target=\"_blank\"><img src=\"https://opencollective.com/assetkit/sponsor/7/avatar.svg\"></a>\n<a href=\"https://opencollective.com/assetkit/sponsor/8/website\" target=\"_blank\"><img src=\"https://opencollective.com/assetkit/sponsor/8/avatar.svg\"></a>\n<a href=\"https://opencollective.com/assetkit/sponsor/9/website\" target=\"_blank\"><img src=\"https://opencollective.com/assetkit/sponsor/9/avatar.svg\"></a>\n\n\n### Trademarks\n\nglTF and COLLADA and their logos are trademarks of Khronos Group.\n"
  },
  {
    "path": "appveyor.yml",
    "content": "image: Visual Studio 2017\n\nbuild_script:\n- ps: >-\n    cd win\n\n    .\\build.bat\n"
  },
  {
    "path": "docs/make.bat",
    "content": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=python -msphinx\r\n)\r\nset SOURCEDIR=source\r\nset BUILDDIR=build\r\nset SPHINXPROJ=cglm\r\n\r\nif \"%1\" == \"\" goto help\r\n\r\n%SPHINXBUILD% >NUL 2>NUL\r\nif errorlevel 9009 (\r\n\techo.\r\n\techo.The Sphinx module was not found. Make sure you have Sphinx installed,\r\n\techo.then set the SPHINXBUILD environment variable to point to the full\r\n\techo.path of the 'sphinx-build' executable. Alternatively you may add the\r\n\techo.Sphinx directory to PATH.\r\n\techo.\r\n\techo.If you don't have Sphinx installed, grab it from\r\n\techo.http://sphinx-doc.org/\r\n\texit /b 1\r\n)\r\n\r\n%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\r\ngoto end\r\n\r\n:help\r\n%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%\r\n\r\n:end\r\npopd\r\n"
  },
  {
    "path": "docs/source/api.rst",
    "content": "API documentation\n================================\n\n.. toctree::\n   :maxdepth: 1\n   :caption: API categories:\n\n   version\n"
  },
  {
    "path": "docs/source/build.rst",
    "content": "Build AssetKit\n================================\n\n| **AssetKit** core uses bundled submodules. Optional glTF decoder side\n  libraries can fetch their decoder dependencies when enabled. The same CMake\n  project can be used standalone, as a subdirectory in another CMake project,\n  or as an installed package.\n\nStandalone CMake build:\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: bash\n  :linenos:\n\n  $ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release\n  $ cmake --build build\n  $ cmake --install build # [Optional]\n\nWith multi-config generators such as Visual Studio or Xcode:\n\n.. code-block:: bash\n  :linenos:\n\n  $ cmake -S . -B build\n  $ cmake --build build --config Release\n  $ cmake --install build --config Release # [Optional]\n\nThe build folder contains the main AssetKit library and, by default, optional\nglTF decoder side libraries.\n\n**CMake Options:**\n\n.. code-block:: CMake\n  :linenos:\n\n  option(AK_SHARED \"Shared build\" ON)\n  option(AK_STATIC \"Static build\" OFF)\n  option(AK_USE_C99 \"\" OFF) # C11 \n  option(AK_USE_TEST \"Enable Tests\" OFF) # for make check - make test\n  option(AK_BUILD_GLTF_DRACO_DECODER \"Build optional glTF Draco decoder shim\" ON)\n  option(AK_BUILD_GLTF_MESHOPT_DECODER \"Build optional glTF meshoptimizer decoder shim\" ON)\n  option(AK_FETCH_DEPS \"Fetch optional decoder dependencies into AK_DEPS_ROOT when missing\" ON)\n  option(AK_ENABLE_LTO \"Enable link-time optimization for release builds\" OFF)\n\n``AK_ENABLE_LTO`` is optional. Release builds already use optimized compiler\nflags; LTO is off by default because support differs by compiler, generator and\nplatform.\n\nEmbedded in another CMake project:\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: CMake\n  :linenos:\n\n  cmake_minimum_required(VERSION 3.16)\n  \n  project(my_app LANGUAGES C)\n  \n  add_subdirectory(external/assetkit/)\n\n  add_executable(my_app src/main.c)\n  target_link_libraries(my_app PRIVATE assetkit::assetkit)\n\nWhen embedded, AssetKit does not force a default build type on the parent\nproject. If dependency fetching is not wanted during parent configure:\n\n.. code-block:: CMake\n  :linenos:\n\n  set(AK_FETCH_DEPS OFF CACHE BOOL \"\" FORCE)\n  set(AK_BUILD_GLTF_DRACO_DECODER OFF CACHE BOOL \"\" FORCE)\n  set(AK_BUILD_GLTF_MESHOPT_DECODER OFF CACHE BOOL \"\" FORCE)\n  add_subdirectory(external/assetkit/)\n\nInstalled package:\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: CMake\n  :linenos:\n\n  find_package(assetkit CONFIG REQUIRED)\n  target_link_libraries(my_app PRIVATE assetkit::assetkit)\n\nOptional glTF compression decoders:\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n.. code-block:: bash\n  :linenos:\n\n  $ cmake -S . -B build\n  $ cmake --build build\n\nDecoder side libraries are built next to ``libassetkit`` by default, but not\nlinked into the main C library. CMake fetches missing decoder dependencies into\n``deps/`` by default. Use ``-DAK_FETCH_DEPS=OFF`` with ``AK_DRACO_ROOT`` /\n``AK_MESHOPT_ROOT`` for offline or packaged builds. Use\n``-DAK_BUILD_GLTF_DRACO_DECODER=OFF`` or\n``-DAK_BUILD_GLTF_MESHOPT_DECODER=OFF`` to skip these side libraries.\n\nWindows:\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nWindows builds use CMake. ``git`` must be available when ``GIT_SUBMODULE=ON``\nor ``AK_FETCH_DEPS=ON``.\n\n.. code-block:: bash\n  :linenos:\n\n  $ cmake -S . -B build\n  $ cmake --build build --config Release\n\nCurrently tests are not available on Windows.\n\nDocumentation (Sphinx):\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n**AssetKit** uses sphinx framework for documentation, it allows lot of formats for documentation. To see all options see sphinx build page:\n\nhttps://www.sphinx-doc.org/en/master/man/sphinx-build.html\n\nExample build:\n\n.. code-block:: bash\n  :linenos:\n\n  $ cd assetkit/docs\n  $ sphinx-build source build\n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "# -*- coding: utf-8 -*-\n#\n# AssetKit documentation build configuration file, created by\n# sphinx-quickstart on Tue Jun  6 20:31:05 2017.\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\n# import os\n# import sys\n# sys.path.insert(0, os.path.abspath('.'))\n\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\n# needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    'sphinx.ext.doctest',\n    'sphinx.ext.todo',\n    'sphinx.ext.coverage',\n    'sphinx.ext.mathjax',\n    'sphinx.ext.ifconfig',\n    'sphinx.ext.viewcode',\n    'sphinx.ext.githubpages'\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\n# source_suffix = ['.rst', '.md']\nsource_suffix = '.rst'\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = u'AssetKit'\ncopyright = u'2020, Recep Aslantas'\nauthor = u'Recep Aslantas'\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = u'0.3.2'\n# The full version, including alpha/beta/rc tags.\nrelease = u'0.3.2'\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = None\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This patterns also effect to html_static_path and html_extra_path\nexclude_patterns = []\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = False\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\nhtml_theme = 'sphinx_rtd_theme'\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\n# html_theme_options = {}\n\nhtml_theme_options = {\n    # 'github_banner': 'true',\n    # 'github_button': 'true',\n    # 'github_user': 'recp',\n    # 'github_repo': 'AssetKit',\n    # 'travis_button': 'true',\n    # 'show_related': 'true',\n    # 'fixed_sidebar': 'true'\n}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\nhtml_static_path = ['_static']\n\n\n# -- Options for HTMLHelp output ------------------------------------------\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'assetkitdoc'\n\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {\n    # The paper size ('letterpaper' or 'a4paper').\n    #\n    # 'papersize': 'letterpaper',\n\n    # The font size ('10pt', '11pt' or '12pt').\n    #\n    # 'pointsize': '10pt',\n\n    # Additional stuff for the LaTeX preamble.\n    #\n    # 'preamble': '',\n\n    # Latex figure (float) alignment\n    #\n    # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (master_doc, 'AssetKit.tex', u'AssetKit Documentation',\n     u'Recep Aslantas', 'manual'),\n]\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    (master_doc, 'AssetKit', u'AssetKit Documentation',\n     [author], 1)\n]\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (master_doc, 'AssetKit', u'AssetKit Documentation',\n     author, 'AssetKit', 'One line description of project.',\n     'Miscellaneous'),\n]\n\n# -- Options for Epub output -------------------------------------------------\n\n# Bibliographic Dublin Core info.\nepub_title = project\nepub_author = author\nepub_publisher = author\nepub_copyright = copyright\n\n# The unique identifier of the text. This can be a ISBN number\n# or the project homepage.\n#\n# epub_identifier = ''\n\n# A unique identification for the text.\n#\n# epub_uid = ''\n\n# A list of files that should not be packed into the epub file.\nepub_exclude_files = ['search.html']\n\n\n# -- Extension configuration -------------------------------------------------\n\n# -- Options for todo extension ----------------------------------------------\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = True\n"
  },
  {
    "path": "docs/source/getting_started.rst",
    "content": "Getting Started\n================================\n\nThere are lot of file formats out there. It is not easy to implement each of them \nindividually in an engine or software. \n\n**AssetKit** provides single extensible interface for all file formats that is supported. \n**AssetKit** tries to full support all major file formats.\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": ".. cglm documentation master file, created by\n   sphinx-quickstart on Tue Jun  6 20:31:05 2017.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\n.. image:: assetkit.png\n   :width: 492px \n   :height: 297px\n   :align: center\n\n|\n\n**AssetKit** is brand-new 2D/3D asset importer, exporter and util library that\nis written in C language. C++ wrappers or other language bindings \ncan be written in the future. This library will include common 3D util funcs.\n\nSupported Formats\n================================================================================\n\n* [x] COLLADA 1.4 and COLLADA 1.4.1\n* [x] COLLADA 1.5\n* [x] glTF 2.0 (Embedded or Separated (.gltf), Binary (.glb), Extensions...)\n* [x] Wavefront Obj (.obj + .mtl)\n* [x] STL (ASCII, Binary)\n* [x] PLY (ASCII, Binary)\n* [ ] USD and friends (License?)\n* [ ] Alembic (License?)\n* [ ] Draco\n* [ ] X3D\n* [x] in progress for next...\n\nFeatures\n================================================================================\n\n* Very very small, very fast and flexible library\n* Single interface for glTF 2.0 (with extensions), COLLADA 1.4/1.4.1/1.5, Wavefront Obj and others...\n* Javascript-like API to get URL or ID `obj = ak_getObjectById(doc, objectId)`...\n* Options to Generate Mesh Normals *(Default: enabled)*\n* Option to Triangulate Polygons *(Default: enabled)*\n* Option to change Coordinate System *(Default: enabled)*\n* Option to calculate Bounding Boxes *(Default: enabled)*\n* Unique and Flexible Coordinate System\n  * Support multiple coordinate system\n  * Can convert any coordinate system to another with adding transform or with changing transform, vertex data...\n* Unique and Flexible Memory Management System \n    * Hierarchical unique memory management \n    * When a node is freed then all sub memories will be freed\n    * COLLADA's **sid** and **ID** values are mapped to memory nodes itself to reduce memory size and make it easy to manage things.\n    * Allow attach ID, sid or user data to a memory node\n* Object-based Asset support; resolve asset element for any element\n* Bugfix some DAE files\n* Will be optimized to be fastest, smallest and most flexible, extendible Asset loader.\n* and others...\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Getting Started:\n\n   build\n   getting_started\n\n.. toctree::\n   :maxdepth: 3\n   :caption: Tutorials:\n\n   quick_intro\n\n.. toctree::\n   :maxdepth: 2\n   :caption: API:\n\n   api\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Options:\n\n   opt\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n"
  },
  {
    "path": "docs/source/opt.rst",
    "content": ".. default-domain:: C\n\nOptions\n===============================================================================\n\nCurrently **AssetKit** provides global options but in th future document based \noptions may be supported.\n\nOptions / Preferences / Settings Design\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nTo make things simplier and easy to manage, **AssetKit** provides options as \n**key** - **value** pair. \n\nThe key always is the :code:`AkOption` enum type. \nThe value's type is :code:`uintptr_t`, so you can pass integers, enums and pointers (by casting to `uintptr_t` e.g. :code:`(uintptr_t)(void *)myPointer`).\n\nDocumented global options:\n\n.. code-block:: c\n\n    typedef enum AkOption {\n      AK_OPT_INDICES_DEFAULT            = 0,  /* false    */\n      AK_OPT_INDICES_SINGLE_INTERLEAVED = 1,  /* false    */\n      AK_OPT_INDICES_SINGLE_SEPARATE    = 2,  /* false    */\n      AK_OPT_INDICES_SINGLE             = 3,  /* false    */\n      AK_OPT_NOINDEX_INTERLEAVED        = 4,  /* true     */\n      AK_OPT_NOINDEX_SEPARATE           = 5,  /* true     */\n      AK_OPT_COORD                      = 6,  /* Y_UP     */\n      AK_OPT_DEFAULT_ID_PREFIX          = 7,  /* id-      */\n      AK_OPT_COMPUTE_BBOX               = 8,  /* false    */\n      AK_OPT_TRIANGULATE                = 9,  /* true     */\n      AK_OPT_GEN_NORMALS_IF_NEEDED      = 10, /* true     */\n      AK_OPT_DEFAULT_PROFILE            = 11, /* COMMON   */\n      AK_OPT_EFFECT_PROFILE             = 12, /* true     */\n      AK_OPT_TECHNIQUE                  = 13, /* \"common\" */\n      AK_OPT_TECHNIQUE_FX               = 14, /* \"common\" */\n      AK_OPT_ZERO_INDEXED_INPUT         = 15, /* false    */\n      AK_OPT_IMAGE_LOAD_FLIP_VERTICALLY = 16, /* true     */\n      AK_OPT_ADD_DEFAULT_CAMERA         = 17, /* true     */\n      AK_OPT_ADD_DEFAULT_LIGHT          = 18, /* false    */\n      AK_OPT_COORD_CONVERT_TYPE         = 19, /* DEFAULT  */\n      AK_OPT_BUGFIXES                   = 20, /* TRUE     */\n      AK_OPT_GLTF_EXT_SPEC_GLOSS        = 21, /* TRUE     */\n      AK_OPT_COMPUTE_EXACT_CENTER       = 22  /* FALSE    */\n    } AkOption;\n\nThe comment at right contains default value for that option. \nAll options will be documented below. (**TODO**)\n\nAs you can see and imagine, not all options are integer or boolean.\nFor instance to set :code:`Y_UP` for :code:`AK_OPT_COORD` option, you need to cast :code:`Y_UP` opinter to :code:`uintptr_t` type.\n\nSample options:\n\n.. code-block:: c\n\n    /* compute bounding box for mesh and for its primitives */\n    ak_opt_set(AK_OPT_COMPUTE_BBOX, true);\n\n    /* triangulate meshes that are not triangles e.g. polygons... */\n    ak_opt_set(AK_OPT_TRIANGULATE, true);\n\n    /* change UP axis to Y UP, see coordinate sys documentation. */\n    ak_opt_set(AK_OPT_TRIANGULATE, (uintptr_t)AK_YUP);\n\nFunctions:\n\n1. :c:func:`ak_opt_set`\n#. :c:func:`ak_opt_get`\n#. :c:func:`ak_opt_set_str`\n\n.. c:function:: void ak_opt_set(AkOption option, uintptr_t value)\n\n    Sets an option by key and value. Pass integers, booleans or pointers as value. \n    Value needs to be casted to **uintptr_t**. Pass only supported values for a key.\n\n    Parameters:\n      | *[in]* **option** option (see AkOption enum)\n      | *[in]* **value**  option value\n\n.. c:function:: uintptr_t ak_opt_get(AkOption option)\n\n    Get value of option as **uintptr_t**. If the option is pointer than you need to cast it to pointer. \n    For instance :code:`(AkCoordSys *)ak_opt_get(AK_OPT_COORD)` or :code:`(void *)ak_opt_get(AK_OPT_COORD)`.\n    If there are no warnings then you don't need to cast result to Boolean or Integers for Boolean/Integer options.\n    For instance :code:`if (ak_opt_get(AK_OPT_TRIANGULATE)) ...`\n\n    Parameters:\n      | *[in]* **option** option (see AkOption enum)\n\n.. c:function:: void ak_opt_set_str(AkOption option, const char *value)\n\n    Similar to :c:func:`ak_opt_set` but it accepts null terminated string parameter \n    and it uses :c:func:`ak_strdup` to duplicate string to keep it. \n    Then it casts duplicated string to :code:`uintptr_t`, so you can get value anytime with :c:func:`ak_opt_get`.\n\n    **NOTE:** When you set new value then the old value will be free-ed. \n    If you need to keep old value then you must duplicate it yourself.\n    Otherwise memory leaks would occoured...\n\n    Parameters:\n      | *[in]* **option** option (see AkOption enum)\n      | *[in]* **value**  option value as null-terminated string\n"
  },
  {
    "path": "docs/source/quick_intro.rst",
    "content": "Quick Implementation\n===================================\n\nAssuming you already followed Build instructions and Getting Started sections.\nAlso assuming you already linked **AssetKit** to your project and set include paths.\n\n1. Include\n----------------\n\n**AssetKit** uses **ak** prefix for all functions and type names. To include **AssetKit** \n\n.. code-block:: c\n  :linenos:\n\n  #include <ak/assetkit.h>\n\n  /* other headers */\n  #include <ak/options.h>\n  ...\n\n2. Preparing\n----------------\n\nYou may want to prepare loader before call :c:func:`ak_load` load function. \n\na. Setting Image loader\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nThere was image loader inside **AssetKit** but it has been dropped to make it more generic.\nBecasue you may already have an image loader e.g. stb_image ... \n\n**AssetKit** can trigger images and cache them for you, if it is already loaded than it will return loaded contents.\n\nYou need to set loader as below. The example used stb_image but you mat use another image loader...\n\n.. code-block:: c\n  :linenos:\n\n  void*\n  imageLoadFromFile(const char * __restrict path,\n                    int        * __restrict width,\n                    int        * __restrict height,\n                    int        * __restrict components) {\n    return stbi_load(path, width, height, components, 0);\n  }\n\n  void*\n  imageLoadFromMemory(const char * __restrict data,\n                      size_t                  len,\n                      int        * __restrict width,\n                      int        * __restrict height,\n                      int        * __restrict components) {\n    return stbi_load_from_memory((stbi_uc const*)data, (int)len, width, height, components, 0);\n  }\n\n  void\n  imageFlipVerticallyOnLoad(bool flip) {\n    stbi_set_flip_vertically_on_load(flip);\n  }\n\n\n  /* Call this before loading document e.g. ak_load() or images */\n  ak_imageInitLoader(imageLoadFromFile, imageLoadFromMemory, imageFlipVerticallyOnLoad);\n\nJust ensure that you set image loader before loading images. It is good to set it once before :c:func:`ak_load`.\n\nb. Setting Options if Needed\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nMake sure that you set UP axis if you want to different one from **Y_UP**. \nFor instance if you want to load contents as **Z_UP** \n\n.. code-block:: c\n\n  ak_opt_set(AK_OPT_COORD, (uintptr_t)AK_ZUP);\n\nsee options in the documentation. If the default values don't work for you then just change them before :c:func:`ak_load`.\n\n3. Load Document\n----------------\n\nUse :c:func:`ak_load()` to load a 3D file. It returns :c:type:`AkDoc` which contains everything you need.\nYou must check the result/return, it must be AK_OK otherwise, document is not loaded or falied at some point.\n\n.. code-block:: c\n  :linenos:\n\n  AkDoc   *doc;\n  AkResult ret;\n  \n  if ((ret = ak_load(&doc, \"[Path to a file e.g ./sample.gltf]\", NULL) != AK_OK) {\n     printf(\"Document couldn't be loaded\");\n     return;\n  }\n\nor \n\n.. code-block:: c\n  :linenos:\n\n  AkDoc   *doc;\n  AkResult ret;\n  \n  ret = ak_load(&doc, \"[Path to a file e.g ./sample.gltf]\", NULL);\n  if (ret != AK_OK) {\n     printf(\"Document couldn't be loaded\");\n     return;\n  }\n\n**doc** is passed as reference, if the result is success than the document will be set that reference parameter.\n\n**AssetKit** will try to load referenced textures, images, binary files... so you must only pass original file, not folder.\n\nNow you loaded the document you want. See next step.\n\n------\n\nThere are two ways to load geometries from loaded document.\n\na. Load scene[s], nodes than load referenced geometries\nb. Load all geometries in the document\n\nThe second way may cause to load unused geometries, because a geometry may not be referenced in scenes.\nIt is better to follow scene > node > instance geometry > geometry path.\n\n4. Load Scene[s]\n----------------\n\n**AssetKit** can load scenes, nodes, geometries and so on. If the file you loaded doesn't support scenes e.g Wavefront Obj.\nAssetKit creates a default scene for that file formats and adds reference of geometries to that scene.\n\nThere are **scene library** and **scene** in AssetKit **document**. The **scene** is the active scene for rendering, it references a scene from library.\n\n.. code-block:: c\n  :linenos:\n\n  AkInstanceBase *instScene;\n  AkVisualScene  *scene;\n\n  if ((instScene = doc->scene.visualScene)) {\n    scene = (AkVisualScene *)ak_instanceObject(doc->scene.visualScene);\n  }\n\n`scene.visualScene` is instance reference ( :c:type:`AkInstanceBase` ), any scene may be instanced with this link/object.\nAnother instance objects may have different types e.g. instance geometry (inherited from :c:type:`AkInstanceBase`).\n\nWe need to get actual scene object from instance object. There are a few helpers for this task.\nBut we will use :c:func:`ak_instanceObject` here. \n\n5. Load Nodes[s]\n----------------\n\nAfter you get a scene, you can iterate through root nodes. There are also nodes in NodeLibrary in document but in this way you only get used nodes.\n\nThere are a few elements in nodes\n\n- Node Transform\n- One or more instance geometries\n- One or more instance cameras\n- One or more instance lights\n- One or more instance nodes\n- One or more child nodes\n- Bounding Box as AABB of Node\n- ...\n\n5.1 Transforms\n~~~~~~~~~~~~~~~~\n\nYou must multiply node's transform with its parent to get transform in WORLD space for each node recursively.\n\nA node can contain Matrix or individual Transform Elements like Rotation, Translation or Scaling. \n**AssetKit** also provides a util to combine these individual transforms into matrix with :c:func:`ak_transformCombine`.\n\n**AssetKit** does not combines them automatically because they may be referenced to animated individually.\n\n5.2 Instance Geometries\n~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis is the one of critical sections to understand. Nodes uses :c:type:`AkInstanceGeometry` type to reference a :c:type:`AkGeometry`.\n\nA :c:type:`AkInstanceGeometry` object may store these informations:\n\n* Instance to geometry\n* Material to bind\n* Instance to morpher\n* Instance to skinner\n\n5.2.1 Instance to geometry | Loading Geometry\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nA node may contain multiple geometries, so you must iterate each one and get the geometry with :c:func:`ak_instanceObject` function.\n\nAfter you get the geometry you can load geeometry elements. A :c:type:`AkGeometry` object can contain one of mesh, spline and brep.\n\n.. code-block:: c\n  :linenos:\n\n  AkObject *prim;\n  AkResult  ret;\n\n  /* \n     return if the geometry is already loaded, \n     you can use a RBTree or HasMap... (see https://github.com/recp/ds) \n    */\n\n  prim = geom->gdata;\n  switch ((AkGeometryType)prim->type) {\n    case AK_GEOMETRY_MESH:\n       /* load mesh */\n      ret = loadMesh(...);\n      break;\n    default:\n      ret = AK_ERR;\n  }\n\n  return ret;\n\nNow it is time to load a mesh.\n\n5.2.2 Loading mesh\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThis tutorial will only cover loading meshes, extra tutorials may be provided in the future for loading curves, nurbs...\n\nA mesh object is packed as :c:type:`AkObject` inside :c:type:`AkGeometry`. In previous section you may see that we have \na switch control to check whether we have a mesh inside geometry or not.\n\n**AssetKit** provides unique design to store this kind of objects with :c:type:`AkObject`. \n(Think :c:type:`AkObject` as **Object** class in .NET or **NSObject** in ObjC.)\nOtherwise we would store \nadditional pointers or inherits Mesh from Geometry and then cast it to mesh. This is another option of course, \neven **AssetKit** may change to this design in the future if needed. Currently we are not doing this because geometry \nobject is top container.\n\n**AssetKit** provides a helper to get object from :c:type:`AkObject` with :c:func:`ak_objGet` macro.\n\nWe can get :c:type:`AkMesh` object from :c:type:`AkGeometry` as\n\n.. code-block:: c\n  :linenos:\n\n  AkMesh *mesh;\n  \n  mesh = ak_objGet(geom->gdata);\n\nNow we have mesh object. Let's inspect a mesh type.\n\nA mesh contains one or more primitives (or submeshes) as :c:type:`AkMeshPrimitive`. \nEach primitive contains AABB, the mesh also contains an AABB which is sum of all.\n\nA mesh also contains default weights for morph targets but a Node in Scene object can override that.\n\n5.2.2.1 Loading mesh primitives\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nA mesh primitive may be one of :c:type:`AkLines`, :c:type:`AkPolygon` or :c:type:`AkTriangles`. \n:c:type:`AkMeshPrimitive` is base type for all of them. You can cast them to :c:type:`AkMeshPrimitive` or you can use **.base** member.\n"
  },
  {
    "path": "docs/source/version.rst",
    "content": ".. default-domain:: C\n\nversion\n================================================================================\n\nHeader: ak/version.h\n\n**AssetKit** uses semantic versioning (http://semver.org) which is MAJOR.MINOR.PATCH \n\n| **AK_VERSION_MAJOR** is major number of the version.\n| **AK_VERSION_MINOR** is minor number of the version.\n| **AK_VERSION_PATCH** is patch number of the version.\n\nevery release increases these numbers. You can check existing version by \nincluding `ak/version.h`\n"
  },
  {
    "path": "include/ak/animation.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_animation_h\n#define assetkit_animation_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n  \n#include <stdint.h>\n#include <stdbool.h>\n#include <string.h>\n#include \"type.h\"\n#include \"source.h\"\n#include \"url.h\"\n\ntypedef enum AkSamplerBehavior {\n  AK_SAMPLER_BEHAVIOR_UNDEFINED      = 0,\n  AK_SAMPLER_BEHAVIOR_CONSTANT       = 1,\n  AK_SAMPLER_BEHAVIOR_GRADIENT       = 2,\n  AK_SAMPLER_BEHAVIOR_CYCLE          = 3,\n  AK_SAMPLER_BEHAVIOR_OSCILLATE      = 4,\n  AK_SAMPLER_BEHAVIOR_CYCLE_RELATIVE = 5\n} AkSamplerBehavior;\n\ntypedef enum AkTargetPropertyType {\n  AK_TARGET_UNKNOWN  = 0,\n  AK_TARGET_X        = 1,\n  AK_TARGET_Y        = 2,\n  AK_TARGET_Z        = 3,\n  AK_TARGET_XY       = 4,\n  AK_TARGET_XYZ      = 5,\n  AK_TARGET_ANGLE    = 6,\n  AK_TARGET_POSITION = 7,\n  AK_TARGET_SCALE    = 8,\n  AK_TARGET_ROTATE   = 9,\n  AK_TARGET_QUAT     = 10,\n  AK_TARGET_WEIGHTS  = 11,\n  AK_TARGET_FLOAT    = 12,\n  AK_TARGET_VEC2     = 13,\n  AK_TARGET_VEC3     = 14,\n  AK_TARGET_VEC4     = 15,\n  AK_TARGET_COLOR    = 16,\n  AK_TARGET_BOOL     = 17\n} AkTargetPropertyType;\n\ntypedef enum AkInterpolationType {\n  AK_INTERPOLATION_UNKNOWN  = 0,\n  AK_INTERPOLATION_LINEAR   = 1,\n  AK_INTERPOLATION_BEZIER   = 2,\n  AK_INTERPOLATION_CARDINAL = 3,\n  AK_INTERPOLATION_HERMITE  = 4,\n  AK_INTERPOLATION_BSPLINE  = 5,\n  AK_INTERPOLATION_STEP     = 6,\n\n  AK_INTERPOLATION_MAXLEN   = 255\n} AkInterpolationType;\n\ntypedef struct AkAnimSampler {\n  AkOneWayIterBase      base;\n  AkInput              *input;\n\n  AkInput              *inputInput;\n  AkInput              *outputInput;\n  AkInput              *interpInput;\n  AkInput              *inTangentInput;\n  AkInput              *outTangentInput;\n\n  AkInterpolationType   uniInterpolation;\n  AkSamplerBehavior     pre;\n  AkSamplerBehavior     post;\n} AkAnimSampler;\n\ntypedef struct AkResolvedTarget {\n  void    *target;\n  uint32_t off;\n  bool     isPartial;\n} AkResolvedTarget;\n\ntypedef struct AkChannel {\n  struct AkChannel    *next;\n  const char          *target;\n  AkResolvedTarget    *resolvedTarget;\n  AkURL                source;\n  AkTargetPropertyType targetType;\n} AkChannel;\n\ntypedef struct AkAnimation {\n  AkOneWayIterBase    base;\n  struct AkAnimation *animation; /* subanimation */\n  AkAnimSampler      *sampler;\n  AkChannel          *channel;\n  const char         *name;\n  AkTree             *extra;\n  \n  /* TODO: WILL BE DELETED */\n  AkSource           *source;\n} AkAnimation;\n\n/*!\n * Per-frame transform sequence produced by ak_nodeBakeAnimation().\n * `matrices` is `count × 16` floats, column-major (cglm convention),\n * each block mapping a node-local point into its parent's space.\n * `times` is `count` floats, parallel to the matrix array.\n *\n * Free with ak_free(out) — the inner buffers were sub-allocated under\n * the struct and cascade in the AssetKit heap.\n */\ntypedef struct AkBakedAnimation {\n  float    *matrices;  /* count × 16 floats, column-major */\n  float    *times;     /* count floats                    */\n  uint32_t  count;\n} AkBakedAnimation;\n\nAK_INLINE\nbool\nak_channelTargetIsPartial(const AkChannel *ch) {\n  return ch->target && strchr(ch->target, '.') != NULL;\n}\n\n/**\n * returns NULL if no attribute (whole target animation),\n * otherwise pointer into ch->target after the '.'\n * result lifetime tied to ch->target — do NOT free\n */\nAK_INLINE\nconst char *\nak_channelTargetAttr(const AkChannel *ch) {\n  const char *dot;\n  if (!ch->target || !(dot = strchr(ch->target, '.'))) return NULL;\n  return dot + 1;\n}\n\nAK_INLINE\nAkResolvedTarget\nak_channelTarget(AkContext * __restrict ctx,\n                 AkChannel * __restrict ch) {\n  const char      *sidAttrib;\n  AkResolvedTarget resolved = {0};\n  uint32_t         attrOff;\n\n  /** glTF (and DAE post-fixup) provide a pre-resolved target. Honor it\n      first — the SID string in ch->target is then optional and used only\n      as a debug/lookup hint.\n\n      glTF example: target => \"translation\"\n        ch->resolvedTarget = AkResolvedTarget* with target = AkTranslate*\n        and off = 0 (glTF animates the whole transform element).\n\n      DAE example for morph weights: target => \"morph-weights(0)\"\n        dae_fixup_channel resolves the source-and-(idx) pattern at fixup\n        time and writes ch->resolvedTarget = AkResolvedTarget* with target\n        = AkInstanceMorph*, off = idx, isPartial = true. */\n  if (ch->resolvedTarget)\n    return *ch->resolvedTarget;\n\n  /** DAE SID path: \"node1/translate.Y\"\n      resolved.target = AkTranslate*, sidAttrib = \"Y\" */\n  if (ch->target) {\n    if ((resolved.target = ak_sid_resolve(ctx, ch->target, &sidAttrib))\n        && (attrOff = ak_sid_attr_offset(sidAttrib)) != UINT32_MAX) {\n      resolved.isPartial = sidAttrib != NULL;\n      resolved.off       = attrOff;\n    } else {\n      resolved.target    = NULL;\n    }\n  }\n\n  return resolved;\n}\n\n#define ak_inputBegin(INP, T) (*(T*)INP->data)\n#define ak_inputEnd(INP, T)   (*(T*)((char*)INP->data + INP->len - sizeof(T)))\n\n/*!\n * @brief Test whether two animations would write to any of the same\n *        animatable slot. Two channels conflict iff they resolve (via\n *        ak_channelTarget) to the same target pointer AND either at least\n *        one is a whole-target write, or they share the same partial slot\n *        offset.\n *\n *        Useful for runtime players that pick which animations may run in\n *        parallel — overlapping writes otherwise produce undefined ordering.\n *\n * @param ctx resolution context (used to evaluate SID-targeted channels)\n * @param a   first animation\n * @param b   second animation\n * @return    true iff any pair of channels conflicts\n */\nAK_EXPORT\nbool\nak_animationsConflict(AkContext   * __restrict ctx,\n                      AkAnimation * __restrict a,\n                      AkAnimation * __restrict b);\n\n/*!\n * @brief Build the maximal conflict-free set anchored at `primary`.\n *\n *        `primary` is always selected (it's the animation the caller wants\n *        to activate). Then each candidate is tested against everything\n *        already selected — added if it doesn't conflict with any of them.\n *\n *        First-fit greedy: candidate iteration order decides which side\n *        of a conflict wins. Pass candidates in the priority order you\n *        want (typically: doc order with `primary` excluded).\n *\n *        Use case: a UI like \"user clicked Animation 2 — what other\n *        animations can stay enabled in parallel?\"\n *\n * @param ctx              resolution context\n * @param primary          anchor animation (must be in result, may be NULL)\n * @param candidates       array of candidate AkAnimation* pointers\n * @param candidatesCount  length of candidates\n * @param outCompatible    pre-allocated buffer of at least\n *                         `candidatesCount + 1` AkAnimation* slots\n * @return count of selected animations (= written into outCompatible)\n */\nAK_EXPORT\nsize_t\nak_animationsCompatibleSet(AkContext         * __restrict ctx,\n                           AkAnimation       * __restrict primary,\n                           AkAnimation      ** __restrict candidates,\n                           size_t                         candidatesCount,\n                           AkAnimation      ** __restrict outCompatible);\n\n/*!\n * @brief Total number of animations across every animation library on the\n *        document. Useful for sizing buffers passed to the *FromDoc\n *        compatible-set helper.\n */\nAK_EXPORT\nsize_t\nak_animationsCount(struct AkDoc * __restrict doc);\n\n/*!\n * @brief Convenience over `ak_animationsCompatibleSet` that walks the\n *        document's animation libraries itself — so callers don't have to\n *        materialise a `candidates[]` array.\n *\n * @param ctx           resolution context\n * @param doc           the AssetKit document\n * @param primary       anchor animation (must be in result, may be NULL)\n * @param outCompatible pre-allocated buffer of at least\n *                      `ak_animationsCount(doc) + 1` slots\n * @return count of selected animations\n */\nAK_EXPORT\nsize_t\nak_animationsCompatibleSetFromDoc(AkContext     * __restrict ctx,\n                                  struct AkDoc  * __restrict doc,\n                                  AkAnimation   * __restrict primary,\n                                  AkAnimation  ** __restrict outCompatible);\n\n\n/*!\n * @brief Hint that the node's animation should be baked rather than\n *        driven via per-property channels.\n *\n *        Returns true when the node's transform chain holds 2+ rotate\n *        elements — the canonical case is a Maya joint with\n *        jointOrient{XYZ} + rotate{XYZ} (six <rotate> elements in one\n *        chain, only three Euler slots in any decomposed-property\n *        animation API: SCNNode.eulerAngles, three.js Object3D.rotation,\n *        Filament TransformManager rotation).\n *\n *        Renderers that animate via decomposed properties MUST bake\n *        these nodes — partial rotates clobber each other in the\n *        Euler slot. Renderers that compose joint world matrices on\n *        the CPU per frame (assetkit-opengl/gk pattern) don't need\n *        this and can keep their per-channel walk.\n */\nAK_EXPORT\nbool\nak_nodeNeedsBaking(struct AkNode * __restrict node);\n\n/*!\n * @brief Sample every animation channel that targets any AkObject in\n *        `node->transform` (translate / rotate / scale / matrix /\n *        skew / quat) on a shared time grid and emit a stream of 4×4\n *        local matrices.\n *\n *        Time grid is the union of the involved channels' keyframe\n *        times (no resampling — every original keyframe is preserved\n *        exactly; channels that lack a value at some t are linearly\n *        interpolated). STEP interpolation is honored; BEZIER /\n *        HERMITE fall back to LINEAR (the bake is keyframe-aligned,\n *        so engine-side interpolation can refine the curve).\n *\n *        Static AkObjects in the chain (those with no targeting\n *        channel) keep their authored values — bind pose is preserved\n *        between animated frames. The function snapshot/restores\n *        animated AkObject state so callers can keep using\n *        node->transform for bind-pose composition afterwards.\n *\n *        Output AkBakedAnimation is heap-allocated; caller frees with\n *        ak_free(out). Returns NULL when the node has no transform or\n *        no channel targets any element of its chain.\n */\nAK_EXPORT\nAkBakedAnimation*\nak_nodeBakeAnimation(struct AkDoc  * __restrict doc,\n                     struct AkNode * __restrict node);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_animation_h */\n"
  },
  {
    "path": "include/ak/assetkit.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_h\n#define assetkit_h\n\n#include <stdlib.h>\n#include <time.h>\n\n#include \"common.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct FList;\nstruct FListItem;\nstruct AkBuffer;\nstruct AkLibrary;\n\n/* End Core Value Types */\n\n#include \"core-types.h\"\n#include \"memory.h\"\n#include \"coord.h\"\n#include \"url.h\"\n#include \"type.h\"\n\ntypedef enum AkFileType {\n  AK_FILE_TYPE_AUTO      = 0,\n  AK_FILE_TYPE_COLLADA   = 1,\n  AK_FILE_TYPE_GLTF      = 2,\n  AK_FILE_TYPE_WAVEFRONT = 3,\n  AK_FILE_TYPE_STL       = 4,\n  AK_FILE_TYPE_PLY       = 5,\n  AK_FILE_TYPE_3MF       = 6\n} AkFileType;\n\ntypedef enum AkAltitudeMode {\n  AK_ALTITUDE_RELATIVETOGROUND = 0,\n  AK_ALTITUDE_ABSOLUTE         = 1\n} AkAltitudeMode;\n\ntypedef enum AkFace {\n  AK_FACE_POSITIVE_X = 1,\n  AK_FACE_NEGATIVE_X = 2,\n  AK_FACE_POSITIVE_Y = 3,\n  AK_FACE_NEGATIVE_Y = 4,\n  AK_FACE_POSITIVE_Z = 5,\n  AK_FACE_NEGATIVE_Z = 6\n} AkFace;\n\ntypedef enum AkChannelFormat {\n  AK_CHANNEL_FORMAT_RGB  = 1,\n  AK_CHANNEL_FORMAT_RGBA = 2,\n  AK_CHANNEL_FORMAT_RGBE = 3,\n  AK_CHANNEL_FORMAT_L    = 4,\n  AK_CHANNEL_FORMAT_LA   = 5,\n  AK_CHANNEL_FORMAT_D    = 6,\n  AK_CHANNEL_FORMAT_XYZ  = 7,\n  AK_CHANNEL_FORMAT_XYZW = 8\n} AkChannelFormat;\n\ntypedef enum AkRangeFormat {\n  AK_RANGE_FORMAT_SNORM = 1,\n  AK_RANGE_FORMAT_UNORM = 2,\n  AK_RANGE_FORMAT_SINT  = 3,\n  AK_RANGE_FORMAT_UINT  = 4,\n  AK_RANGE_FORMAT_FLOAT = 5\n} AkRangeFormat;\n\ntypedef enum AkPrecisionFormat {\n  AK_PRECISION_FORMAT_DEFAULT = 1,\n  AK_PRECISION_FORMAT_LOW     = 2,\n  AK_PRECISION_FORMAT_MID     = 3,\n  AK_PRECISION_FORMAT_HIGHT   = 4,\n  AK_PRECISION_FORMAT_MAX     = 5\n} AkPrecisionFormat;\n\ntypedef enum AkInputSemantic {\n  /* read semanticRaw */\n  AK_INPUT_OTHER           = 0,\n  AK_INPUT_BINORMAL        = 1,\n  AK_INPUT_COLOR           = 2,\n  AK_INPUT_CONTINUITY      = 3,\n  AK_INPUT_IMAGE           = 4,\n  AK_INPUT_INPUT           = 5,\n  AK_INPUT_IN_TANGENT      = 6,\n  AK_INPUT_INTERPOLATION   = 7,\n  AK_INPUT_INV_BIND_MATRIX = 8,\n  AK_INPUT_JOINT           = 9,\n  AK_INPUT_LINEAR_STEPS    = 10,\n  AK_INPUT_MORPH_TARGET    = 11,\n  AK_INPUT_MORPH_WEIGHT    = 12,\n  AK_INPUT_NORMAL          = 13,\n  AK_INPUT_OUTPUT          = 14,\n  AK_INPUT_OUT_TANGENT     = 15,\n  AK_INPUT_POSITION        = 16,\n  AK_INPUT_TANGENT         = 17,\n  AK_INPUT_TEXBINORMAL     = 18,\n  AK_INPUT_TEXCOORD        = 19,\n  AK_INPUT_TEXTANGENT      = 20,\n  AK_INPUT_UV              = 21,\n  AK_INPUT_WEIGHT          = 22\n} AkInputSemantic;\n\ntypedef enum AkCurveElementType {\n  AK_CURVE_LINE      = 1,\n  AK_CURVE_CIRCLE    = 2,\n  AK_CURVE_ELLIPSE   = 3,\n  AK_CURVE_PARABOLA  = 4,\n  AK_CURVE_HYPERBOLA = 5,\n  AK_CURVE_NURBS     = 6,\n} AkCurveElementType;\n\ntypedef enum AkSurfaceElementType {\n  AK_SURFACE_CONE          = 1,\n  AK_SURFACE_PLANE         = 2,\n  AK_SURFACE_CYLINDER      = 3,\n  AK_SURFACE_NURBS_SURFACE = 4,\n  AK_SURFACE_SPHERE        = 5,\n  AK_SURFACE_TORUS         = 6,\n  AK_SURFACE_SWEPT_SURFACE = 7\n} AkSurfaceElementType;\n\ntypedef enum AkInstanceType {\n  AK_INSTANCE_NODE       = 1,\n  AK_INSTANCE_CAMERA     = 2,\n  AK_INSTANCE_LIGHT      = 3,\n  AK_INSTANCE_GEOMETRY   = 4,\n  AK_INSTANCE_IMAGE      = 5,\n  AK_INSTANCE_CONTROLLER = 6,\n  AK_INSTANCE_EFFECT     = 7\n} AkInstanceType;\n\ntypedef struct AkValue {\n  void      *value;\n  AkTypeDesc type;\n} AkValue;\n\ntypedef struct AkTreeNodeAttr {\n  const char            *name;\n  char                  *val;\n  struct AkTreeNodeAttr *next;\n  struct AkTreeNodeAttr *prev;\n} AkTreeNodeAttr;\n\ntypedef struct AkTreeNode {\n  AkTreeNodeAttr    *attribs;\n  \n  const char        *name;\n  char              *val;\n\n  struct AkTreeNode *chld;\n  struct AkTreeNode *parent;\n  struct AkTreeNode *next;\n  struct AkTreeNode *prev;\n  \n  unsigned long      attrc;\n  unsigned long      chldc;\n} AkTreeNode;\n\ntypedef struct AkTreeNode AkTree;\n\n/*!\n * @brief Return optional metadata attached to an AssetKit object.\n *\n * COLLADA <extra> and preserved glTF extras/extensions are exposed as\n * AkTree. The returned tree is owned by the document heap.\n */\nAK_EXPORT\nAkTree*\nak_extra(void * __restrict obj);\n\n/*!\n * @brief Attach optional metadata to an AssetKit object.\n */\nAK_EXPORT\nvoid\nak_extra_set(void   * __restrict obj, AkTree * __restrict extra);\n\n#include \"source.h\"\n\ntypedef struct AkUnit {\n  const char * name;\n  double       dist;\n} AkUnit;\n\ntypedef struct AkColorRGBA {\n  AkFloat R;\n  AkFloat G;\n  AkFloat B;\n  AkFloat A;\n} AkColorRGBA;\n\ntypedef union AkColor {\n  AK_ALIGN(16) AkColorRGBA rgba;\n  AK_ALIGN(16) AkFloat4    vec;\n} AkColor;\n\nAK_INLINE\nbool\nak_colorLessThanOne(AkColor color) {\n  return color.rgba.R < 0.999\n      || color.rgba.G < 0.999\n      || color.rgba.B < 0.999\n      || color.rgba.A < 0.999\n  ;\n}\n\nAK_INLINE \nfloat\nak_sRGB_linearf(float channel) {\n  if (channel <= 0.04045) {\n    return channel / 12.92;\n  } else {\n    return powf((channel + 0.055) / 1.055, 2.4);\n  }\n}\n\nAK_INLINE\nfloat\nak_linear_sRGBf(float channel) {\n  if (channel <= 0.0031308f) {\n    return channel * 12.92f;\n  } else {\n    return 1.055f * powf(channel, 1.0f / 2.4f) - 0.055f;\n  }\n}\n\nAK_INLINE\nvoid\nak_sRGB_linear(AkColor * __restrict color) {\n  color->rgba.R = ak_sRGB_linearf(color->rgba.R);\n  color->rgba.G = ak_sRGB_linearf(color->rgba.G);\n  color->rgba.B = ak_sRGB_linearf(color->rgba.B);\n}\n\nAK_INLINE \nvoid\nak_linear_sRGB(AkColor * __restrict color) {\n  color->rgba.R = ak_linear_sRGBf(color->rgba.R);\n  color->rgba.G = ak_linear_sRGBf(color->rgba.G);\n  color->rgba.B = ak_linear_sRGBf(color->rgba.B);\n}\n\ntypedef struct AkContributor {\n  const char * author;\n  const char * authorEmail;\n  const char * authorWebsite;\n  const char * authoringTool;\n  const char * comments;\n  const char * copyright;\n  const char * sourceData;\n\n  struct AkContributor * next;\n} AkContributor;\n\ntypedef struct AkAltitude {\n  AkAltitudeMode mode;\n  double         val;\n} AkAltitude;\n\ntypedef struct AkGeoLoc {\n  double     lng;\n  double     lat;\n  AkAltitude alt;\n} AkGeoLoc;\n\ntypedef struct AkCoverage {\n  AkGeoLoc geoLoc;\n} AkCoverage;\n\ntypedef struct AkAssetInf {\n  AkCoordSys    *coordSys;\n  AkUnit        *unit;\n  AkContributor *contributor;\n  AkCoverage    *coverage;\n  const char    *subject;\n  const char    *title;\n  const char    *keywords;\n  const char    *revision;\n  AkTree        *extra;\n  time_t         created;\n  time_t         modified;\n} AkAssetInf;\n\ntypedef struct AkDocInf {\n  AkAssetInf   base;\n  const char  *dir;\n  const char  *name;\n  size_t       dirlen;\n  AkFileType   ftype;\n  bool         flipImage;\n} AkDocInf;\n\ntypedef struct AkTechnique {\n  const char * profile;\n\n  /**\n   * @brief\n   * COLLADA Specs 1.5:\n   * This XML Schema namespace attribute identifies an additional schema\n   * to use for validating the content of this instance document. Optional.\n   */\n  const char * xmlns;\n  AkTree     * chld;\n\n  struct AkTechnique * next;\n} AkTechnique;\n\n/* FX */\n/* Effects */\n/*\n * base type of param\n */\ntypedef struct AkParam {\n  const char     *ref;\n  struct AkParam *prev;\n  struct AkParam *next;\n} AkParam;\n\ntypedef struct AkHexData {\n  const char *format;\n  const char *hexval; /* hex value    */\n  void       *data;   /* binary value */\n} AkHexData;\n\ntypedef struct AkInitFrom {\n  struct AkInitFrom *next;\n  const char        *ref;\n  const char        *resolvedFullPath;\n  AkHexData         *hex;\n  struct AkBuffer   *buff;\n  const char        *buffMime;\n  AkFace             face;\n  AkUInt             mipIndex;\n  AkUInt             depth;\n  AkInt              arrayIndex;\n  AkBool             mipsGenerate;\n} AkInitFrom;\n\nstruct AkNode;\ntypedef struct AkInstanceBase {\n  /* const char * sid; */\n  AkURL                  url;\n  AkInstanceType         type;\n  void                  *object;\n  const char            *name;\n  AkTree                *extra;\n  struct AkNode         *node;\n  struct AkInstanceBase *prev;\n  struct AkInstanceBase *next;\n} AkInstanceBase;\n\ntypedef struct AkEvaluateTarget {\n  AkParam        *param;\n  AkInstanceBase *instanceImage;\n  unsigned long   index;\n  unsigned long   slice;\n  unsigned long   mip;\n  AkFace          face;\n} AkEvaluateTarget;\n\n#include \"profile.h\"\n\nstruct AkNewParam;\ntypedef struct AkEffect {\n  /* const char * id; */\n  const char      *name;\n  struct AkNewParam  *newparam;\n  AkProfile       *profile;\n  AkTree          *extra;\n  struct AkEffect *next;\n\n  /* effect specific options, override global options */\n  AkProfileType    bestProfile;\n} AkEffect;\n\ntypedef struct AkInstanceEffect {\n  AkInstanceBase   base;\n  AkTechniqueHint *techniqueHint;\n} AkInstanceEffect;\n\ntypedef struct AkMaterial {\n  /* const char * id; */\n  AkOneWayIterBase   base;\n  const char        *name;\n  AkInstanceEffect  *effect;\n  AkTree            *extra;\n} AkMaterial;\n\nstruct AkAccessor;\n\ntypedef struct AkInput {\n  const char        *semanticRaw;\n  struct AkInput    *next;\n  struct AkAccessor *accessor;\n  uint32_t           index; /* TEXCOORD0, TEXCOORD1... */\n  bool               isIndexed;\n  AkInputSemantic    semantic;\n  uint32_t           offset;\n  uint32_t           set;\n  \n  /* TODO: WILL BE DELETED */\n//  AkURL              source;\n} AkInput;\n\nstruct AkInstanceMaterial;\ntypedef struct AkBindMaterial {\n  AkParam                   *param;\n  struct AkInstanceMaterial *tcommon;\n  AkTechnique               *technique;\n  AkTree                    *extra;\n} AkBindMaterial;\n\ntypedef struct AkInstanceGeometry {\n  AkInstanceBase          base;\n\n  AkBindMaterial         *bindMaterial;\n  struct AkInstanceMorph *morpher;\n  struct AkInstanceSkin  *skinner;\n} AkInstanceGeometry;\n\ntypedef struct AkInstanceNode {\n  AkInstanceBase base;\n  const char    *proxy;\n} AkInstanceNode;\n\n/*\n * TODO: separate all instances to individual nodes?\n */\nstruct AkMatrix;\nstruct AkBoundingBox;\nstruct AkTransform;\n\ntypedef struct AkBind {\n  const char    * semantic;\n  const char    * target;\n  struct AkBind * next;\n} AkBind;\n\ntypedef struct AkBindVertexInput {\n  struct AkBindVertexInput *next;\n  const char               *semantic;\n  const char               *inputSemantic;\n  AkUInt                    inputSet;\n} AkBindVertexInput;\n\ntypedef struct AkInstanceMaterial {\n  AkInstanceBase       base;\n  const char          *symbol;\n  AkTechniqueOverride *techniqueOverride;\n  AkBind              *bind;\n  AkBindVertexInput   *bindVertexInput;\n} AkInstanceMaterial;\n\ntypedef struct AkRender {\n  /* const char * sid; */\n\n  const char     * name;\n  const char     * cameraNode;\n  AkStringArrayL * layer;\n  AkInstanceMaterial * instanceMaterial;\n  AkTree         * extra;\n\n  struct AkRender * next;\n} AkRender;\n\ntypedef struct AkEvaluateScene {\n  /* const char * id; */\n  /* const char * sid; */\n\n  const char * name;\n  AkRender   * render;\n  AkTree     * extra;\n  AkBool       enable;\n\n  struct AkEvaluateScene * next;\n} AkEvaluateScene;\n\nstruct AkInstanceList;\n\ntypedef struct AkVisualScene {\n  /* const char * id; */\n  AkOneWayIterBase       base;\n  const char            *name;\n  struct AkNode         *node;\n  struct AkNode         *firstCamNode; /* first found camera       */\n  struct AkInstanceList *cameras;      /* all cameras inside scene */\n  struct AkInstanceList *lights;       /* all lights inside scene  */\n  AkEvaluateScene       *evaluateScene;\n  struct AkBoundingBox  *bbox;\n  AkTree                *extra;\n} AkVisualScene;\n\ntypedef struct AkScene {\n  /*\n   TODO:\n      instance_physics_scene\n      instance_kinematics_scene\n   */\n  AkInstanceBase *visualScene;\n  AkTree * extra;\n} AkScene;\n\nstruct AkMorph;\nstruct AkSkin;\n\ntypedef struct AkLibraries {\n  struct AkLibrary *cameras;\n  struct AkLibrary   *lights;\n  struct AkLibrary   *effects;\n  struct AkLibrary   *libimages;\n  struct AkLibrary   *materials;\n  struct AkLibrary   *geometries;\n  struct AkLibrary   *controllers;\n  struct AkLibrary   *visualScenes;\n  struct AkLibrary   *nodes;\n  struct AkLibrary   *animations;\n    \n  struct FListItem   *buffers;\n  struct FListItem   *accessors;\n  struct FListItem   *textures;\n  struct FListItem   *samplers;\n  struct FListItem   *images;\n  struct AkMorph     *morphs;\n  struct AkSkin      *skins;\n} AkLibraries;\n\ntypedef const char* (*AkFetchFromURLHandler)(const char * __restrict url);\n\ntypedef struct AkDoc {\n  AkDocInf   *inf;\n  AkCoordSys *coordSys;\n  AkUnit     *unit;\n  AkTree     *extra;\n  void       *reserved;\n  void       *userData;\n  float       loadMillis;\n  AkLibraries lib;\n  AkScene     scene;\n\n  /* KHR_materials_variants: document-level variant names. */\n  struct AkMaterialVariant *materialVariants;\n  uint32_t                  materialVariantCount;\n} AkDoc;\n\n#include \"context.h\"\n#include \"geom.h\"\n#include \"image.h\"\n#include \"string.h\"\n#include \"coord-util.h\"\n#include \"library.h\"\n#include \"instance.h\"\n#include \"cam.h\"\n#include \"transform.h\"\n#include \"sid.h\"\n#include \"light.h\"\n#include \"node.h\"\n#include \"texture.h\"\n#include \"animation.h\"\n#include \"controller.h\"\n#include \"gsplat.h\"\n\nAK_EXPORT\nAkResult\nak_load(AkDoc     ** __restrict dest,\n        const char * __restrict url,\n        .../* options */);\n\nAK_EXPORT\nvoid *\nak_getId(void * __restrict objptr);\n\nAK_EXPORT\nAkResult\nak_setId(void       * __restrict objptr,\n         const char * __restrict objectId);\n\nAK_EXPORT\nAkResult\nak_moveId(void * __restrict objptrOld,\n          void * __restrict objptrNew);\n\nAK_EXPORT\nvoid *\nak_getObjectById(AkDoc      * __restrict doc,\n                 const char * __restrict objectId);\n\nAK_EXPORT\nvoid *\nak_getObjectByUrl(AkURL * __restrict url);\n\nconst char*\nak_getFile(const char *url);\n\nchar*\nak_getFileFrom(AkDoc *doc, const char *url);\n\nAK_EXPORT\nconst char *\nak_generatId(AkDoc      * __restrict doc,\n             void       * __restrict parentmem,\n             const char * __restrict prefix);\n\nAK_EXPORT\nvoid*\nak_getAssetInfo(void * __restrict obj,\n                size_t itemOffset);\n\n/* same as: ak_getAssetInfo(obj, offsetof(AkAssetInf, coordSys)) */\nAK_EXPORT\nAkCoordSys*\nak_getCoordSys(void * __restrict obj);\n\nAK_EXPORT\nbool\nak_hasCoordSys(void * __restrict obj);\n\nAK_EXPORT\nvoid\nak_retainURL(void * __restrict obj, AkURL * __restrict url);\n\nAK_EXPORT\nvoid\nak_releaseURL(void * __restrict obj, AkURL * __restrict url);\n\nAK_EXPORT\nvoid\nak_setFetchFromURLHandler(AkFetchFromURLHandler handler);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_h */\n"
  },
  {
    "path": "include/ak/bbox.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_bbox_h\n#define assetkit_bbox_h\n\n#include \"common.h\"\n#include <stdbool.h>\n\nstruct AkMesh;\nstruct AkMeshPrimitive;\nstruct AkGeometry;\nstruct AkScene;\nstruct AkMeshPrimitive;\n\ntypedef struct AkBoundingBox {\n  float min[3];\n  float max[3];\n  bool  isvalid;\n} AkBoundingBox;\n\n/*!\n * @brief calc bbox for whole scene, this calc scene bbox with transformations\n *        of geom nodes\n *\n * @param scene visual scene\n */\nvoid\nak_bbox_scene(struct AkVisualScene * __restrict scene);\n\n/*!\n * @brief calc bbox for whole geometry\n *        this will affect scene bbox\n *\n * @param geom  geometry\n */\nvoid\nak_bbox_geom(struct AkGeometry * __restrict geom);\n\n/*!\n * @brief calc bbox for whole mesh\n *        this will affect geom bbox\n *\n * @param mesh  mesh\n */\nvoid\nak_bbox_mesh(struct AkMesh * __restrict mesh);\n\n/*!\n * @brief calc bbox for mesh primitive\n *        this will affect geom bbox and mesh bbox\n *\n * @param prim  primitive\n */\nvoid\nak_bbox_mesh_prim(struct AkMeshPrimitive * __restrict prim);\n\n/*!\n * @brief get center of bbox\n *\n * @param[in]  bbox   bbox\n * @param[out] center center of bbox\n */\nvoid\nak_bbox_center(AkBoundingBox * __restrict bbox,\n               float center[3]);\n\n/*!\n * @brief returns radius of sphere which is surround bbox square\n *\n * @param bbox bbox\n *\n * @return radius (r) of outer sphere\n */\nfloat\nak_bbox_radius(AkBoundingBox * __restrict bbox);\n\n#endif /* assetkit_bbox_h */\n"
  },
  {
    "path": "include/ak/cam.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_cam_h\n#define assetkit_cam_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n\nstruct AkLibraryItemBase;\n\ntypedef enum AkProjectionType {\n  AK_PROJECTION_PERSPECTIVE  = 0, /* default */\n  AK_PROJECTION_ORTHOGRAPHIC = 1,\n  AK_PROJECTION_OTHER        = 2\n} AkProjectionType;\n\ntypedef struct AkProjection {\n  AkProjectionType type;\n  uint32_t         tag;\n} AkProjection;\n\ntypedef struct AkPerspective {\n  AkProjection base;\n  float        xfov;\n  float        yfov;\n  float        aspectRatio;\n  float        znear;\n  float        zfar;\n} AkPerspective;\n\ntypedef struct AkOrthographic {\n  AkProjection base;\n  float        xmag;\n  float        ymag;\n  float        aspectRatio;\n  float        znear;\n  float        zfar;\n} AkOrthographic;\n\ntypedef struct AkOptics {\n  AkProjection *tcommon;\n  AkTechnique  *technique;\n} AkOptics;\n\ntypedef struct AkImager {\n  AkTechnique *technique;\n  AkTree      *extra;\n} AkImager;\n\ntypedef struct AkCamera {\n  /* const char * id; */\n  AkOneWayIterBase base;\n  const char      *name;\n  AkOptics        *optics;\n  AkImager        *imager;\n  AkTree          *extra;\n} AkCamera;\n\n/*!\n * @brief Top camera in scene if available\n *\n * pass NULL which parameter is not wanted and reduce overhead of calc these \n * params, only pass param[s] which you need\n *\n * @warning the returned projection matrix's aspect ratio is same as exported\n *\n * @param[in]  doc        document (required)\n * @param[out] camera     found camera (optional)\n * @param[out] matrix     transform matrix (optional) of camera in world\n * @param[out] projMatrix proj matrix (optional)\n *\n * @return AK_OK if camera found else AK_EFOUND\n */\nAK_EXPORT\nAkResult\nak_firstCamera(AkDoc     * __restrict doc,\n               AkCamera ** camera,\n               float     * matrix,\n               float     * projMatrix);\n\nAK_EXPORT\nconst AkCamera*\nak_defaultCamera(void * __restrict memparent);\n\n/*!\n * @brief Allocate a fresh perspective camera and register it in the\n *        document's cameras library.\n *\n * Wires up AkCamera + AkOptics + AkPerspective in one shot using the\n * heap that owns @p doc. The returned pointer is owned by that heap;\n * its lifetime is tied to @p memparent (or the document if memparent\n * is NULL). After this call the camera is queryable via the cameras\n * library — callers don't need to do their own lib-list management.\n *\n * The camera is *not* attached to any scene node here — use\n * ak_nodeAttachCamera() for that.\n *\n * @param[in]  doc        document the camera will live in (required)\n * @param[in]  memparent  heap parent for ownership (NULL → doc)\n * @param[in]  yfov       vertical field of view in radians\n * @param[in]  aspect     aspect ratio (width / height); 0 → unset\n * @param[in]  znear      near clipping distance\n * @param[in]  zfar       far clipping distance\n *\n * @return Newly allocated AkCamera, or NULL on allocation failure.\n */\nAK_EXPORT\nAkCamera *\nak_camMakePerspective(AkDoc * __restrict doc,\n                      void  * __restrict memparent,\n                      float yfov,\n                      float aspect,\n                      float znear,\n                      float zfar);\n\n/*!\n * @brief Allocate a fresh orthographic camera and register it in the\n *        document's cameras library.\n *\n * Mirrors ak_camMakePerspective(): allocates AkCamera + AkOptics +\n * AkOrthographic, wires them, and adds the camera to doc->lib.cameras.\n *\n * @param[in]  doc        document the camera will live in (required)\n * @param[in]  memparent  heap parent for ownership (NULL → doc)\n * @param[in]  xmag       half-width of the view volume\n * @param[in]  ymag       half-height of the view volume\n * @param[in]  aspect     aspect ratio (width / height); 0 → unset\n * @param[in]  znear      near clipping distance\n * @param[in]  zfar       far clipping distance\n *\n * @return Newly allocated AkCamera, or NULL on allocation failure.\n */\nAK_EXPORT\nAkCamera *\nak_camMakeOrthographic(AkDoc * __restrict doc,\n                       void  * __restrict memparent,\n                       float xmag,\n                       float ymag,\n                       float aspect,\n                       float znear,\n                       float zfar);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_cam_h */\n"
  },
  {
    "path": "include/ak/common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_common_h\n#define assetkit_common_h\n\n/* since C99 or compiler ext */\n#include <stdint.h>\n#include <stddef.h>\n#include <float.h>\n#include <stdbool.h>\n#include <errno.h>\n#include <math.h>\n\n#ifdef DEBUG\n#  include <assert.h>\n#  include <stdio.h>\n#endif\n\n#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)\n#  define AK_WINAPI\n#endif\n\n#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__MINGW64__)\n#  ifdef AK_STATIC\n#    define AK_EXPORT\n#  elif defined(AK_EXPORTS)\n#    define AK_EXPORT __declspec(dllexport)\n#  else\n#    define AK_EXPORT __declspec(dllimport)\n#  endif\n#  define AK_HIDE\n#else\n#  define AK_EXPORT   __attribute__((visibility(\"default\")))\n#  define AK_HIDE     __attribute__((visibility(\"hidden\")))\n#endif\n\n#if defined(_MSC_VER)\n#  define AK_INLINE   __forceinline\n#  define AK_ALIGN(X) __declspec(align(X))\n#else\n#  define AK_ALIGN(X) __attribute((aligned(X)))\n#  define AK_INLINE   static inline __attribute((always_inline))\n#endif\n\n\n#ifndef __has_builtin\n#  define __has_builtin(x) 0\n#endif\n\n#define AK_ARRAY_LEN(ARR) sizeof(ARR) / sizeof(ARR[0])\n\n#define AK_APPEND_FLINK(SRC,LAST,ITEM)                                        \\\n  do {                                                                        \\\n    if (LAST)                                                                 \\\n      LAST->next = ITEM;                                                      \\\n    else                                                                      \\\n      SRC = ITEM;                                                             \\\n    LAST = ITEM;                                                              \\\n  } while (0)\n\ntypedef int32_t AkEnum;\n\ntypedef enum AkResult {\n  AK_NOOP     = 1,     /* no operation needed */\n  AK_OK       = 0,\n  AK_ERR      = -1,    /* UKNOWN ERR */\n  AK_ETCOMMON = -2,    /* no tcommon found */\n  AK_EFOUND   = -1000,\n  AK_ENOMEM   = -ENOMEM,\n  AK_EPERM    = -EPERM,\n  AK_EBADF    = -EBADF, /* docoumnt couldn't parsed / loaded */\n  AK_EINVAL   = -EINVAL\n} AkResult;\n\ntypedef struct AkOneWayIterBase {\n  struct AkOneWayIterBase *next;\n} AkOneWayIterBase;\n\ntypedef struct AkTwoWayIterBase {\n  struct AkTwoWayIterBase *next;\n  struct AkTwoWayIterBase *prev;\n} AkTwoWayIterBase;\n\n#define AK_PATH_FROM_DOC(DOC, DEST, FILE_NAME)                                \\\n  do {                                                                        \\\n    size_t dirLength, newPathLength;                                          \\\n                                                                              \\\n    dirLength     = strlen(DOC->inf->dir);                                    \\\n    newPathLength = dirLength + strlen(FILE_NAME) + 1;                        \\\n                                                                              \\\n    DEST = alloca(newPathLength + 1);                                         \\\n    strcpy(DEST, DOC->inf->dir);                                              \\\n    if (DOC->inf->dir[dirLength - 1] != '/' && FILE_NAME[0] != '/') {         \\\n      strcat(DEST, \"/\");                                                      \\\n    }                                                                         \\\n    strcat(DEST, FILE_NAME);                                                  \\\n  } while (0)\n\n#endif /* assetkit_common_h */\n"
  },
  {
    "path": "include/ak/context.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_context_h\n#define assetkit_context_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n#include \"map.h\"\n#include \"util.h\"\n\ntypedef struct AkContext {\n  AkDoc              *doc;\n  AkTechniqueHint    *techniqueHint;\n  AkInstanceMaterial *instanceMaterial;\n  //  AkMap              *bindVertexInput;\n} AkContext;\n\nAK_INLINE AkContext AkContextZeroed(void) { return (AkContext){0}; }\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_context_h */\n"
  },
  {
    "path": "include/ak/controller.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_controller_h\n#define assetkit_controller_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n#include \"geom.h\"\n\nstruct AkNode;\nstruct FListItem;\n\ntypedef enum AkMorphMethod {\n  /* Weights of blend shapes normalized to 1 (or 100%) */\n  AK_MORPH_METHOD_NORMALIZED = 1,  \n\n  /* Blend shapes defined as a difference from the base shape */\n  AK_MORPH_METHOD_RELATIVE   = 2,  \n\n  /* Alias for RELATIVE, treat additive as relative in this context */\n  AK_MORPH_METHOD_ADDITIVE   = AK_MORPH_METHOD_RELATIVE,\n\n  /* Each blend shape applied fully on top of the previous one */\n  AK_MORPH_METHOD_ABSOLUTE   = 3\n} AkMorphMethod;\n\ntypedef struct AkBoneWeight {\n  uint32_t joint;\n  float    weight;\n} AkBoneWeight;\n\ntypedef struct AkBoneWeights {\n  uint32_t     *counts;     /* joint count per vertex                     */\n  size_t       *indexes;    /* offset of weight at buffer by index        */\n  AkBoneWeight *weights;\n  size_t        nWeights;   /* cache: count of weights                    */\n  size_t        nVertex;    /* cache: count of pJointsCount/pWeightsIndex */\n} AkBoneWeights;\n\n/**\n * Skin controller — vertex skinning data.\n *\n * Per-vertex bone data storage differs by source format:\n *   - DAE: variable-count CSR layout in `weights[]` (filled by\n *     dae_fixup_ctlr in authored primitive order).\n *   - glTF: raw vec4 JOINTS_n / WEIGHTS_n accessors stay in `prim->input`\n *     (no aggregation — the format is already GPU-ready).\n *\n * Bridges should call ak_skinFillWeights() rather than touching `weights[]`\n * or `prim->input` directly. The helper hides this format divergence and\n * yields fixed-N (typically 4) flat buffers ready for upload.\n *\n * Default joints (`joints[]`) are populated for glTF; DAE leaves it NULL\n * and resolves joints per-instance via AkInstanceSkin.overrideJoints (DAE\n * lets the same skin bind to different skeletons per instance).\n *\n * `skeleton` is the closest common ancestor of joints, used by Apple\n * SCNSkinner.skeleton (and similar engines) as the coordinate-space\n * reference for bone lookups. Optional — when NULL, callers fall back\n * to joints[0] (typically the root joint by convention). glTF: filled\n * from the optional `skin.skeleton` JSON hint. DAE: filled from\n * <instance_controller>'s first <skeleton> URL when fixing the\n * instance.\n */\ntypedef struct AkSkin {\n  AkOneWayIterBase base;\n  AkFloat4x4      *invBindPoses;\n  struct AkNode  **joints;   /* default joints (glTF; NULL for DAE)        */\n  AkBoneWeights  **weights;  /* per primitive (DAE only; NULL for glTF)    */\n  struct AkNode   *skeleton; /* common ancestor; NULL if not authored      */\n  void            *inspectResult; /* private cache for raw accessor pairs   */\n  size_t           nJoints;  /* cache: joint count                         */\n  uint32_t         nPrims;   /* cache: primitive count                     */\n  uint32_t         nMaxJoints;\n  AkFloat4x4       bindShapeMatrix;\n} AkSkin;\n\ntypedef enum AkMorphableType {\n  AK_MORPHABLE_GEOMETRY, /* per-target geometry, must be same as base  */\n  AK_MORPHABLE_MORPHABLE /* morph inputs if no geometry object is used */\n} AkMorphableType;\n\n/**\n * @brief input/attribute layout in shader orr in interleaved buffer\n * \n *   currently two layouts are supported: \n *     ------------------------------------------------------------------------\n *     P1 P2 P3    N1 N2 N3    T01 T02 T03 ...\n *     P1 N1 T01   P2 N2 T02   P3  N3  T03 ... (natural layout)\n * \n *  IMPORTANT: in natural layout, input orders may not same as baseShape\n *             if you need same order as baseShape, use P1P2N1N2 layout\n *             or create an issue to bring this feature to here which is in TODO.\n */\ntypedef enum AkMorphInterleaveLayout {\n  AK_MORPH_UNKNOWN  = 0,\n  AK_MORPH_P1P2N1N2 = 1, /* each target's inputs are groupped by input type */\n  AK_MORPH_NATURAL  = 3  /* P1N1 P2N2 but input orders are natural as target */\n} AkMorphInterleaveLayout;\n\n// typedef struct AkSparseMorphInfo {\n//   uint32_t *affectedVertices;  /* indices of vertices that change            */\n//   uint32_t  nAffectedVertices;\n//   float     sparsityRatio;     /* 0.0-1.0, useful for optimization decisions */\n// } AkSparseMorphInfo;\n\n/* per-target inputs to morph */\ntypedef struct AkMorphable {\n  struct AkMorphable  *next;\n  AkInput             *input;  \n  uint32_t             inputCount;\n} AkMorphable;\n\ntypedef struct AkMorphPreset {\n  const char   *name;      /* \"neutral\", \"smile_max\", ... */\n  AkFloatArray *weights;   /* length = morph->targetCount */\n} AkMorphPreset;\n\ntypedef struct AkMorphTarget {\n  struct AkMorphTarget *next;\n  AkObject             *target;         /* AkGeometry or AkMorphable to morph */\n  uint32_t              primitiveCount; /* number of mesh primitives to morph */\n} AkMorphTarget;\n\ntypedef struct AkMorphInspectInput {\n  struct AkMorphInspectInput *next;\n  AkInput                    *input;\n  uint32_t                    intrOffset;\n  union {\n    bool                      inBaseMesh;\n    bool                      inTarget;\n  };\n} AkMorphInspectInput;\n\ntypedef struct AkMorphInspectMorphable {\n  struct AkMorphInspectMorphable *next;\n  AkMorphInspectInput            *input;\n  AkMorphInspectInput            *lastInput;\n  uint32_t                        inputsCount;\n  float                           weight;\n\n  /* Per-primitive slice inside a target view. Multi-primitive meshes can\n     have different vertex counts and strides per primitive. */\n  uint32_t                        vertexCount;\n  uint32_t                        stridePerVertex;\n  size_t                          bufferOffset;\n  size_t                          bufferSize;\n} AkMorphInspectMorphable;\n\ntypedef struct AkMorphInspectTargetView {\n  struct AkMorphInspectTargetView *next;\n  AkMorphInspectMorphable         *morphable;\n  uint32_t                         nTargets;\n  /* Total byte size of this target slice, summed across all\n     per-primitive morphables. */\n  size_t                           interleaveBufferSize;\n} AkMorphInspectTargetView;\n\n/*\n  AkMorphInspectView\n         o\n         |\n         o -> AkMorphInspectTargetView ( like Mesh )\n                         o\n                         | \n                         o ->  AkMorphInspectMorphable 1 ( like Mesh Primitive )\n                                         o\n                                         -> AkMorphInspectInput 1\n                                         -> AkMorphInspectInput 2\n                                         -> AkMorphInspectInput 3\n                         o ->  AkMorphInspectMorphable 2\n                         o ->  AkMorphInspectMorphable 3\n        o -> AkMorphInspectTargetView\n                         o\n                         | \n                         o ->  AkMorphInspectMorphable 1\n                         o ->  AkMorphInspectMorphable 2\n                         o ->  AkMorphInspectMorphable 3\n*/\ntypedef struct AkMorphInspectView {\n  /* first one is baseShape if includeBaseShape param is set to 'true' */\n  AkMorphInspectTargetView *base;\n  AkMorphInspectTargetView *targets;\n  AkFloatArray             *initialWeights;\n  uint32_t                  nTargets;\n  size_t                    interleaveTotalBufferSize;\n  bool                      includeBaseShape;\n  bool                      ignoreUncommonInputs;\n  AkMorphInterleaveLayout   layout;\n} AkMorphInspectView;\n\ntypedef struct AkMorph {\n  AkOneWayIterBase    base;\n  AkMorphTarget      *target;\n  AkMorphInspectView *inspectResult;\n  AkFloatArray       *defaultWeights; /* this overrides mesh.weights        */\n  const char        **targetNames;    /* optional, length = targetCount     */\n  AkMorphPreset      *presets;        /* optional named weight sets         */\n  AkMorphMethod       method;\n  uint32_t            targetCount;\n  uint32_t            presetCount;\n} AkMorph;\n\ntypedef bool\n(*AkMorphProgressFn)(AkMorph * __restrict morph,\n                     uint32_t             targetIndex,\n                     uint32_t             targetCount,\n                     void   * __restrict userdata);\n\n// TODO: multi-morph-per-mesh just thought loudly ?\n// typedef struct AkPrimitiveMorph {\n//   AkOneWayIterBase    base;\n//   AkMorphTarget      *target;\n//   AkMorphInspectView *inspectResult;\n//   AkFloatArray       *weights; /* default weights or NULL to zero */\n//   AkMorphMethod       method;\n//   uint32_t            targetCount; \n// } AkPrimitiveMorph;\n// \n// typedef struct AkMeshMorph {\n//   AkOneWayIterBase  base;\n//   AkPrimitiveMorph *morph;\n//   float             weight;\n// } AkMeshMorph;\n\ntypedef struct AkInstanceMorph {\n  AkMorph      *morph;\n  AkFloatArray *overrideWeights;  /* override morph.weights and mesh.weight or NULL */\n} AkInstanceMorph;\n\ntypedef struct AkInstanceSkin {\n  AkSkin         *skin;\n  struct AkNode **overrideJoints; /* override default joints or NULL  */\n} AkInstanceSkin;\n\n/*!\n * @brief format-agnostic per-vertex bone-data extraction with INTERLEAVED\n *        output, suitable for a single GPU vertex buffer (OpenGL/Metal/\n *        Vulkan typical layout).\n *\n *        Per-vertex layout in the output buffer:\n *\n *          | JointIDs[maxJoint] (uint16) | Weights[maxJoint] (float) |\n *\n *        Matches a shader vertex input like:\n *\n *           in uvec4 JOINTS;  (backed by uint16 vertex input)\n *           in vec4  WEIGHTS;\n *\n *        Quality is identical to ak_skinFillWeights() — same format-\n *        agnostic core (DAE CSR + glTF accessors), same top-N selection\n *        when authored joint count exceeds maxJoint, same renormalize so\n *        weights sum to 1. Only the output packing differs (interleaved\n *        single buffer vs. separate idx/wgt arrays).\n *\n *        For the SceneKit/RealityKit path use ak_skinFillWeights()\n *        instead — Apple's APIs require separate boneIndices and\n *        boneWeights SCNGeometrySources.\n *\n *        Layout: per vertex `[J0..J(N-1) (uint16) W0..W(N-1) (float)]`.\n *\n * @param skin      AkSkin (for both DAE-CSR and glTF-accessor inputs)\n * @param prim      mesh primitive being skinned (vertex order source)\n * @param primIdx   primitive index fallback; prim pointer is authoritative\n * @param maxJoint  storage slots per vertex (typically 4)\n * @param buff      destination buffer; if *buff is NULL one is alloc'd\n *                  with ak_calloc(NULL, ...) and stored back into *buff\n *                  (caller frees with ak_free)\n * @return  total bytes written, or 0 on failure\n */\nAK_EXPORT\nsize_t\nak_skinInterleave(AkSkin          * __restrict skin,\n                  AkMeshPrimitive * __restrict prim,\n                  uint32_t                     primIdx,\n                  uint32_t                     maxJoint,\n                  void           ** __restrict buff);\n\n/*!\n * @brief format-agnostic per-vertex bone-data extraction for one primitive.\n *\n *        Fills caller-provided fixed-N flat buffers (joint indices + weights)\n *        regardless of the asset's source format:\n *\n *          - DAE  primitives use the CSR layout in skin->weights[];\n *                 variable joint count per vertex -> top-N selected by weight,\n *                 zero-padded if count<N, normalized so weights sum to 1.\n *          - glTF primitives keep JOINTS_n / WEIGHTS_n sets as raw accessors\n *                 in prim->input; all sets are merged, top-N is selected, and\n *                 UBYTE/USHORT joint indices are written as uint16_t.\n *\n *        Bridges should call this rather than reading skin->weights[] or\n *        prim->input directly — the dual storage is an implementation\n *        detail of the parsers.\n *\n * @param[in]  skin        skin owning the bone data\n * @param[in]  prim        mesh primitive being skinned\n * @param[in]  primIdx     primitive index fallback; prim pointer is authoritative\n * @param[in]  maxJoint    fixed slot count per vertex (typically 4)\n * @param[out] outIndices  buffer for vertexCount × maxJoint × sizeof(uint16_t)\n * @param[out] outWeights  buffer for vertexCount × maxJoint × sizeof(float)\n * @return     vertex count on success, 0 on error.\n */\nAK_EXPORT\nsize_t\nak_skinFillWeights(AkSkin          * __restrict skin,\n                   AkMeshPrimitive * __restrict prim,\n                   uint32_t                     primIdx,\n                   uint32_t                     maxJoint,\n                   uint16_t        * __restrict outIndices,\n                   float           * __restrict outWeights);\n\n/*!\n * @brief collect vertex indices affected by one joint for one primitive.\n *\n *        Works for both DAE CSR skin weights and glTF JOINTS_n/WEIGHTS_n\n *        accessors. The function returns the total matching vertex count even\n *        when `outVertices` is NULL or `capacity` is smaller than the result,\n *        so callers can first query size and then fill a buffer.\n *\n * @param[in]  skin         skin owning the bone data\n * @param[in]  prim         mesh primitive at index `primIdx`\n * @param[in]  primIdx      primitive index in mesh\n * @param[in]  jointIdx     joint index to scan for\n * @param[out] outVertices  optional vertex-index buffer\n * @param[in]  capacity     number of uint32_t slots in outVertices\n * @return     total number of affected vertices.\n */\nAK_EXPORT\nsize_t\nak_skinVerticesForJoint(AkSkin          * __restrict skin,\n                        AkMeshPrimitive * __restrict prim,\n                        uint32_t                     primIdx,\n                        uint32_t                     jointIdx,\n                        uint32_t        * __restrict outVertices,\n                        size_t                       capacity);\n\n/*!\n * @brief inspect a morph to get bufferSize and bufferStride to alloc memory for\n *        interleaved morph buffer with desired inputs. Also returns a list of \n *        inputs for each target. You can use this list to collect inputs from\n *        morph targets. \n *\n *        inspected result will be stored in morph->inspectResult. You can use\n *        this result to collect inputs same order as baseShape's inputs' order\n *        from morph targets. Inputs that dont exists in baseShape will be ignored.\n *        If you need them, pass ignoreUncommonInputs = false.\n * \n * @param[in]  baseMesh               base mesh to morph\n * @param[in]  morph                  AkMorph object\n * @param[in]  desiredInputs          desired inputs (other inputs will be ignored)\n *                                        or NULL to collect all inputs, desiredInputsCount must be 0 in this case\n * @param[in]  desiredInputsCount     desired inputs count or 0 to collect all inputs\n * @param[in]  includeBaseShape       if true, baseShape will be included in result e.g. bytes stride, buffer size etc.\n * @param[in]  ignoreUncommonInputs   if true, all inputs that dont exist in base mesh will be ignored\n */\nAK_EXPORT\nAkResult\nak_morphInspect(AkGeometry * __restrict baseMesh,\n                AkMorph    * __restrict morph,\n                AkInputSemantic         desiredInputs[],\n                uint8_t                 desiredInputsCount,\n                bool                    includeBaseShape,\n                bool                    ignoreUncommonInputs);\n\n/*!\n  * @brief prepare morph inspect result to interleave morph object with desired inputs\n  *        this prepares inputs order by specified layout parameter and sets intrOffset, \n  *        inBaseMesh etc. properties.\n  *\n  *        make sure that you called ak_morphInspect() to get buffSize\n  *        and alloc a buffer with that size.\n  *\n  * @param[in]  inspectView  inspect result\n  * @param[in]  layout       interleave layout e.g. p1p2n1n2 or p1n1p2n2\n  */\nAK_EXPORT\nAkResult\nak_morphInspectPrepareLayout(AkMorphInspectView * __restrict inspectView, \n                             AkMorphInterleaveLayout         layout);\n\n/*!\n * @brief interleave morph object with desired inputs with desired input orders.\n *\n *        Make sure that you called ak_morphInspect() to get buffSize\n *        and alloc a buffer with that size.\n *\n *        All inputs except desired inputs will be ignored. If morph object don't\n *        contain a desired input than it will be ignored too.\n *\n *        You can send this buffer to GPU and use directly.\n * \n *        WARN: all inputs that dont exist in base mesh will be ignored\n *              if you need them, you can use ak_morphInspect() to get all \n *              inputs for your own interleave() implementation. Create an issue \n *              if you need bring this feature to here.\n *\n * @param[in]  baseMesh      base mesh to morph\n * @param[in]  morph         AkMorph object\n * @param[in]  layout        interleave layout e.g. p1p2n1n2 or p1n1p2n2\n * @param[out] destBuff      pre-allocated buffer to store interleaved data\n */\nAK_EXPORT\nAkResult\nak_morphInterleave(AkGeometry * __restrict baseMesh,\n                   AkMorph    * __restrict morph,\n                   AkMorphInterleaveLayout layout,\n                   void       * __restrict destBuff);\n\nAK_EXPORT\nAkResult\nak_morphInterleaveWithProgress(AkGeometry         * __restrict baseMesh,\n                               AkMorph            * __restrict morph,\n                               AkMorphInterleaveLayout         layout,\n                               void               * __restrict destBuff,\n                               AkMorphProgressFn                progress,\n                               void               * __restrict userdata);\n\nAK_EXPORT\nconst AkMorphPreset*\nak_morphPresetByName(AkMorph    * __restrict morph,\n                     const char * __restrict name);\n\nAK_EXPORT\nbool\nak_morphApplyPreset(AkMorph    * __restrict morph,\n                    const char * __restrict presetName,\n                    float      * __restrict outWeights,\n                    uint32_t                capacity);\n\nAK_INLINE\nbool\nak_morphHasOverride(const AkInstanceMorph* inst) {\n  return inst && inst->overrideWeights && inst->overrideWeights->count > 0;\n}\n\n/* TODO: CPU morph evaluator (utility, low priority)\n *\n * Computes the final blended vertex data on the CPU using base mesh + targets\n * + weights. Output is a \"final deformed mesh\" ready to be uploaded as a\n * single static draw — no GPU-side blending required.\n *\n * Use cases:\n *   - mesh export / bake tools (e.g., bake \"smile_max\" pose as a static asset)\n *   - software renderers without GPU shader blending\n *   - pre-bake / asset pipeline tooling\n *\n * Modern engines (SceneKit, RealityKit, custom Metal/Vulkan) do NOT need this:\n * they consume ak_morphInterleave output + a weights uniform array and blend\n * on the GPU. This evaluator is purely for the niche cases above.\n *\n * Sketch:\n *\n * AK_EXPORT\n * AkResult\n * ak_morphEvaluate(AkGeometry            * __restrict baseMesh,\n *                  AkMorph               * __restrict morph,\n *                  const AkInstanceMorph * __restrict inst,     // NULL → defaults\n *                  void                  * __restrict destBuff);// pre-alloc base layout\n */\n\n//AkResult\n//ak_morphEvaluateWeights(const AkMorph         * __restrict morph,\n//                        const AkInstanceMorph * __restrict inst,\n//                        AkFloat                ** __restrict out /* len = morph->targetCount */) {\n//  AkFloatArray *ov;\n//  AkFloatArray *def;\n//  AkMesh       *mesh;\n//  uint32_t      n;\n//\n//  if (!morph || !out)\n//    return AK_ERR;\n//\n//  n   = morph->targetCount;\n//  ov  = (inst) ? inst->overrideWeights : NULL;\n//\n//  /* 1) Kaynak seçimi ve kopyalama (override > default > zeros) */\n//  if (ov && ov->count) {\n//    *out = ov->items;\n//    return AK_OK;\n//  } else if ((def = morph->defaultWeights) && def->count) {\n//    *out = def->items;\n//    return AK_OK;\n//  } else if ((ak_objGet(morph->target->target))) {\n//\n//\n//    return mesh->weights;\n//  }\n//\n//  return AK_OK;\n//}\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_controller_h */\n"
  },
  {
    "path": "include/ak/coord-util.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_coord_util_h\n#define assetkit_coord_util_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n\nstruct AkNode;\nstruct AkVisualScene;\n\nAK_EXPORT\nvoid\nak_changeCoordSys(AkDoc * __restrict doc,\n                  AkCoordSys * newCoordSys);\n\nAK_EXPORT\nvoid\nak_changeCoordSysGeom(AkGeometry * __restrict geom,\n                      AkCoordSys * newCoordSys);\n\nAK_EXPORT\nvoid\nak_changeCoordSysMesh(AkMesh * __restrict mesh,\n                      AkCoordSys * newCoordSys);\n\nAK_EXPORT\nvoid\nak_fixNodeCoordSys(struct AkNode * __restrict node);\n\nAK_EXPORT\nvoid\nak_fixSceneCoordSys(struct AkVisualScene * __restrict scene);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_coord_util_h */\n"
  },
  {
    "path": "include/ak/coord.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_coord_h\n#define assetkit_coord_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n\ntypedef enum AkCoordCvtType {\n  AK_COORD_CVT_DISABLED      = 0, /* import document as they are,\n                                     don't apply any coord sys operation    */\n  AK_COORD_CVT_FIX_TRANSFORM = 1, /* only add transforms to fix orientation */\n  AK_COORD_CVT_ALL           = 2, /* change positions, all things...        */\n  AK_COORD_CVT_DEFAULT       = AK_COORD_CVT_FIX_TRANSFORM\n} AkCoordCvtType;\n\ntypedef struct AkAxisAccessor {\n  int8_t right;\n  int8_t up;\n  int8_t fwd;\n\n  int8_t s_right;\n  int8_t s_up;\n  int8_t s_fwd;\n} AkAxisAccessor;\n\ntypedef enum AkAxis {\n  AK_AXIS_NEGATIVE_X =-1,\n  AK_AXIS_NEGATIVE_Y =-2,\n  AK_AXIS_NEGATIVE_Z =-3,\n\n  AK_AXIS_POSITIVE_X = 1,\n  AK_AXIS_POSITIVE_Y = 2,\n  AK_AXIS_POSITIVE_Z = 3,\n} AkAxis;\n\ntypedef enum AkAxisRotDirection {\n  AK_AXIS_ROT_DIR_LH = -2,\n  AK_AXIS_ROT_DIR_RH = 0\n} AkAxisRotDirection;\n\ntypedef struct AkAxisOrientation {\n  AkAxis right;    /* +X */\n  AkAxis up;       /* +Y */\n  AkAxis fwd;      /* -Z */\n} AkAxisOrientation;\n\ntypedef struct AkCoordSys {\n  AkAxisOrientation  axis;\n\n  /* the default value is AK_AXIS_ROT_DIRECTION_RH (Right Handed) */\n  AkAxisRotDirection rotDirection;\n\n  /* when creating custom coord sys, this value must be set correctly, \n     there is no default value */\n  AkAxisOrientation  cameraOrientation;\n} AkCoordSys;\n\n/* Right Hand (Default) */\nextern AkCoordSys * AK_ZUP;\nextern AkCoordSys * AK_YUP;\nextern AkCoordSys * AK_XUP;\n\n/* Left Hand */\nextern AkCoordSys * AK_ZUP_LH;\nextern AkCoordSys * AK_YUP_LH;\nextern AkCoordSys * AK_XUP_LH;\n\nstruct AkTransform;\n\nAK_INLINE\nvoid\nak_coordAxisToiVec3(AkAxisOrientation axisOri, int32_t vec[3]) {\n  vec[0] = axisOri.right;\n  vec[1] = axisOri.up;\n  vec[2] = axisOri.fwd;\n}\n\nAK_INLINE\nvoid\nak_coordToiVec3(AkCoordSys * __restrict coordSys, int32_t vec[3]) {\n  vec[0] = coordSys->axis.right;\n  vec[1] = coordSys->axis.up;\n  vec[2] = coordSys->axis.fwd;\n}\n\nAK_INLINE\nbool\nak_coordOrientationIsEq(AkCoordSys *c1, AkCoordSys *c2) {\n  return  c1->axis.right == c2->axis.right\n            && c1->axis.up == c2->axis.up\n            && c1->axis.fwd == c2->axis.fwd;\n}\n\nAK_EXPORT\nvoid\nak_coordCvtVector(AkCoordSys *oldCoordSystem,\n                  float      *vector,\n                  AkCoordSys *newCoordSystem);\n\nAK_EXPORT\nvoid\nak_coordCvtVectorTo(AkCoordSys *oldCoordSystem,\n                    float      *oldVector,\n                    AkCoordSys *newCoordSystem,\n                    float      *newVector);\n\nAK_EXPORT\nvoid\nak_coordCvtVectors(AkCoordSys *oldCoordSystem,\n                   float      *vectorArray,\n                   size_t      len,\n                   AkCoordSys *newCoordSystem);\n\nAK_EXPORT\nvoid\nak_coordCvtTransform(AkCoordSys *oldCoordSystem,\n                     AkFloat4x4  transform,\n                     AkCoordSys *newCoordSystem);\n\n/* find transform between two coordinate system */\nAK_EXPORT\nvoid\nak_coordFindTransform(struct AkTransform *transform,\n                      AkCoordSys         *oldCoordSys,\n                      AkCoordSys         *newCoordSys);\n\nAK_EXPORT\nvoid\nak_coordFixCamOri(AkCoordSys *oldCoordSys,\n                  AkCoordSys *newCoordSys,\n                  AkFloat4x4  transform);\n\nstruct AkDoc;\nstruct AkNode;\n\nAK_EXPORT\nvoid\nak_coordRotNodeForFixedCoord(struct AkDoc *doc,\n                             void         *memparent,\n                             AkObject    **destTransform);\n\nAK_EXPORT\nvoid\nak_coordCvtNodeTransforms(struct AkDoc  * __restrict doc,\n                          struct AkNode * __restrict node);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_coord_h */\n"
  },
  {
    "path": "include/ak/core-types.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_core_types_h\n#define assetkit_core_types_h\n\ntypedef const char           *AkString;\ntypedef char                 *AkMutString;\ntypedef bool                  AkBool;\ntypedef int16_t               AkInt16;\ntypedef uint16_t              AkUInt16;\ntypedef int32_t               AkInt;\ntypedef uint32_t              AkUInt;\ntypedef int64_t               AkInt64;\ntypedef uint64_t              AkUInt64;\ntypedef float                 AkFloat;\ntypedef double                AkDouble;\n\ntypedef AkBool                AkBool4[4];\ntypedef AkInt                 AkInt2[2];\ntypedef AkInt                 AkInt4[4];\ntypedef AkFloat               AkFloat2[2];\ntypedef AkDouble              AkDouble2[2];\n\ntypedef              AkFloat  AkFloat3[3];\ntypedef              AkDouble AkDouble3[3];\ntypedef AK_ALIGN(16) AkFloat  AkFloat4[4];\ntypedef AK_ALIGN(16) AkDouble AkDouble4[4];\ntypedef AK_ALIGN(32) AkDouble AkDouble4x4[4];\ntypedef AK_ALIGN(32) AkFloat4 AkFloat4x4[4];\n\n#undef AK__DEF_ARRAY\n\n#define AK__DEF_ARRAY(TYPE)                                                   \\\n  typedef struct TYPE##Array {                                                \\\n    size_t count;                                                             \\\n    TYPE   items[];                                                           \\\n  } TYPE##Array;                                                              \\\n                                                                              \\\n  typedef struct TYPE##ArrayL {                                               \\\n    struct TYPE##ArrayL * next;                                               \\\n    size_t count;                                                             \\\n    TYPE   items[];                                                           \\\n  } TYPE##ArrayL\n\nAK__DEF_ARRAY(AkBool);\nAK__DEF_ARRAY(AkInt);\nAK__DEF_ARRAY(AkUInt);\nAK__DEF_ARRAY(AkFloat);\nAK__DEF_ARRAY(AkDouble);\nAK__DEF_ARRAY(AkString);\n\n#undef AK__DEF_ARRAY\n\n#endif /* assetkit_core_types_h */\n"
  },
  {
    "path": "include/ak/geom.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_geom_h\n#define assetkit_geom_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n#include \"map.h\"\n#include \"bbox.h\"\n#include \"map.h\"\n\nstruct RBTree;\nstruct AkGeometry;\nstruct AkMesh;\nstruct FListItem;\nstruct AkMaterial;\n\ntypedef enum AkGeometryType {\n  AK_GEOMETRY_MESH   = 1,\n  AK_GEOMETRY_SPLINE = 2,\n  AK_GEOMETRY_BREP   = 3\n} AkGeometryType;\n\ntypedef enum AkGeometryEditFlags {\n  AK_GEOM_EDIT_FLAG_ARRAYS  = 1,\n  AK_GEOM_EDIT_FLAG_INDICES = 2,\n  AK_GEOM_EDIT_FLAG_MUTEX   = 3\n} AkGeometryEditFlags;\n\ntypedef enum AkMeshPrimitiveType {\n  AK_PRIMITIVE_LINES      = 1,\n  AK_PRIMITIVE_POLYGONS   = 2,\n  AK_PRIMITIVE_TRIANGLES  = 3,\n  AK_PRIMITIVE_POINTS     = 4\n} AkMeshPrimitiveType;\n\ntypedef enum AkTriangleMode {\n  AK_TRIANGLES      = 1,\n  AK_TRIANGLE_STRIP = 3,\n  AK_TRIANGLE_FAN   = 5\n} AkTriangleMode;\n\ntypedef enum AkLineMode {\n  AK_LINES      = 1,\n  AK_LINE_LOOP  = 3,\n  AK_LINE_STRIP = 5\n} AkLineMode;\n\ntypedef struct AkVertices {\n  /* const char   * id; */\n  const char   *name;\n  AkTree       *extra;\n  AkInput      *input;\n  uint32_t      inputCount;\n} AkVertices;\n\n/* TODO: */\ntypedef struct AkJointDesc {\n  uint32_t *counts;\n  size_t   *indexes;\n  uint32_t  allCount;\n} AkJointDesc;\n\ntypedef struct AkMeshPrimitive {\n  struct AkMeshPrimitive *next;\n  struct AkMesh          *mesh;\n  AkBoundingBox          *bbox;   /* per-primitive bbox */\n  const char             *name;\n  const char             *bindmaterial;\n  struct AkMaterial      *material;\n  AkInput                *input;\n  AkInput                *pos;\n  AkUIntArray            *indices;\n  AkTree                 *extra;\n  void                   *udata;\n  AkMeshPrimitiveType     type;\n  uint32_t                nPolygons;\n  uint32_t                inputCount;\n  AkFloat3                center;\n\n  /* TODO: remove */\n  uint32_t                indexStride;\n  uint32_t                reserved1; /* private member */\n  uint32_t                reserved2; /* private member */\n  void                   *reserved3; /* private member */\n\n  /* KHR_materials_variants: optional material overrides. */\n  struct AkMaterialVariantMapping *variantMappings;\n  uint32_t                         variantMappingCount;\n\n  /* KHR_gaussian_splatting: optional splat metadata. Splat inputs stay\n     in the normal primitive input chain. */\n  struct AkGaussianSplat          *gsplat;\n} AkMeshPrimitive;\n\ntypedef struct AkLines {\n  AkMeshPrimitive base;\n  AkLineMode      mode;\n} AkLines;\n\ntypedef struct AkPolygon {\n  AkMeshPrimitive base;\n  AkDoubleArrayL *holes;\n  AkUIntArray    *vcount;\n  AkBool          haveHoles;\n} AkPolygon;\n\ntypedef struct AkTriangles {\n  AkMeshPrimitive base;\n  AkTriangleMode  mode;\n} AkTriangles;\n\ntypedef struct AkMeshEditHelper {\n  AkGeometryEditFlags flags;\n  struct RBTree      *buffers;         /* new buffers               */\n  struct RBTree      *indices;         /* new indices               */\n  AkMap              *inputBufferMap;  /* input-accessor-buffer map */\n  void               *mutex;\n  void               *duplicator;\n  bool                skipFixIndices;\n} AkMeshEditHelper;\n\ntypedef struct AkMesh {\n  struct AkGeometry *geom;\n  const char        *convexHullOf;\n  AkMeshPrimitive   *primitive;\n  AkBoundingBox     *bbox;\n  AkTree            *extra;\n  AkMeshEditHelper  *edith;\n  struct FListItem  *skins;\n  const char        *name;\n  AkFloatArray      *weights;\n  uint32_t           primitiveCount;\n  AkFloat3           center;\n} AkMesh;\n\ntypedef struct AkSpline {\n  struct AkGeometry *geom;\n  AkSource          *source;\n  AkVertices        *cverts;\n  AkTree            *extra;\n  AkBool             closed;\n} AkSpline;\n\ntypedef struct AkLine {\n  AkFloat3 origin;\n  AkFloat3 direction;\n  AkTree  *extra;\n} AkLine;\n\ntypedef struct AkCircle {\n  AkFloat radius;\n  AkTree *extra;\n} AkCircle;\n\ntypedef struct AkEllipse {\n  AkFloat2 radius;\n  AkTree  *extra;\n} AkEllipse;\n\ntypedef struct AkParabola {\n  AkFloat focal;\n  AkTree *extra;\n} AkParabola;\n\ntypedef struct AkHyperbola {\n  AkFloat2 radius;\n  AkTree  *extra;\n} AkHyperbola;\n\ntypedef struct AkNurbs {\n  AkSource   *source;\n  AkVertices *cverts;\n  AkTree     *extra;\n  AkUInt      degree;\n  AkBool      closed;\n} AkNurbs;\n\ntypedef struct AkCurve {\n  AkFloatArrayL *orient;\n  AkFloat3       origin;\n  AkObject       *curve;\n  struct AkCurve *next;\n} AkCurve;\n\ntypedef struct AkCurves {\n  AkCurve *curve;\n  AkTree  *extra;\n} AkCurves;\n\ntypedef struct AkCone {\n  AkFloat radius;\n  AkFloat angle;\n  AkTree *extra;\n} AkCone;\n\ntypedef struct AkPlane {\n  AkFloat4 equation;\n  AkTree  *extra;\n} AkPlane;\n\ntypedef struct AkCylinder {\n  AkFloat2 radius;\n  AkTree  *extra;\n} AkCylinder;\n\ntypedef struct AkNurbsSurface {\n  AkSource   *source;\n  AkVertices *cverts;\n  AkTree     *extra;\n  AkUInt      degree_u;\n  AkUInt      degree_v;\n  AkBool      closed_u;\n  AkBool      closed_v;\n} AkNurbsSurface;\n\ntypedef struct AkSphere {\n  AkFloat radius;\n  AkTree *extra;\n} AkSphere;\n\ntypedef struct AkTorus {\n  AkFloat2 radius;\n  AkTree * extra;\n} AkTorus;\n\ntypedef struct AkSweptSurface {\n  AkCurve *curve;\n  AkFloat3 direction;\n  AkFloat3 origin;\n  AkFloat3 axis;\n  AkTree  *extra;\n} AkSweptSurface;\n\ntypedef struct AkSurface {\n  /* const char * sid; */\n\n  const char       *name;\n  AkObject         *surface;\n  AkFloatArrayL    *orient;\n  AkFloat3          origin;\n  struct AkSurface *next;\n} AkSurface;\n\ntypedef struct AkSurfaces {\n  AkSurface *surface;\n  AkTree    *extra;\n} AkSurfaces;\n\ntypedef struct AkEdges {\n  /* const char    * id; */\n  const char    *name;\n  AkInput       *input;\n  AkUIntArray   *primitives;\n  AkTree        *extra;\n  AkUInt         count;\n  uint32_t       inputCount;\n} AkEdges;\n\ntypedef struct AkWires {\n  /* const char    * id; */\n  const char  *name;\n  AkInput     *input;\n  AkUIntArray *vcount;\n  AkUIntArray *primitives;\n  AkTree      *extra;\n  AkUInt       count;\n  uint32_t     inputCount;\n} AkWires;\n\ntypedef struct AkFaces {\n  /* const char    * id; */\n  const char  *name;\n  AkInput     *input;\n  AkUIntArray *vcount;\n  AkUIntArray *primitives;\n  AkTree      *extra;\n  AkUInt       count;\n  uint32_t     inputCount;\n} AkFaces;\n\ntypedef struct AkPCurves {\n  /* const char    * id; */\n  const char  *name;\n  AkInput     *input;\n  AkUIntArray *vcount;\n  AkUIntArray *primitives;\n  AkTree      *extra;\n  AkUInt       count;\n  uint32_t     inputCount;\n} AkPCurves;\n\ntypedef struct AkShells {\n  /* const char    * id; */\n  const char  *name;\n  AkInput     *input;\n  AkUIntArray *vcount;\n  AkUIntArray *primitives;\n  AkTree      *extra;\n  AkUInt       count;\n  uint32_t     inputCount;\n} AkShells;\n\ntypedef struct AkSolids {\n  /* const char    * id; */\n  const char  *name;\n  AkInput     *input;\n  AkUIntArray *vcount;\n  AkUIntArray *primitives;\n  AkTree      *extra;\n  AkUInt       count;\n  uint32_t     inputCount;\n} AkSolids;\n\n/* TODO: */\ntypedef struct AkBoundryRep {\n  struct AkGeometry *geom;\n  AkCurves          *curves;\n  AkCurves          *surfaceCurves;\n  AkSurfaces        *surfaces;\n  AkSource          *source;\n  AkVertices        *vertices;\n  AkEdges           *edges;\n  AkWires           *wires;\n  AkFaces           *faces;\n  AkPCurves         *pcurves;\n  AkShells          *shells;\n  AkSolids          *solids;\n  AkTree            *extra;\n} AkBoundryRep;\n\ntypedef struct AkGeometry {\n  /* const char * id; */\n  AkOneWayIterBase   base;\n  const char        *name;\n  AkObject          *gdata;\n  AkTree            *extra;\n  AkMap             *materialMap;\n  AkBoundingBox     *bbox;\n} AkGeometry;\n\ntypedef enum AkMeshIsolateType {\n  AK_MESH_ISOLATE_NONE      = 0 << 0,\n  AK_MESH_ISOLATE_BUFFERS   = 1 << 1,\n  AK_MESH_ISOLATE_ACCESSORS = 2 << 2\n} AkMeshIsolateType;\n\n/*!\n * @brief Total input count except VERTEX input\n *\n * returns primitive.inputCount - 1 + primitive.vertices.inputCount\n *\n * @return total input count\n */\nAK_EXPORT\nuint32_t\nak_meshInputCount(AkMesh * __restrict mesh);\n\n/*!\n * @brief set material (symbol) for primitive\n *        actual material will set with bind_material/instance material\n *\n * @param prim     mesh primitive\n * @param material material\n *\n * @return result\n */\nAK_EXPORT\nAkResult\nak_meshSetMaterial(AkMeshPrimitive *prim,\n                   const char      *material);\n\n/*!\n * @brief triangulate all mesh primitives\n *\n * @param mesh mesh\n *\n * @return new triangles/faces count\n */\nAK_EXPORT\nuint32_t\nak_meshTriangulate(AkMesh * __restrict mesh);\n\n/*!\n * @brief triangulate polygon\n *\n * @param poly polygon primitive\n *\n * @return new triangles/faces count\n */\nAK_EXPORT\nuint32_t\nak_meshTriangulatePoly(AkPolygon * __restrict poly);\n\n/*!\n * @brief returns true if least one primitive doesn't have normals\n *\n * @param mesh mesh\n *\n * @return boolean\n */\nAK_EXPORT\nbool\nak_meshNeedsNormals(AkMesh * __restrict mesh);\n\n/*!\n * @brief returns true if primitive doesn't have normals\n *\n * @param prim primitive\n *\n * @return boolean\n */\nAK_EXPORT\nbool\nak_meshPrimNeedsNormals(AkMeshPrimitive * __restrict prim);\n\n/*!\n * @brief generate normals for all pritimives of mesh\n *\n * @param mesh mesh\n */\nAK_EXPORT\nvoid\nak_meshGenNormals(AkMesh * __restrict mesh);\n\n/*!\n * @brief prepare mesh for edit, or enable edit mode with default attribs\n *\n * @param mesh mesh\n */\nAK_EXPORT\nvoid\nak_meshBeginEdit(AkMesh * __restrict mesh);\n\n/*!\n * @brief prepare mesh for edit, or enable edit mode\n *\n * @param mesh  meshs\n * @param flags flags needed while edit mode\n */\nAK_EXPORT\nvoid\nak_meshBeginEditA(AkMesh  * __restrict mesh,\n                  AkGeometryEditFlags flags);\n\n/*!\n * @brief finish edit, disable edit mode, relese allocated memory for editing\n *\n * @param mesh mesh\n */\nAK_EXPORT\nvoid\nak_meshEndEdit(AkMesh * __restrict mesh);\n\nAK_EXPORT\nAkUIntArray*\nak_meshIndicesArrayFor(AkMesh          * __restrict mesh,\n                       AkMeshPrimitive * __restrict prim,\n                       bool                         readonly);\n\nAK_EXPORT\nAkSourceBuffState*\nak_meshReserveBuffer(AkMesh * __restrict mesh,\n                     void   * __restrict buffid,\n                     size_t              itemSize,\n                     uint32_t            stride,\n                     size_t              acc_count);\n\nAK_EXPORT\nvoid\nak_meshReserveBufferForInput(AkMesh   * __restrict mesh,\n                             AkInput  * __restrict input,\n                             size_t                count);\n\nAK_EXPORT\nvoid\nak_meshReserveBuffers(AkMesh          * __restrict mesh,\n                      AkMeshPrimitive * __restrict prim,\n                      size_t                       count);\n\nAK_EXPORT\nAkResult\nak_meshFillBuffers(AkMesh * __restrict mesh);\n\nAK_EXPORT\nvoid\nak_moveIndices(AkMesh * __restrict mesh);\n\nAK_EXPORT\nvoid\nak_meshMoveBuffers(AkMesh * __restrict mesh);\n\nAK_EXPORT\nAkSourceEditHelper*\nak_meshSourceEditHelper(AkMesh  * __restrict mesh,\n                        AkInput * __restrict input);\n\nAK_EXPORT\nAkDuplicator*\nak_meshDuplicatorForIndices(AkMesh          * __restrict mesh,\n                            AkMeshPrimitive * __restrict prim);\n\nAK_EXPORT\nvoid\nak_meshFixIndexBuffer(AkMesh          * __restrict mesh,\n                      AkMeshPrimitive * __restrict prim,\n                      AkDuplicator    * __restrict duplicator);\n\n\nvoid\nak_meshReIndexInputs(AkMesh * __restrict mesh);\n\nvoid\nak_inputNameIndexed(AkInput * __restrict input,\n                    char    * __restrict buf);\n\nAK_EXPORT\nvoid\nak_inputNameBySet(AkInput * __restrict input,\n                  char    * __restrict buf);\n\nAK_EXPORT\nAkInput*\nak_meshInputGet(AkMeshPrimitive *prim,\n                const char      *inputSemantic,\n                uint32_t         set);\n\nAK_EXPORT\nbool\nak_meshIsIsolated(AkMesh *mesh);\n\n/*!\n * @brief current isolation rule\n *\n * 1. Separated buffer per accessor\n * 2. Separated accessor per input\n *\n * @return true if the rules are match\n */\nAK_EXPORT\nbool\nak_meshIsPrimIsolated(AkMeshPrimitive *prim);\n\nAK_EXPORT\nvoid\nak_meshIsolate(AkMesh *mesh);\n\nAK_EXPORT\nvoid\nak_meshIsolatePrim(AkMeshPrimitive *prim);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_geom_h */\n"
  },
  {
    "path": "include/ak/gsplat.h",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_gsplat_h\n#define assetkit_gsplat_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n\n/*!\n * @brief KHR_gaussian_splatting metadata.\n *\n * The base extension stores splats as POINT primitives. Per-splat data\n * stays in AkMeshPrimitive.input (POSITION, ROTATION, SCALE, OPACITY,\n * COLOR_0..COLOR_15). This struct stores only the extension-level\n * rendering hints and optional decoder output for compressed payloads.\n */\n\ntypedef enum AkGaussianSplatKernel {\n  AK_GSPLAT_KERNEL_UNKNOWN = 0,\n  AK_GSPLAT_KERNEL_ELLIPSE = 1   /* 2D ellipse projection of an ellipsoid */\n} AkGaussianSplatKernel;\n\ntypedef enum AkGaussianSplatColorSpace {\n  AK_GSPLAT_COLOR_UNKNOWN              = 0,\n  AK_GSPLAT_COLOR_SRGB_REC709_DISPLAY  = 1, /* \"srgb_rec709_display\" */\n  AK_GSPLAT_COLOR_LIN_REC709_DISPLAY   = 2  /* \"lin_rec709_display\"  */\n} AkGaussianSplatColorSpace;\n\ntypedef enum AkGaussianSplatProjection {\n  AK_GSPLAT_PROJECTION_PERSPECTIVE  = 0, /* default */\n  AK_GSPLAT_PROJECTION_ORTHOGRAPHIC = 1\n} AkGaussianSplatProjection;\n\ntypedef enum AkGaussianSplatSortingMethod {\n  AK_GSPLAT_SORTING_CAMERA_DISTANCE = 0, /* default */\n  AK_GSPLAT_SORTING_NONE            = 1\n} AkGaussianSplatSortingMethod;\n\ntypedef struct AkGaussianSplat {\n  AkGaussianSplatKernel        kernel;\n  AkGaussianSplatColorSpace    colorSpace;\n  AkGaussianSplatProjection    projection;\n  AkGaussianSplatSortingMethod sortingMethod;\n\n  /* Filled by an optional decoder when a compression extension is present. */\n  void                        *decodedData;     /* opaque, decoder-owned */\n  uint32_t                     decodedCount;     /* decoded splat count */\n  uint32_t                     reserved;\n} AkGaussianSplat;\n\n/*---------------------------------------------------------------------*/\n/* External decoder interface.                                         */\n/*                                                                     */\n/* Apps provide a side library exporting assetkit_gsplat_create, the   */\n/* same pattern used by Draco / meshoptimizer / KTX2 shims. AssetKit   */\n/* dlopens it from AK_OPT_GLTF_GSPLAT_DECODER_PATH or, when autoload   */\n/* is enabled, from the standard side-library name.                    */\n/*                                                                     */\n/* The uncompressed base KHR_gaussian_splatting extension does not     */\n/* need this decoder; renderers read primitive accessors directly.     */\n/*---------------------------------------------------------------------*/\n\nstruct AkHeap;\nstruct AkGLTFState;\nstruct AkMeshPrimitive;\nstruct json_t;\n\n/* Decoders may implement either entrypoint. decodeBytes is preferred when\n   the compression extension references a bufferView directly; decodePrimitive\n   is kept for formats that need broader glTF state. */\ntypedef int\n(*AkGaussianSplatDecodeBytesFn)(struct AkHeap          * heap,\n                                struct AkMeshPrimitive * prim,\n                                const uint8_t          * data,\n                                size_t                   size);\n\ntypedef int\n(*AkGaussianSplatDecodePrimitiveFn)(struct AkGLTFState     * gst,\n                                    struct AkMeshPrimitive * prim,\n                                    const struct json_t    * jprim,\n                                    const struct json_t    * jcompression);\n\ntypedef struct AkGaussianSplatDecoder {\n  void                              *userdata;\n  AkGaussianSplatDecodeBytesFn       decodeBytes;\n  AkGaussianSplatDecodePrimitiveFn   decodePrimitive;\n  void                             (*close)(void *ud);\n} AkGaussianSplatDecoder;\n\n/*!\n * @brief Decoder-library entrypoint. Returns 0 on success.\n */\ntypedef int\n(*AkGaussianSplatDecoderCreateFn)(AkGaussianSplatDecoder * out);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_gsplat_h */\n"
  },
  {
    "path": "include/ak/image.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_image_h\n#define assetkit_image_h\n\n#include \"common.h\"\n\nstruct AkInitFrom;\n\ntypedef enum AkImageType {\n  AK_IMAGE_TYPE_1D   = 0,\n  AK_IMAGE_TYPE_2D   = 1,\n  AK_IMAGE_TYPE_3D   = 2,\n  AK_IMAGE_TYPE_CUBE = 3\n} AkImageType;\n\ntypedef struct AkImageData {\n  void    *data;\n  uint32_t width;\n  uint32_t height;\n  AkEnum   comp;\n} AkImageData;\n\ntypedef struct AkSizeExact {\n  uint32_t width;\n  uint32_t height;\n} AkSizeExact;\n\ntypedef struct AkSizeRatio {\n  float width;\n  float height;\n} AkSizeRatio;\n\ntypedef struct AkMips {\n  uint32_t levels;\n  bool     autoGenerate;\n} AkMips;\n\ntypedef struct AkImageFormat {\n  const char       *space;\n  const char       *exact;\n  AkChannelFormat   channel;\n  AkRangeFormat     range;\n  AkPrecisionFormat precision;\n} AkImageFormat;\n\ntypedef struct AkImageSize {\n  AkUInt width;\n  AkUInt height;\n  AkUInt depth;\n} AkImageSize;\n\ntypedef struct AkImageBase {\n  AkImageFormat     *format;\n  struct AkInitFrom *initFrom;\n  long               arrayLen;\n  AkImageType        type;\n} AkImageBase;\n\ntypedef struct AkImage2d {\n  AkImageBase  base;\n  AkSizeExact *sizeExact;\n  AkSizeRatio *sizeRatio;\n  AkMips      *mips;\n  const char  *unnormalized;\n} AkImage2d;\n\ntypedef struct AkImage3d {\n  AkImageBase base;\n  AkImageSize size;\n  AkMips      mips;\n} AkImage3d;\n\ntypedef struct AkImageCube {\n  AkImageBase base;\n  uint32_t    width;\n  AkMips      mips;\n} AkImageCube;\n\ntypedef struct AkImage {\n  /* const char * id;  */\n  /* const char * sid; */\n  const char        *name;\n  struct AkInitFrom *initFrom;\n  AkImageBase       *image;\n  AkImageData       *data;\n  AkTree            *extra;\n  struct AkImage    *next;\n\n  AkBool             renderable;\n  AkBool             renderableShare;\n\n  bool               flipOnLoad;\n} AkImage;\n\nAK_EXPORT\nvoid\nak_imageLoad(AkImage * __restrict image);\n\n/* Loader Configurator */\ntypedef AkImageData* (*AkImageLoadFromFileFn)(AkHeap     * __restrict heap,\n                                              AkImage    * __restrict image,\n                                              const char * __restrict path,\n                                              bool                    flipVertically);\n\ntypedef AkImageData* (*AkImageLoadFromMemoryFn)(AkHeap   * __restrict heap,\n                                                AkImage  * __restrict image,\n                                                AkBuffer * __restrict buff,\n                                                bool                  flipVertically);\n\ntypedef void  (*AkImageFlipVerticallyOnLoad)(bool flip);\n\nAK_EXPORT\nvoid\nak_imageInitLoader(AkImageLoadFromFileFn   fromFile,\n                   AkImageLoadFromMemoryFn fromMemory);\n\n#endif /* assetkit_image_h */\n"
  },
  {
    "path": "include/ak/instance.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_instance_h\n#define assetkit_instance_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n#include \"node.h\"\n\ntypedef struct AkInstanceListItem {\n  struct AkInstanceListItem *prev;\n  struct AkInstanceListItem *next;\n  AkInstanceBase *instance;\n  size_t          index;\n} AkInstanceListItem;\n\ntypedef struct AkInstanceList {\n  AkInstanceListItem *first;\n  AkInstanceListItem *last;\n  size_t              count;\n  size_t              lastindex;\n} AkInstanceList;\n\nAK_EXPORT\nAkInstanceBase*\nak_instanceMake(AkHeap * __restrict heap,\n                void   * __restrict memparent,\n                void   * __restrict object);\n\nAK_EXPORT\nAkInstanceGeometry*\nak_instanceMakeGeom(AkHeap     * __restrict heap,\n                    void       * __restrict memparent,\n                    AkGeometry * __restrict object);\n\nAK_EXPORT\nvoid\nak_instanceListAdd(AkInstanceList *list,\n                   AkInstanceBase *inst);\n\nAK_EXPORT\nvoid\nak_instanceListDel(AkInstanceList *list,\n                   AkInstanceListItem *item);\n\nAK_EXPORT\nvoid\nak_instanceListEmpty(AkInstanceList *list);\n\nchar*\nak_instanceName(AkInstanceListItem *item);\n\nAK_EXPORT\nvoid *\nak_instanceObject(AkInstanceBase *instance);\n\nAK_EXPORT\nAkNode *\nak_instanceObjectNode(AkNode * node);\n\nAK_EXPORT\nAkGeometry *\nak_instanceObjectGeom(AkNode * node);\n\nAK_EXPORT\nAkGeometry *\nak_instanceObjectGeomId(AkDoc * __restrict doc,\n                        const char * id);\n\nAK_EXPORT\nAkNode*\nak_instanceMoveToSubNode(AkNode * __restrict node,\n                         AkInstanceBase     *inst);\n\nAK_EXPORT\nAkNode*\nak_instanceMoveToSubNodeIfNeeded(AkNode * __restrict node,\n                                 AkInstanceBase     *inst);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_instance_h */\n"
  },
  {
    "path": "include/ak/library.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_lib_h\n#define assetkit_lib_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n#include \"cam.h\"\n#include \"light.h\"\n\ntypedef struct AkLibrary {\n  /* const char * id; */\n\n  struct AkLibrary *next;\n  const char       *name;\n  AkTree           *extra;\n  AkOneWayIterBase *chld;\n  uint64_t          count;\n} AkLibrary;\n\nAK_EXPORT\nAkGeometry *\nak_libFirstGeom(AkDoc * __restrict doc);\n\nAK_EXPORT\nAkResult\nak_libAddCamera(AkDoc * __restrict doc, AkCamera * __restrict cam);\n\nAK_EXPORT\nAkResult\nak_libAddLight(AkDoc * __restrict doc, AkLight * __restrict light);\n\nAK_EXPORT\nvoid\nak_libInsertInto(AkLibrary *lib, void *item, int32_t prevoff, int32_t nextoff);\n\nAK_EXPORT\nAkLibrary*\nak_libFirstOrCreat(AkDoc * __restrict doc, uint32_t itemOffset);\n\nAK_EXPORT\nAkLibrary*\nak_libImageFirstOrCreat(AkDoc * __restrict doc);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_lib_h */\n"
  },
  {
    "path": "include/ak/light.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_light_h\n#define assetkit_light_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n\ntypedef enum AkLightType {\n  AK_LIGHT_TYPE_AMBIENT     = 1,\n  AK_LIGHT_TYPE_DIRECTIONAL = 2,\n  AK_LIGHT_TYPE_POINT       = 3,\n  AK_LIGHT_TYPE_SPOT        = 4,\n  AK_LIGHT_TYPE_CUSTOM      = 5\n} AkLightType;\n\ntypedef struct AkLightBase {\n  AkLightType type;\n  uint32_t    ctype; /* custom type, because type always is custom */\n  AkColor     color;\n  AkFloat3    direction;\n  float       intensity;\n  float       range;\n} AkLightBase;\n\ntypedef AkLightBase AkAmbientLight;\ntypedef AkLightBase AkDirectionalLight;\n\ntypedef struct AkPointLight {\n  AkLightBase base;\n  float       constAttn;\n  float       linearAttn;\n  float       quadAttn;\n} AkPointLight;\n\ntypedef struct AkSpotLight {\n  AkLightBase base;\n  float       constAttn;\n  float       linearAttn;\n  float       quadAttn;\n  float       innerConeAngle;\n  float       outerConeAngle;\n  float       falloffAngle;\n  float       falloffExp;\n} AkSpotLight;\n\ntypedef struct AkLight {\n  /* const char * id; */\n  const char     *name;\n  AkLightBase    *tcommon;\n  AkTechnique    *technique;\n  AkTree         *extra;\n  struct AkLight *next;\n} AkLight;\n\nAK_EXPORT\nAkLight*\nak_defaultLight(void * __restrict memparent);\n\n/*!\n * @brief Allocate a light of the given type with sensible defaults\n *        (white color, downward direction for directional/spot,\n *        constant attenuation for point/spot, 30° spot falloff)\n *        and register it in the document's lights library.\n *\n * Wires up AkLight + the matching tcommon variant (AkLightBase for\n * ambient/directional, AkPointLight, or AkSpotLight) in one call.\n * Pair with ak_nodeAttachLight() to expose the light in the scene\n * tree.\n *\n * @param[in]  doc        document the light will live in (required)\n * @param[in]  memparent  heap parent for ownership (NULL → doc)\n * @param[in]  type       AK_LIGHT_TYPE_AMBIENT/DIRECTIONAL/POINT/SPOT\n *\n * @return Newly allocated AkLight, or NULL on failure (unsupported\n *         type, allocation failure, etc.)\n */\nAK_EXPORT\nAkLight *\nak_lightMake(AkDoc * __restrict doc,\n             void  * __restrict memparent,\n             AkLightType type);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_light_h */\n"
  },
  {
    "path": "include/ak/map.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* TODO: use separate rbtree instead of heap's rbtree (optional)\n *       this map impl have many TODOs\n */\n\n#ifndef assetkit_map_h\n#define assetkit_map_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n#include \"memory.h\"\n#include <stdbool.h>\n\ntypedef struct AkMapItem {\n  struct AkMapItem *prev;\n  struct AkMapItem *next;\n  void             *data;\n  bool              isMapItem;\n} AkMapItem;\n\ntypedef struct AkMap {\n  AkHeap    *heap;\n  AkMapItem *root;\n} AkMap;\n\ntypedef AkHeapSrchCmpFn AkMapCmp;\n\nAK_EXPORT\nvoid\nak_map_addptr(AkMap *map, void *ptr);\n\nAK_EXPORT\nvoid*\nak_map_find(AkMap *map, void *id);\n\nAK_EXPORT\nAkMapItem*\nak_map_findm(AkMap *map, void *id);\n\nAK_EXPORT\nvoid\nak_map_add(AkMap *map,\n           void  *value,\n           void  *id);\n\nAK_EXPORT\nvoid\nak_multimap_add(AkMap *map,\n                void  *value,\n                void  *id);\n\nAK_EXPORT\nAkMap *\nak_map_new(AkMapCmp cmp);\n\nAK_EXPORT\nvoid\nak_map_destroy(AkMap *map);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_map_h */\n"
  },
  {
    "path": "include/ak/material.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_material_h\n#define assetkit_material_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n#include \"texture.h\"\n\nstruct AkBindMaterial;\nstruct AkMeshPrimitive;\nstruct AkEffect;\nstruct AkInstanceMaterial;\nstruct AkMaterial;\nstruct AkDoc;\nstruct AkTreeNode;\n\n/*!\n * @brief Named material variant from KHR_materials_variants.\n */\ntypedef struct AkMaterialVariant {\n  struct AkMaterialVariant *next;\n  const char               *name;\n  struct AkTreeNode        *extras;\n} AkMaterialVariant;\n\n/*!\n * @brief Primitive material override for a document variant index.\n */\ntypedef struct AkMaterialVariantMapping {\n  struct AkMaterialVariantMapping *next;\n  struct AkMaterial               *material;\n  uint32_t                         variantIndex;\n} AkMaterialVariantMapping;\n\nAK_EXPORT\nAkMaterialVariant*\nak_materialVariantByName(struct AkDoc * __restrict doc,\n                         const char   * __restrict name);\n\ntypedef enum AkOpaque {\n  AK_OPAQUE_OPAQUE   = 0, /* fully opaque */\n  AK_OPAQUE_A_ONE    = 1,\n  AK_OPAQUE_A_ZERO   = 2,\n  AK_OPAQUE_RGB_ONE  = 3,\n  AK_OPAQUE_RGB_ZERO = 4,\n  AK_OPAQUE_BLEND    = 5, /* Blend only */\n  AK_OPAQUE_MASK     = 6, /* Activate alpha cutoff */\n  AK_OPAQUE_DEFAULT  = AK_OPAQUE_OPAQUE\n} AkOpaque;\n\ntypedef enum AkMaterialType {\n  AK_MATERIAL_PHONG              = 1,\n  AK_MATERIAL_BLINN              = 2,\n  AK_MATERIAL_LAMBERT            = 3,\n  AK_MATERIAL_CONSTANT           = 4,\n  AK_MATERIAL_METALLIC_ROUGHNESS = 5,  /* PBR Material */\n  AK_MATERIAL_SPECULAR_GLOSSINES = 6,  /* PBR Material */\n  AK_MATERIAL_PBR                = 7,  /* PBR Material */\n\n  AK_MATERIAL_UNLIT              = AK_MATERIAL_CONSTANT\n} AkMaterialType;\n\n/*\ntypedef struct AkFloatOrParam {\n  float   *val;\n  AkParam *param;\n} AkFloatOrParam;\n*/\n\ntypedef struct AkColorDesc {\n  AkColor      *color;\n  AkParam      *param;\n  AkTextureRef *texture;\n} AkColorDesc;\n\ntypedef struct AkTransparent {\n  AkColorDesc    *color;\n  float           amount;\n  AkOpaque        opaque;\n  float           cutoff;\n} AkTransparent;\n\ntypedef struct AkReflective {\n  AkColorDesc    *color;\n  float           amount;\n} AkReflective;\n\ntypedef struct AkOcclusion {\n  AkTextureRef      *tex;\n  float              strength;\n  AkTextureChannels  textureChannels;\n} AkOcclusion;\n\ntypedef struct AkNormalMap {\n  AkTextureRef *tex;\n  float         scale;\n} AkNormalMap;\n\ntypedef struct AkMaterialMetallicProp {\n  AkTextureRef      *tex;\n  float              intensity;\n  AkTextureChannels  textureChannels;\n} AkMaterialMetallicProp;\n\ntypedef struct AkMaterialSpecularProp {\n  AkTextureRef      *specularTex;\n  AkColorDesc       *color;\n  union {\n    float            strength;\n    float            shininess;\n  };\n  AkTextureChannels  textureChannels;\n} AkMaterialSpecularProp;\n\ntypedef struct AkMaterialClearcoat {\n  AkTextureRef      *roughnessTexture;\n  AkTextureRef      *normalTexture;\n\n  AkTextureRef      *texture;\n  float              intensity;\n  float              roughness;\n  float              normalScale;\n  AkTextureChannels  textureChannels;\n  AkTextureChannels  roughnessTextureChannels;\n} AkMaterialClearcoat;\n\ntypedef struct AkMaterialEmissionProp {\n  AkColorDesc color;\n  float       strength;\n} AkMaterialEmissionProp;\n\n/* KHR_materials_transmission */\ntypedef struct AkMaterialTransmissionProp {\n  AkTextureRef      *texture;\n  float              factor;\n  AkTextureChannels  textureChannels;\n} AkMaterialTransmissionProp;\n\ntypedef struct AkMaterialSheen {\n  AkColorDesc       *color;\n  AkTextureRef      *roughnessTexture;\n  float              roughness;\n  AkTextureChannels  roughnessTextureChannels;\n} AkMaterialSheen;\n\ntypedef struct AkMaterialIridescence {\n  AkTextureRef      *texture;\n  AkTextureRef      *thicknessTexture;\n  float              factor;\n  float              ior;\n  float              thicknessMinimum;\n  float              thicknessMaximum;\n  AkTextureChannels  textureChannels;\n  AkTextureChannels  thicknessTextureChannels;\n} AkMaterialIridescence;\n\ntypedef struct AkMaterialVolume {\n  AkTextureRef      *thicknessTexture;\n  AkColor            attenuationColor;\n  float              thicknessFactor;\n  float              attenuationDistance;\n  AkTextureChannels  thicknessTextureChannels;\n} AkMaterialVolume;\n\ntypedef struct AkMaterialAnisotropy {\n  AkTextureRef *texture;\n  float         strength;\n  float         rotation;\n} AkMaterialAnisotropy;\n\ntypedef struct AkMaterialDispersion {\n  float         dispersion;\n} AkMaterialDispersion;\n\ntypedef struct AkMaterialDiffuseTransmission {\n  AkTextureRef      *texture;\n  AkColorDesc       *color;\n  float              factor;\n  AkTextureChannels  textureChannels;\n} AkMaterialDiffuseTransmission;\n\ntypedef struct AkTechniqueFxCommon {\n  AkColorDesc                *ambient;\n  AkMaterialEmissionProp      *emission;\n\n  union {\n    AkColorDesc              *diffuse;\n    AkColorDesc              *albedo;\n  };\n\n  AkOcclusion                *occlusion;\n  AkNormalMap                *normal;\n  AkMaterialClearcoat        *clearcoat;\n\n  /* metallic properties      */\n  AkMaterialMetallicProp     *metalness;\n  AkMaterialMetallicProp     *roughness;\n\n  /* specular */\n  AkMaterialSpecularProp     *specular;\n\n  /* reflectivity */\n  AkReflective               *reflective;\n  float                       ior;\n\n  /* TODO: can we merge transparent and transmission */\n  /* transparency */\n  AkTransparent              *transparent;\n  AkMaterialTransmissionProp *transmission;\n  AkMaterialSheen            *sheen;\n  AkMaterialIridescence      *iridescence;\n  AkMaterialVolume           *volume;\n  AkMaterialAnisotropy       *anisotropy;\n  AkMaterialDispersion       *dispersion;\n  AkMaterialDiffuseTransmission *diffuseTransmission;\n\n  /* common */\n  AkMaterialType              type;\n  bool                        doubleSided;\n} AkTechniqueFxCommon;\n\n/*!\n * @brief a helper that returns effect for given mesh prim for a bindMaterial\n *\n * @param bindMat      bind material object in AkNode\n * @param meshPrim     mesh primitive\n * @param foundInstMat instance material\n *\n * @return effect that points by a AkMaterial\n */\nAK_EXPORT\nstruct AkEffect*\nak_effectForBindMaterial(struct AkBindMaterial      * __restrict bindMat,\n                         struct AkMeshPrimitive     * __restrict meshPrim,\n                         struct AkInstanceMaterial ** __restrict foundInstMat);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_material_h */\n"
  },
  {
    "path": "include/ak/memory.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_memory_h\n#define assetkit_memory_h\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include \"common.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct AkObject {\n  struct AkObject * next;\n  size_t size;\n  AkEnum type;\n  void * pData;\n  char   data[];\n} AkObject;\n\n/* Inline payload access — pData is the start of the struct copy living\n   inside the AkObject's tail buffer. Used when ak_objAlloc was invoked\n   with sizeof(SomeStruct). */\n#define ak_objGetOrNull(OBJ) (OBJ ? (OBJ)->pData : NULL)\n#define ak_objGet(OBJ)       ((OBJ)->pData)\n\n/* Pointer-storage access — when ak_objAlloc was invoked with\n   sizeof(SomePtr) the inline payload is one pointer-sized slot holding a\n   reference to an external object (the referenced object owns its own\n   lifetime). ak_objGetTarget dereferences once to recover that pointer. */\n#define ak_objGetTarget(OBJ) (*(void **)((OBJ)->pData))\n\n#define ak_allocator ak_mem_allocator()\n\ntypedef struct AkHeapAllocator {\n  void  *(*malloc)(size_t);\n  void  *(*calloc)(size_t, size_t);\n  void *(*realloc)(void *, size_t);\n  int  (*memalign)(void **, size_t, size_t);\n  char  *(*strdup)(const char *);\n  void     (*free)(void *);\n  size_t   (*size)(const void *);\n} AkHeapAllocator;\n\ntypedef struct AkHeapSrchCtx AkHeapSrchCtx;\ntypedef struct AkHeapNode    AkHeapNode;\ntypedef struct AkHeap        AkHeap;\nstruct AkURL;\n\ntypedef int (*AkHeapSrchCmpFn)(void * __restrict key1,\n                               void * __restrict key2);\n\ntypedef void (*AkHeapSrchPrintFn)(void * __restrict key);\n\ntypedef enum AkHeapFlags {\n  AK_HEAP_FLAGS_NONE        = 0,\n  AK_HEAP_FLAGS_INITIALIZED = 1 << 0,\n  AK_HEAP_FLAGS_DYNAMIC     = 1 << 1,\n} AkHeapFlags;\n\ntypedef enum AkHeapNodeFlags {\n  AK_HEAP_NODE_FLAGS_NONE      = 0,       /* plain node                      */\n  AK_HEAP_NODE_FLAGS_HEAP_CHLD = 1 << 0,  /* node has attached heaps         */\n  AK_HEAP_NODE_FLAGS_EXT       = 1 << 1,  /* node has one of:                */\n  AK_HEAP_NODE_FLAGS_SID_CHLD  = 1 << 2,  /* least one of children has sid   */\n  AK_HEAP_NODE_FLAGS_RED       = 1 << 3,  /* RBtree color bit                */\n  AK_HEAP_NODE_FLAGS_SRCH      = 1 << 4,  /* node has an id                  */\n  AK_HEAP_NODE_FLAGS_SID       = 1 << 5,  /* memory node or its attr has sid */\n  AK_HEAP_NODE_FLAGS_REFC      = 1 << 6,  /* node is reference counted       */\n  AK_HEAP_NODE_FLAGS_EXTRA     = 1 << 7,  /* node has <extra> element        */\n  AK_HEAP_NODE_FLAGS_INF       = 1 << 8,  /* node has <asset> element        */\n  AK_HEAP_NODE_FLAGS_URL       = 1 << 9,  /* node has retained mem via url   */\n  AK_HEAP_NODE_FLAGS_USR       = 1 << 10, /* user data                       */\n  AK_HEAP_NODE_FLAGS_USRF      = 1 << 11, /* user data must be freed         */\n  AK_HEAP_NODE_FLAGS_MMAP      = 1 << 12, /* attached mmap-ed memory list    */\n  AK_HEAP_NODE_FLAGS_SID_NODE  = AK_HEAP_NODE_FLAGS_SID,\n  AK_HEAP_NODE_FLAGS_EXT_ALL   = AK_HEAP_NODE_FLAGS_EXT\n                               | AK_HEAP_NODE_FLAGS_SRCH\n                               | AK_HEAP_NODE_FLAGS_SID\n                               | AK_HEAP_NODE_FLAGS_REFC\n                               | AK_HEAP_NODE_FLAGS_EXTRA\n                               | AK_HEAP_NODE_FLAGS_INF\n                               | AK_HEAP_NODE_FLAGS_URL\n                               | AK_HEAP_NODE_FLAGS_USR\n                               | AK_HEAP_NODE_FLAGS_USRF\n                               | AK_HEAP_NODE_FLAGS_MMAP,\n  AK_HEAP_NODE_FLAGS_EXT_FRST = AK_HEAP_NODE_FLAGS_SRCH\n} AkHeapNodeFlags;\n\nAK_EXPORT\nAkHeapAllocator *\nak_heap_allocator(AkHeap * __restrict heap);\n\nAK_EXPORT\nAkHeap *\nak_heap_getheap(void * __restrict memptr);\n\nAK_EXPORT\nAkHeap *\nak_heap_default(void);\n\nAK_EXPORT\nAkHeap *\nak_heap_new(AkHeapAllocator *allocator,\n            AkHeapSrchCmpFn cmp,\n            AkHeapSrchPrintFn print);\n\nAK_EXPORT\nvoid\nak_heap_attach(AkHeap * __restrict parent,\n               AkHeap * __restrict chld);\n\nAK_EXPORT\nvoid\nak_heap_dettach(AkHeap * __restrict parent,\n                AkHeap * __restrict chld);\n\nAK_EXPORT\nvoid\nak_heap_setdata(AkHeap * __restrict heap,\n                void   * __restrict memptr);\n\nAK_EXPORT\nvoid*\nak_heap_data(AkHeap * __restrict heap);\n\nAK_EXPORT\nvoid\nak_heap_init(AkHeap          * __restrict heap,\n             AkHeapAllocator * __restrict allocator,\n             AkHeapSrchCmpFn              cmp,\n             AkHeapSrchPrintFn            print);\n\nAK_EXPORT\nvoid\nak_heap_destroy(AkHeap * __restrict heap);\n\nAK_EXPORT\nchar*\nak_heap_strdup(AkHeap * __restrict heap,\n               void * __restrict parent,\n               const char * str);\n\nAK_EXPORT\nchar*\nak_heap_strndup(AkHeap * __restrict heap,\n                void   * __restrict parent,\n                const char * str,\n                size_t       size);\n\nAK_EXPORT\nvoid*\nak_heap_alloc(AkHeap * __restrict heap,\n              void   * __restrict parent,\n              size_t size);\n\nAK_EXPORT\nvoid*\nak_heap_calloc(AkHeap * __restrict heap,\n               void   * __restrict parent,\n               size_t size);\n\nAK_EXPORT\nvoid*\nak_heap_realloc(AkHeap * __restrict heap,\n                void   * __restrict parent,\n                void   * __restrict memptr,\n                size_t newsize);\n\nAK_EXPORT\nvoid *\nak_heap_chld(AkHeapNode *heapNode);\n\nAK_EXPORT\nvoid\nak_heap_chld_set(AkHeapNode * __restrict heapNode,\n                 AkHeapNode * __restrict chldNode);\n\nAK_EXPORT\nAkHeapNode *\nak_heap_parent(AkHeapNode *heapNode);\n\nAK_EXPORT\nvoid\nak_heap_setp(AkHeapNode * __restrict heapNode,\n             AkHeapNode * __restrict newParent);\n\nAK_EXPORT\nvoid\nak_heap_moveh(AkHeapNode * __restrict heapNode,\n              AkHeap     * __restrict newheap);\n\nAK_EXPORT\nvoid\nak_heap_setpm(void   * __restrict memptr,\n              void   * __restrict newparent);\n\nAK_EXPORT\nvoid\nak_heap_free(AkHeap     * __restrict heap,\n             AkHeapNode * __restrict heapNode);\n\nAK_EXPORT\nvoid\nak_heap_cleanup(AkHeap * __restrict heap);\n\nAK_EXPORT\nvoid *\nak_heap_getId(AkHeap     * __restrict heap,\n              AkHeapNode * __restrict heapNode);\n\nAK_EXPORT\nvoid\nak_heap_setId(AkHeap     * __restrict heap,\n              AkHeapNode * __restrict heapNode,\n              void       * __restrict memId);\n\nAK_EXPORT\nAkResult\nak_heap_getNodeById(AkHeap      * __restrict heap,\n                    void        * __restrict memId,\n                    AkHeapNode ** __restrict dest);\n\nAK_EXPORT\nAkResult\nak_heap_getNodeByURL(AkHeap       * __restrict heap,\n                     struct AkURL * __restrict url,\n                     AkHeapNode  ** __restrict dest);\n\nAK_EXPORT\nAkResult\nak_heap_getMemById(AkHeap * __restrict heap,\n                   void   * __restrict memId,\n                   void  ** __restrict dest);\n\nAK_EXPORT\nvoid*\nak_heap_setUserData(AkHeap * __restrict heap,\n                    void   * __restrict mem,\n                    void   * __restrict userData);\n\nAK_EXPORT\nint\nak_heap_refc(AkHeapNode * __restrict heapNode);\n\nAK_EXPORT\nint\nak_heap_retain(AkHeapNode * __restrict heapNode);\n\nAK_EXPORT\nvoid\nak_heap_release(AkHeapNode * __restrict heapNode);\n\nAK_EXPORT\nvoid\nak_heap_printKeys(AkHeap * __restrict heap);\n\n/* default heap helpers */\n\nAK_EXPORT\nAkHeap*\nak_attachedHeap(void * __restrict memptr);\n\nAK_EXPORT\nvoid\nak_setAttachedHeap(void   * __restrict memptr,\n                   AkHeap * __restrict heap);\n\nAK_EXPORT\nAkHeapAllocator *\nak_mem_allocator(void);\n\nAK_EXPORT\nvoid\nak_mem_printKeys(void);\n\nAK_EXPORT\nvoid*\nak_malloc(void * __restrict parent,\n          size_t size);\n\nAK_EXPORT\nvoid*\nak_calloc(void * __restrict parent,\n          size_t size);\n\nAK_EXPORT\nchar*\nak_strdup(void * __restrict parent,\n          const char * __restrict str);\n\nAK_EXPORT\nvoid*\nak_realloc(void * __restrict parent,\n           void * __restrict memptr,\n           size_t newsize);\n\nAK_EXPORT\nvoid\nak_mem_setp(void * __restrict memptr,\n            void * __restrict newparent);\n\nAK_EXPORT\nvoid *\nak_mem_parent(void *mem);\n\nAK_EXPORT\nvoid\nak_free(void * __restrict memptr);\n\nAK_EXPORT\nvoid *\nak_mem_getId(void * __restrict memptr);\n\nAK_EXPORT\nvoid\nak_mem_setId(void * __restrict memptr,\n             void * __restrict memId);\n\nAK_EXPORT\nAkResult\nak_mem_getMemById(void * __restrict ctx,\n                  void * __restrict memId,\n                  void ** __restrict dest);\n\nAK_EXPORT\nint\nak_refc(void * __restrict mem);\n\nAK_EXPORT\nint\nak_retain(void * __restrict mem);\n\nAK_EXPORT\nvoid\nak_release(void * __restrict mem);\n\n/* mem wrapper helpers */\n\nAK_EXPORT\nAkObject*\nak_objAlloc(AkHeap * __restrict heap,\n            void * __restrict memParent,\n            size_t typeSize,\n            AkEnum typeEnum,\n            bool zeroed);\n\nAK_EXPORT\nvoid*\nak_userData(void * __restrict mem);\n\nAK_EXPORT\nvoid*\nak_setUserData(void * __restrict mem, void * __restrict userData);\n\nAK_EXPORT\nAkObject*\nak_objFrom(void * __restrict memptr);\n\nAK_EXPORT\nvoid*\nak_mmap_rdonly(int fd, size_t size);\n\nAK_EXPORT\nvoid\nak_unmap(void *file, size_t size);\n\nAK_EXPORT\nvoid\nak_mmap_attach(void * __restrict obj, void * __restrict mapped, size_t sized);\n\nAK_EXPORT\nvoid\nak_unmmap_attached(void * __restrict obj);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* assetkit_memory_h */\n"
  },
  {
    "path": "include/ak/node.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_node_h\n#define assetkit_node_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n\nstruct AkInstanceMorph;\nstruct AkAccessor;\n\ntypedef enum AkNodeFlags {\n  AK_NODEF_FIXED_COORD = 1\n} AkNodeFlags;\n\n/*!\n * @brief Per-instance TRS accessors for EXT_mesh_gpu_instancing.\n */\ntypedef struct AkInstanceAttribs {\n  struct AkAccessor *translation;  /* optional, vec3 x count */\n  struct AkAccessor *rotation;     /* optional, vec4 x count (quaternion) */\n  struct AkAccessor *scale;        /* optional, vec3 x count */\n  uint32_t           count;        /* number of instances */\n} AkInstanceAttribs;\n\ntypedef enum AkNodeType {\n  AK_NODE_TYPE_NODE        = 1,\n  AK_NODE_TYPE_CAMERA_NODE = 2,\n  AK_NODE_TYPE_JOINT       = 3\n} AkNodeType;\n\ntypedef struct AkListIter {\n  void *prev;\n  void *next;\n} AkListIter;\n\ntypedef struct AkTreeWithParentIter {\n  void *prev;\n  void *next;\n  void *parent;\n  void *chld;\n} AkTreeWithParentIter;\n\ntypedef struct AkTreeIter {\n  void *prev;\n  void *next;\n  void *chld;\n} AkTreeIter;\n\ntypedef struct AkNode {\n  /* const char * id;  */\n  /* const char * sid; */\n\n  const char           *name;\n  AkNodeFlags           flags;\n  AkNodeType            nodeType;\n  AkStringArray        *layer;\n  struct AkTransform   *transform;\n  bool                  visible;\n\n  /* only avilable if library is forced to calculate them\n     check these two matrix to avoid extra or same calculation\n   */\n  struct AkMatrix      *matrix;\n  struct AkMatrix      *matrixWorld;\n  struct AkBoundingBox *bbox;\n  \n  AkInstanceGeometry   *geometry;\n  AkInstanceBase       *camera;\n  AkInstanceBase       *light;\n  AkInstanceNode       *node;\n\n  /* EXT_mesh_gpu_instancing, NULL if not authored. */\n  AkInstanceAttribs    *instancing;\n\n  AkTree               *extra;\n\n  struct AkNode        *prev;\n  struct AkNode        *next;\n  struct AkNode        *chld;\n  struct AkNode        *parent;\n} AkNode;\n\nAK_EXPORT\nvoid\nak_addSubNode(AkNode * __restrict parent,\n              AkNode * __restrict subnode,\n              bool                fixCoordSys);\n\n/*!\n * @brief Allocate a fresh AkNode in the document's heap.\n *\n * Optionally attaches the new node as a child of @p parent (via\n * ak_addSubNode without coord-sys fix-up — the caller is in control\n * of orientation). The node's name is duplicated into the heap if\n * @p name is non-NULL.\n *\n * @param[in]  doc     document the node will live in (required)\n * @param[in]  parent  parent node to attach as child of, or NULL\n *                     to leave the new node unparented\n * @param[in]  name    optional node name (deep-copied)\n *\n * @return Newly allocated AkNode, or NULL on allocation failure.\n */\nAK_EXPORT\nAkNode *\nak_nodeMake(AkDoc      * __restrict doc,\n            AkNode     * __restrict parent,\n            const char * name);\n\n/*!\n * @brief Find a direct child of @p parent whose name matches @p name.\n *\n * Linear scan over the immediate children chain. NULL parent or NULL\n * name returns NULL. Returns the first match — names aren't unique\n * by spec, so callers that care should walk the result chain\n * themselves.\n */\nAK_EXPORT\nAkNode *\nak_nodeFindChildByName(AkNode     * __restrict parent,\n                       const char * name);\n\n/*!\n * @brief Find a child by name, creating it under @p parent if missing.\n *\n * Convenience wrapper for the common \"ensure a designated container\n * node exists\" pattern (e.g. a \"User Cameras\" group). Equivalent to\n * ak_nodeFindChildByName followed by ak_nodeMake when nothing matches.\n */\nAK_EXPORT\nAkNode *\nak_nodeFindOrMakeChild(AkDoc      * __restrict doc,\n                       AkNode     * __restrict parent,\n                       const char * name);\n\n/*!\n * @brief Attach a camera to a node by creating a camera instance.\n *\n * Allocates an AkInstanceBase, sets type = AK_INSTANCE_CAMERA, and\n * chains it onto node->camera (preserving any existing camera\n * instance — multi-camera nodes are uncommon but legal).\n *\n * Does NOT register the camera in the cameras library — that's the\n * job of ak_camMakePerspective / ak_camMakeOrthographic. Pair them.\n *\n * @return The freshly allocated camera instance.\n */\nAK_EXPORT\nAkInstanceBase *\nak_nodeAttachCamera(AkNode   * __restrict node,\n                    AkCamera * __restrict cam);\n\n/*!\n * @brief Attach a light to a node by creating a light instance.\n *\n * Mirrors ak_nodeAttachCamera: allocates an AkInstanceBase with\n * type = AK_INSTANCE_LIGHT and chains it onto node->light. Pair\n * with ak_lightMake() which handles the lights library entry.\n *\n * @return The freshly allocated light instance.\n */\nAK_EXPORT\nAkInstanceBase *\nak_nodeAttachLight(AkNode  * __restrict node,\n                   AkLight * __restrict light);\n\n/*!\n * @brief Replace the node's transform with a single column-major 4×4\n *        matrix (AKT_MATRIX item).\n *\n * Allocates an `AkTransform` for the node if it didn't already have\n * one, then drops a fresh AKT_MATRIX-typed AkObject in its `item`\n * slot containing the supplied matrix. Any prior transform chain\n * (translate / rotate / scale items) is replaced — convenient for\n * runtime UI edits where the user works with a decomposed pose and\n * we serialize the composed result.\n *\n * @param[in]  node    target node (required)\n * @param[in]  matrix  16 floats in column-major order\n *                     (matches cglm / OpenGL / SCNMatrix4 layout)\n */\nAK_EXPORT\nvoid\nak_nodeSetTransformMatrix(AkNode * __restrict node,\n                          const float matrix[16]);\n\n/*!\n * @brief Find a root-level node in a visual scene by name.\n *\n * Visual scenes hold their roots as a sibling chain reachable via\n * `scene->node->next`. This walks that chain looking for a name\n * match. NULL inputs return NULL.\n */\nAK_EXPORT\nAkNode *\nak_sceneFindRoot(struct AkVisualScene * __restrict scene,\n                 const char * name);\n\n/*!\n * @brief Find a root-level node by name, or create one in @p scene.\n *\n * Convenience for \"ensure a top-level container exists\" — e.g. a\n * \"User Cameras\" group placed alongside the asset's authored roots.\n * Created nodes are appended to the end of the existing root chain\n * (memparent = scene), preserving the original asset ordering.\n */\nAK_EXPORT\nAkNode *\nak_sceneFindOrMakeRoot(AkDoc                * __restrict doc,\n                       struct AkVisualScene * __restrict scene,\n                       const char * name);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_node_h */\n"
  },
  {
    "path": "include/ak/options.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_options_h\n#define assetkit_options_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n\n/* TODO: */\ntypedef enum AkOption {\n  AK_OPT_INDICES_DEFAULT            = 0,  /* false    */\n  AK_OPT_INDICES_SINGLE_INTERLEAVED = 1,  /* false    */\n  AK_OPT_INDICES_SINGLE_SEPARATE    = 2,  /* false    */\n  AK_OPT_INDICES_SINGLE             = 3,  /* false    */\n  AK_OPT_NOINDEX_INTERLEAVED        = 4,  /* true     */\n  AK_OPT_NOINDEX_SEPARATE           = 5,  /* true     */\n  AK_OPT_COORD                      = 6,  /* Y_UP     */\n  AK_OPT_DEFAULT_ID_PREFIX          = 7,  /* id-      */\n  AK_OPT_COMPUTE_BBOX               = 8,  /* false    */\n  AK_OPT_TRIANGULATE                = 9,  /* true     */\n  AK_OPT_GEN_NORMALS_IF_NEEDED      = 10, /* true     */\n  AK_OPT_DEFAULT_PROFILE            = 11, /* COMMON   */\n  AK_OPT_EFFECT_PROFILE             = 12, /* true     */\n  AK_OPT_TECHNIQUE                  = 13, /* \"common\" */\n  AK_OPT_TECHNIQUE_FX               = 14, /* \"common\" */\n  AK_OPT_ZERO_INDEXED_INPUT         = 15, /* false    */\n  AK_OPT_IMAGE_LOAD_FLIP_VERTICALLY = 16, /* true     */\n  AK_OPT_ADD_DEFAULT_CAMERA         = 17, /* true     */\n  AK_OPT_ADD_DEFAULT_LIGHT          = 18, /* false    */\n  AK_OPT_COORD_CONVERT_TYPE         = 19, /* DEFAULT  */\n  AK_OPT_BUGFIXES                   = 20, /* TRUE     */\n  AK_OPT_COMPUTE_EXACT_CENTER       = 21, /* FALSE    */\n  AK_OPT_USE_MMAP                   = 22, /* TRUE     */\n\n  /* TODO: not implemented yet,\n     https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#mikktspace\n     https://github.com/mmikk/MikkTSpace \n   */\n  AK_OPT_GEN_TANGENTS_IF_NEEDED     = 23, /* true     */\n\n  /* if your engine doesnt support triangle strip, triangle fan,\n     let AssetKit convert them to TRIANGLES */\n  AK_OPT_CVT_TRIANGLESTRIP          = 24, /* false    */\n  AK_OPT_CVT_TRIANGLEFAN            = 25, /* false    */\n\n  /* if your engine doesnt support line loop, line strip,\n     let AssetKit convert them to LINES */\n  AK_OPT_CVT_LINELOOP               = 26, /* false    */\n  AK_OPT_CVT_LINESTRIP              = 27, /* false    */\n\n  /* Keep KHR_mesh_quantization accessors in their authored integer form. */\n  AK_OPT_PRESERVE_QUANTIZED_ATTRS   = 28, /* false    */\n\n  /* Optional glTF extension decoder settings. */\n  AK_OPT_GLTF_EXT_DECODER_AUTOLOAD   = 29, /* true     */\n  AK_OPT_GLTF_MESHOPT_DECODER_PATH   = 30, /* NULL     */\n  AK_OPT_GLTF_DRACO_DECODER_PATH     = 31, /* NULL     */\n  AK_OPT_GLTF_GSPLAT_DECODER_PATH    = 32, /* NULL     */\n  AK_OPT_GLTF_KTX2_DECODER_PATH      = 33, /* NULL     */\n} AkOption;\n\nAK_EXPORT\nvoid\nak_opt_set(AkOption option, uintptr_t value);\n\nAK_EXPORT\nuintptr_t\nak_opt_get(AkOption option);\n\nAK_EXPORT\nvoid\nak_opt_set_str(AkOption option, const char *value);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_options_h */\n"
  },
  {
    "path": "include/ak/path.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_path_h\n#define assetkit_path_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n#include <stdio.h>\n\nstruct AkHeap;\nstruct AkDoc;\n\nAK_EXPORT\nconst char *\nak_path_fragment(const char *path);\n\nAK_EXPORT\nsize_t\nak_path_trim(const char *path,\n             char *trimmed);\n\nAK_EXPORT\nint\nak_path_join(char   *fragments[],\n             char   *buf,\n             size_t *size);\n\nAK_EXPORT\nint\nak_path_isfile(const char *path);\n\nAK_EXPORT\nchar*\nak_path_dir(struct AkHeap * __restrict heap,\n            void          * __restrict memparent,\n            const char    * __restrict path);\n\nAK_EXPORT\nconst char*\nak_fullpath(struct AkDoc * __restrict doc,\n            const char   * __restrict ref,\n            char         * __restrict buf);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_path_h */\n"
  },
  {
    "path": "include/ak/profile.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_profile_h\n#define assetkit_profile_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n#include \"material.h\"\n\nstruct AkEffect;\n\ntypedef enum AkProfileType {\n  AK_PROFILE_TYPE_UNKOWN =-1,\n  AK_PROFILE_TYPE_COMMON = 0,\n  AK_PROFILE_TYPE_GLTF   = 6\n} AkProfileType;\n\ntypedef struct AkTechniqueFx {\n  /* const char * id; */\n  /* const char * sid; */\n  AkTechniqueFxCommon  *common;\n  AkTree               *extra;\n  struct AkTechniqueFx *next;\n} AkTechniqueFx;\n\ntypedef struct AkTechniqueOverride {\n  const char * ref;\n  const char * pass;\n} AkTechniqueOverride;\n\ntypedef struct AkTechniqueHint {\n  struct AkTechniqueHint *next;\n  const char             *platform;\n  const char             *ref;\n  const char             *profile;\n  AkProfileType           profileType;\n} AkTechniqueHint;\n\nstruct AkNewParam;\n\ntypedef struct AkProfile {\n  /* const char * id; */\n  AkProfileType     type;\n  struct AkNewParam       *newparam;\n  AkTechniqueFx    *technique;\n  AkTree           *extra;\n  struct AkProfile *next;\n} AkProfile;\n\ntypedef AkProfile AkProfileCommon;\ntypedef AkProfile AkProfileGLTF;\n\nAkProfile*\nak_profile(struct AkEffect * __restrict effect,\n           AkProfile       * __restrict after);\n\nAkProfileType\nak_profileType(struct AkEffect * __restrict effect);\n\nuint32_t\nak_supportedProfiles(AkProfileType ** profileTypes);\n\nvoid\nak_setSupportedProfiles(AkProfileType profileTypes[],\n                        uint32_t      count);\n\nconst char*\nak_platform(void);\n\nvoid\nak_setPlatform(const char platform[64]);\n\nAK_EXPORT\nAkProfileCommon*\nak_getProfileCommon(struct AkEffect * __restrict effect);\n\nAK_EXPORT\nAkTechniqueFxCommon*\nak_getProfileTechniqueCommon(struct AkEffect * __restrict effect);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_profile_h */\n"
  },
  {
    "path": "include/ak/sid.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * TODO: maybe there is better way to implement sid addressing?\n */\n\n#ifndef assetkit_sid_h\n#define assetkit_sid_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n#include \"context.h\"\n\nAK_EXPORT\nconst char *\nak_sid_get(void *memnode);\n\nAK_EXPORT\nconst char *\nak_sid_geta(void *memnode,\n            void *memptr);\n\nAK_EXPORT\nvoid\nak_sid_dup(void *newMemnode,\n           void *oldMemnode);\n\nAK_EXPORT\nvoid\nak_sid_set(void       *memnode,\n           const char * __restrict sid);\n\nAK_EXPORT\nvoid\nak_sid_seta(void       *memnode,\n            void       *memptr,\n            const char * __restrict sid);\n\nAK_EXPORT\nvoid *\nak_sid_resolve(AkContext   * __restrict ctx,\n               const char  * __restrict target,\n               const char ** __restrict attribString);\n\nAK_EXPORT\nvoid *\nak_sid_resolve_from(AkContext   * __restrict ctx,\n                    const char  * __restrict id,\n                    const char  * __restrict target,\n                    const char ** __restrict attribString);\n  \nAK_EXPORT\nvoid *\nak_sid_resolve_val(AkContext  * __restrict ctx,\n                   const char * __restrict target);\n\nAK_EXPORT\nuint32_t\nak_sid_attr_offset(const char *attr);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_sid_h */\n"
  },
  {
    "path": "include/ak/source.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_source_h\n#define assetkit_source_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n\n#include <stdint.h>\n#include <stdbool.h>\n#include \"type.h\"\n#include \"url.h\"\n#include \"core-types.h\"\n\n/*\n  Input -> Source -> TechniqueCommon (Accessor) -> Buffer\n  Input -> Accessor -> Buffer\n*/\n\nstruct AkTechnique;\nstruct AkBuffer;\n\n/* for vectors: item count,\n   for matrics: item count | matrix size\n*/\ntypedef enum AkComponentSize {\n  AK_COMPONENT_SIZE_UNKNOWN = 0,\n  AK_COMPONENT_SIZE_SCALAR  = 1,\n  AK_COMPONENT_SIZE_VEC2    = 2,\n  AK_COMPONENT_SIZE_VEC3    = 3,\n  AK_COMPONENT_SIZE_VEC4    = 4,\n  AK_COMPONENT_SIZE_MAT2    = (4  << 3) | 2,\n  AK_COMPONENT_SIZE_MAT3    = (9  << 3) | 3,\n  AK_COMPONENT_SIZE_MAT4    = (16 << 3) | 4\n} AkComponentSize;\n\ntypedef struct AkDataParam {\n  /* const char * sid; */\n\n  struct AkDataParam *next;\n  const char         *name;\n  const char         *semantic;\n  AkTypeDesc          type;\n} AkDataParam;\n\ntypedef struct AkBuffer {\n  const char *name;\n  void       *data;\n  size_t      length;\n} AkBuffer;\n\ntypedef struct AkAccessor {\n  struct AkBuffer *buffer;\n  const char      *name;\n  void            *min;\n  void            *max;\n  size_t           byteOffset;           /* byte offset on the buffer        */\n  size_t           byteStride;           /* stride in bytes                  */\n  size_t           byteLength;           /* total bytes for this accessor    */\n  uint32_t         count;                /* count to access buffer           */\n  uint32_t         bytesPerComponent;    /* component stride in bytes        */\n  AkComponentSize  componentSize;        /* vec1 | vec2 | vec3 | vec4 ...    */\n  AkTypeId         componentType;        /* single component type            */\n  uint32_t         componentCount;\n  size_t           fillByteSize;         /* filled size for single access    */\n  int32_t          gpuTarget;            /* GPU buffer target to bound       */\n  bool             normalized;\n\n  /* Source-side metadata preserved across dequantize. When AssetKit\n     widens a normalized integer / KHR_mesh_quantization integer\n     attribute to float, componentType becomes AKT_FLOAT and normalized\n     is cleared — but the original encoding is kept here so callers can\n     reason about the source format (and reconstruct the quantized\n     mapping if needed). When AK_OPT_PRESERVE_QUANTIZED_ATTRS is set\n     and the buffer is left as integers, originalComponentType ==\n     componentType and originallyNormalized == normalized. */\n  AkTypeId         originalComponentType;\n  bool             originallyNormalized;\n} AkAccessor;\n\ntypedef struct AkSource {\n  /* const char * id; */\n  const char         *name;\n  AkBuffer           *buffer;\n  AkAccessor         *tcommon;\n  \n  struct AkTechnique *technique;\n  struct AkSource    *next;\n  int32_t             target;\n} AkSource;\n\ntypedef struct AkDuplicatorRange {\n  struct AkDuplicatorRange *next;\n  AkUIntArray              *dupc;\n  AkUIntArray              *dupcsum;\n  size_t                    startIndex;\n  size_t                    endIndex;\n} AkDuplicatorRange;\n\ntypedef struct AkDuplicator {\n  AkDuplicatorRange *range;\n  void              *buffstate;\n  void              *vertices;\n  size_t             dupCount;\n  size_t             bufCount;\n} AkDuplicator;\n\ntypedef struct AkSourceBuffState {\n  AkDuplicator *duplicator;\n  void         *buff;\n  char         *url;\n  size_t        count;\n  uint32_t      stride;\n} AkSourceBuffState;\n\ntypedef struct AkSourceEditHelper {\n  struct AkSourceEditHelper *next;\n  AkAccessor                *oldsource;\n  AkAccessor                *source;\n} AkSourceEditHelper;\n\nAK_EXPORT\nAkBuffer*\nak_sourceDetachArray(AkAccessor * __restrict acc);\n\n/* Dequantize an accessor's source data into a caller-supplied float buffer.\n   Always writes (count * componentCount) floats; outCapacity must be at\n   least that many. Uses originalComponentType / originallyNormalized to\n   drive integer-to-float conversion (normalized integers divide by the\n   type max, non-normalized integers cast to float). Accessors that\n   already store floats are copied through unchanged.\n\n   This is the on-demand path callers reach for when AssetKit was asked to\n   keep the raw quantized buffer (AK_OPT_PRESERVE_QUANTIZED_ATTRS) but a\n   particular consumer wants floats for one accessor.\n\n   Returns the number of floats written (0 on error or zero count). */\nAK_EXPORT\nsize_t\nak_accessorAsFloat(AkAccessor * __restrict acc,\n                   float      * __restrict out,\n                   size_t                  outCapacity);\n\n/* In-place dequantize: replaces the accessor's buffer with a tightly-packed\n   float buffer, updates componentType / byteStride / fillByteSize /\n   normalized, and registers the new buffer on the owning doc. Idempotent —\n   accessors that are already AKT_FLOAT are left untouched.\n\n   originalComponentType / originallyNormalized are preserved so callers\n   can still recover the source-side encoding after the in-place widen. */\nAK_EXPORT\nvoid\nak_accessorMakeFloat(AkAccessor * __restrict acc);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_source_h */\n"
  },
  {
    "path": "include/ak/string.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_string_h\n#define assetkit_string_h\n\n#include \"common.h\"\n#include \"core-types.h\"\n\nAK_EXPORT\nconst char*\nak_strltrim_fast(const char * __restrict str);\n\n/*!\n * @brief util for count tokens before call strtok to save realloc calls\n *\n * @param[in]  buff string buffer\n * @param[in]  sep  separators use comma to use multiple e.g. \" \\t\\r\"\n * @param[out] len  non-separator char count\n *\n * @return returns word count\n */\nAK_EXPORT\nint\nak_strtok_count(char * __restrict buff,\n                char * __restrict sep,\n                size_t           *len);\n\nAK_EXPORT\nint\nak_strtok_count_fast(char * __restrict buff,\n                     size_t            srclen,\n                     size_t           *len);\n\n\nAK_EXPORT\nunsigned long\nak_strtof(char    * __restrict src,\n          size_t               srclen,\n          unsigned long        n,\n          AkFloat * __restrict dest);\n\nAK_EXPORT\nunsigned long\nak_strtof_line(char    * __restrict src,\n               size_t               srclen,\n               unsigned long        n,\n               AkFloat * __restrict dest);\n\nAK_EXPORT\nunsigned long\nak_strtod(char     * __restrict src,\n          size_t                srclen,\n          unsigned long         n,\n          AkDouble * __restrict dest);\n\nAK_EXPORT\nunsigned long\nak_strtoui(char    * __restrict src,\n           size_t               srclen,\n           unsigned long        n,\n           AkUInt  * __restrict dest);\n\nAK_EXPORT\nunsigned long\nak_strtoi(char    * __restrict src,\n          size_t               srclen,\n          unsigned long        n,\n          AkInt   * __restrict dest);\n\nAK_EXPORT\nunsigned long\nak_strtoi_line(char    * __restrict src,\n               size_t               srclen,\n               unsigned long        n,\n               AkInt   * __restrict dest);\n\nAK_EXPORT\nunsigned long\nak_strtob(char    * __restrict src,\n          size_t               srclen,\n          unsigned long        n,\n          AkBool  * __restrict dest);\n\nAK_EXPORT\nchar*\nak_tolower(char *str);\n\nAK_EXPORT\nchar*\nak_toupper(char *str);\n\n#endif /* assetkit_string_h */\n"
  },
  {
    "path": "include/ak/texture.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_texture_h\n#define assetkit_texture_h\n\n#include \"common.h\"\n#include \"image.h\"\n\ntypedef enum AkWrapMode {\n  AK_WRAP_MODE_WRAP        = 1,\n  AK_WRAP_MODE_MIRROR      = 2,\n  AK_WRAP_MODE_CLAMP       = 3,\n  AK_WRAP_MODE_BORDER      = 4,\n  AK_WRAP_MODE_MIRROR_ONCE = 5\n} AkWrapMode;\n\ntypedef enum AkMinFilter {\n  AK_MINFILTER_LINEAR       = 0,\n  AK_MINFILTER_NEAREST      = 1,\n  AK_MINFILTER_ANISOTROPIC  = 2,\n\n  AK_LINEAR_MIPMAP_NEAREST  = 2,\n  AK_LINEAR_MIPMAP_LINEAR   = 3,\n  AK_NEAREST_MIPMAP_NEAREST = 4,\n  AK_NEAREST_MIPMAP_LINEAR  = 5\n} AkMinFilter;\n\ntypedef enum AkMagFilter {\n  AK_MAGFILTER_LINEAR       = 0,\n  AK_MAGFILTER_NEAREST      = 1\n} AkMagFilter;\n\ntypedef enum AkMipFilter {\n  AK_MIPFILTER_LINEAR  = 0,\n  AK_MIPFILTER_NONE    = 1,\n  AK_MIPFILTER_NEAREST = 2\n} AkMipFilter;\n\ntypedef enum AkTextureColorSpace {\n  AK_TEXTURE_COLORSPACE_UNSPECIFIED = 0,\n  AK_TEXTURE_COLORSPACE_LINEAR      = 1,\n  AK_TEXTURE_COLORSPACE_SRGB        = 2\n} AkTextureColorSpace;\n\ntypedef enum AkTextureChannels {\n  AK_TEXTURE_CHANNEL_NONE = 0,\n  AK_TEXTURE_CHANNEL_R    = 1 << 0,\n  AK_TEXTURE_CHANNEL_G    = 1 << 1,\n  AK_TEXTURE_CHANNEL_B    = 1 << 2,\n  AK_TEXTURE_CHANNEL_A    = 1 << 3,\n  AK_TEXTURE_CHANNEL_RGB  = AK_TEXTURE_CHANNEL_R\n                            | AK_TEXTURE_CHANNEL_G\n                            | AK_TEXTURE_CHANNEL_B,\n  AK_TEXTURE_CHANNEL_RGBA = AK_TEXTURE_CHANNEL_RGB\n                            | AK_TEXTURE_CHANNEL_A,\n  AK_TEXTURE_CHANNEL_GB   = AK_TEXTURE_CHANNEL_G\n                            | AK_TEXTURE_CHANNEL_B\n} AkTextureChannels;\n\ntypedef struct AkSampler {\n  const char     *uniformName;\n  const char     *coordInputName;\n  AkColor        *borderColor;\n//  AkInstanceBase *instanceImage;\n  AkTree         *extra;\n  const char     *name;\n\n  AkWrapMode      wrapS;\n  AkWrapMode      wrapT;\n  AkWrapMode      wrapP;\n\n  AkMinFilter     minfilter;\n  AkMagFilter     magfilter;\n  AkMipFilter     mipfilter;\n\n  uint32_t        maxAnisotropy;\n  uint32_t        mipMaxLevel;\n  uint32_t        mipMinLevel;\n  float           mipBias;\n} AkSampler;\n\ntypedef struct AkTexture {\n  struct AkTexture *next;\n  AkImage          *image;\n  AkSampler        *sampler;\n  const char       *name;\n  AkTypeId          type;\n} AkTexture;\n\ntypedef struct AkTextureTransform {\n  AkFloat2    offset;\n  float       rotation;\n  AkFloat2    scale;\n  int         slot;\n  const char *coordInputName;\n} AkTextureTransform;\n\ntypedef struct AkTextureRef {\n  struct AkTexture   *texture;\n\n  /* to bind texture to input coord dynamically, e.g. used by bindMaterial in \n     node like COLLADA  */\n  const char         *texcoord;\n\n  /* glTF like texture bind */\n  const char         *coordInputName;\n  int                 slot;\n\n  AkTextureColorSpace colorSpace;\n  AkTextureChannels   channels;\n\n  /* Texture Transform */\n  AkTextureTransform *transform;\n} AkTextureRef;\n\nAK_INLINE\nAkTextureRef*\nak_texref_usage(AkTextureRef        *texref,\n                AkTextureColorSpace  colorSpace,\n                AkTextureChannels    channels) {\n  if (texref) {\n    texref->colorSpace = colorSpace;\n    texref->channels   = channels;\n  }\n  return texref;\n}\n\n#ifdef __cglm__\nAK_INLINE\nmat4s\nak_textrans_mat4(AkTextureTransform *transform) {\n  return glms_mat4_(textrans)(transform->scale[0],\n                              transform->scale[1],\n                              transform->rotation,\n                              transform->offset[0],\n                              transform->offset[1]);\n}\n#endif\n\n#endif /* assetkit_texture_h */\n"
  },
  {
    "path": "include/ak/transform.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_transform_h\n#define assetkit_transform_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n\ntypedef struct AkTransform {\n  AkObject *base; /* fixup transform */\n  AkObject *item;\n} AkTransform;\n\ntypedef struct AkLookAt {\n  /* const char * sid; */\n\n  AkFloat3     val[3];\n} AkLookAt;\n\ntypedef struct AkMatrix {\n  /* const char * sid; */\n\n  AK_ALIGN(16) AkFloat val[4][4];\n} AkMatrix;\n\ntypedef struct AkRotate {\n  /* const char * sid; */\n  AK_ALIGN(16) AkFloat val[4];\n} AkRotate;\n\ntypedef struct AkScale {\n  /* const char * sid; */\n  AkFloat      val[3];\n} AkScale;\n\ntypedef struct AkSkew {\n  /* const char * sid; */\n  AkFloat      angle;\n  AkFloat3     rotateAxis;\n  AkFloat3     aroundAxis;\n} AkSkew;\n\ntypedef struct AkTranslate {\n  /* const char * sid; */\n  AkFloat      val[3];\n} AkTranslate;\n\ntypedef struct AkQuaternion {\n  AK_ALIGN(16) AkFloat val[4];\n} AkQuaternion;\n\n/*!\n * @brief build skew matrix from AkSkew\n *\n * @param skew   skew element\n * @param matrix skew matrix (must be aligned 16)\n */\nAK_EXPORT\nvoid\nak_transformSkewMatrix(AkSkew * __restrict skew,\n                       float  * matrix);\n\n/*!\n * @brief combines all node's transform elements\n *\n * if there is no transform then this returns identity matrix\n *\n * @param transform transform\n * @param matrix    combined transform (must be aligned 16)\n */\nAK_EXPORT\nvoid\nak_transformCombine(AkTransform * __restrict transform,\n                    float       * __restrict matrix);\n\n/*!\n * @brief combines all node's transform elements in **world coord sys**\n *\n * this func walks/traverses to top node to get world coord\n * this is not cheap op, obviously.\n *\n * @param node   node\n * @param matrix combined transform (must be aligned 16)\n */\nAK_EXPORT\nvoid\nak_transformCombineWorld(AkNode * __restrict node,\n                         float  * matrix);\n\n/*!\n * @brief duplicate all transforms of node1 to node2\n * \n * @warning duplicated transform will alloc extra memory\n */\nAK_EXPORT\nvoid\nak_transformDup(AkNode * __restrict srcNode,\n                AkNode * __restrict destNode);\n\nAK_EXPORT\nvoid\nak_transformFreeBase(AkTransform * __restrict transform);\n\nAK_EXPORT\nAkObject*\nak_getTransformTRS(AkNode *node, AkTypeId transformType);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_transform_h */\n"
  },
  {
    "path": "include/ak/trash.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_trash_h\n#define assetkit_trash_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n\nAK_EXPORT\nvoid ak_trash_add(void *mem);\n\nAK_EXPORT\nvoid\nak_trash_empty(void);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_trash_h */\n"
  },
  {
    "path": "include/ak/type.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_types_h\n#define assetkit_types_h\n\n#include \"common.h\"\n#include <stdbool.h>\n\ntypedef enum AkTypeId {\n  AKT_UNKNOWN       =-1,\n  AKT_NONE          = 0,\n  AKT_CUSTOM        = 0,\n\n  AKT_OBJECT        = 1,\n\n  AKT_BOOL          = 2,\n  AKT_BOOL2         = 3,\n  AKT_BOOL3         = 4,\n  AKT_BOOL4         = 5,\n  AKT_INT           = 6,\n  AKT_INT2          = 7,\n  AKT_INT3          = 8,\n  AKT_INT4          = 9,\n  AKT_FLOAT         = 10,\n  AKT_FLOAT2        = 11,\n  AKT_FLOAT3        = 12,\n  AKT_FLOAT4        = 13,\n  AKT_FLOAT2x2      = 14,\n  AKT_FLOAT3x3      = 15,\n  AKT_FLOAT4x4      = 16,\n  AKT_STRING        = 17,\n\n  AKT_SAMPLER1D     = 18,\n  AKT_SAMPLER2D     = 19,\n  AKT_SAMPLER3D     = 20,\n  AKT_SAMPLER_CUBE  = 21,\n  AKT_SAMPLER_RECT  = 22,\n  AKT_SAMPLER_DEPTH = 23,\n\n  AKT_IDREF         = 24,\n  AKT_NAME          = 25,\n  AKT_SIDREF        = 26,\n  AKT_TOKEN         = 27,\n\n  AKT_UINT          = 28,\n  AKT_BYTE          = 29,\n  AKT_UBYTE         = 30,\n  AKT_SHORT         = 31,\n  AKT_USHORT        = 32,\n  AKT_DOUBLE        = 33,\n  AKT_INT64         = 34,\n  AKT_UINT64        = 35,\n\n  AKT_EFFECT,\n  AKT_PROFILE,\n  AKT_PARAM,\n  AKT_NEWPARAM,\n  AKT_SETPARAM,\n  AKT_TECHNIQUE_FX,\n  AKT_TECHNIQUE,\n  AKT_SAMPLER,\n  AKT_TEXTURE,\n  AKT_TEXTURE_REF,\n  AKT_TEXTURE_NAME,\n  AKT_TEXCOORD,\n  AKT_NODE,\n  AKT_SCENE,\n  AKT_SOURCE,\n  AKT_ACCESSOR,\n  AKT_BUFFER,\n  AKT_GEOMETRY,\n  AKT_MESH,\n  AKT_CONTROLLER,\n  AKT_SKIN,\n  AKT_MORPH,\n  AKT_MORPH_INST,\n  AKT_RESOLVED_TARGET,\n\n  AKT_LOOKAT,\n  AKT_TRANSLATE,\n  AKT_ROTATE,\n  AKT_SCALE,\n  AKT_SKEW,\n  AKT_MATRIX,\n  AKT_QUATERNION\n} AkTypeId;\n\ntypedef struct AkTypeDesc {\n  const char *typeName;\n  AkTypeId    typeId;\n  int         size;\n  int         userData;\n} AkTypeDesc;\n\nAK_EXPORT\nAkTypeId\nak_typeid(void * __restrict mem);\n\nAK_EXPORT\nAkTypeId\nak_typeidh(AkHeapNode * __restrict hnode);\n\nAK_EXPORT\nvoid\nak_setypeid(void * __restrict mem,\n            AkTypeId tid);\n\nAK_EXPORT\nbool\nak_isKindOf(void * __restrict mem,\n            void * __restrict other);\n\nAK_EXPORT\nAkTypeDesc*\nak_typeDesc(AkTypeId typeId);\n\nAK_EXPORT\nAkTypeDesc*\nak_typeDescByName(const char * __restrict name);\n\nAK_EXPORT\nvoid\nak_registerType(AkTypeId typeId, AkTypeDesc *desc);\n\n#endif /* assetkit_types_h */\n"
  },
  {
    "path": "include/ak/url.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_url_h\n#define assetkit_url_h\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"common.h\"\n\ntypedef struct AkURL {\n  const char   *url;      /* only fragment */\n  struct AkDoc *doc;      /* document      */\n  void         *ptr;      /* direct link   */\n  void         *reserved; /* private       */\n} AkURL;\n\nvoid\nak_url_init(void  *parent,\n            char  *urlstring,\n            AkURL *dest);\n\nvoid\nak_url_dup(AkURL *src,\n           void  *parent,\n           AkURL *dest);\n\nvoid\nak_url_init_with_id(AkHeapAllocator *alc,\n                    void            *parent,\n                    char            *idstirng,\n                    AkURL           *dest);\n\nchar *\nak_url_string(AkHeapAllocator *alc, char *id);\n\nvoid\nak_url_ref(AkURL *url);\n\nvoid\nak_url_unref(AkURL *url);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* assetkit_url_h */\n"
  },
  {
    "path": "include/ak/util.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_util_h\n#define assetkit_util_h\n\n#include \"common.h\"\n\n/* pre-defined compare funcs */\n\nAK_EXPORT\nint\nak_cmp_str(void *key1, void *key2);\n\nAK_EXPORT\nint\nak_cmp_ptr(void *key1, void *key2);\n\nAK_EXPORT\nint\nak_digitsize(size_t number);\n\n#endif /* assetkit_util_h */\n"
  },
  {
    "path": "include/ak/version.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef assetkit_version_h\n#define assetkit_version_h\n\n#define AK_VERSION_MAJOR 0\n#define AK_VERSION_MINOR 3\n#define AK_VERSION_PATCH 2\n\n#endif /* assetkit_version_h */\n"
  },
  {
    "path": "scripts/strpool.py",
    "content": "#!/usr/bin/python3\n#\n# Copyright (C) 2020 Recep Aslantas\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nfrom os.path import realpath\nfrom os.path import dirname\n\nbasedir = dirname(realpath(__file__)) + '/../'\n\nstrpools = [\n  'src/strpool.py',\n  'src/io/dae/strpool.py',\n  'src/io/gltf/strpool.py'\n]\n\nfor sp in strpools:\n  __file__ = basedir + sp\n  exec(open(__file__).read())\n"
  },
  {
    "path": "src/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n\nadd_subdirectory(anim)\nadd_subdirectory(bbox)\nadd_subdirectory(bitwise)\nadd_subdirectory(camera)\nadd_subdirectory(coord)\nadd_subdirectory(default)\nadd_subdirectory(geom)\nadd_subdirectory(image)\nadd_subdirectory(instance)\nadd_subdirectory(io)\nadd_subdirectory(lib)\nadd_subdirectory(light)\nadd_subdirectory(mat)\nadd_subdirectory(mem)\nadd_subdirectory(mesh)\nadd_subdirectory(morph)\nadd_subdirectory(node)\nadd_subdirectory(platform)\nadd_subdirectory(resc)\nadd_subdirectory(skin)\nadd_subdirectory(topo)\nadd_subdirectory(transform)\nadd_subdirectory(miniz)\n\nif(MSVC OR MSYS OR MINGW)\n  add_subdirectory(win32)\nendif()\n"
  },
  {
    "path": "src/accessor.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"accessor.h\"\n#include \"default/semantic.h\"\n#include <assert.h>\n#include <stdint.h>\n\nAkAccessor*\nak_accessor_dup(AkAccessor *oldacc) {\n  AkHeap      *heap;\n  AkAccessor  *acc;\n\n  heap = ak_heap_getheap(oldacc);\n  acc  = ak_heap_alloc(heap, ak_mem_parent(oldacc), sizeof(*acc));\n\n  memcpy(acc, oldacc, sizeof(*oldacc));\n  ak_setypeid(acc, AKT_ACCESSOR);\n\n  return acc;\n}\n\n/* Read one component from a quantized source pointer as float. The\n   componentType describes the bytes actually under `src`, and `normalized`\n   selects between divide-by-typemax (true) and plain cast (false). Used by\n   both ak_accessorAsFloat and ak_accessorMakeFloat. */\nAK_INLINE\nfloat\nak_accessor_componentToFloat(const void * __restrict src,\n                             AkTypeId                componentType,\n                             bool                    normalized) {\n  switch (componentType) {\n    case AKT_FLOAT:\n      return *(const float *)src;\n    case AKT_BYTE: {\n      float f = (float)(*(const int8_t *)src);\n      if (normalized) {\n        f /= 127.0f;\n        if (f < -1.0f) f = -1.0f;\n      }\n      return f;\n    }\n    case AKT_UBYTE: {\n      float f = (float)(*(const uint8_t *)src);\n      if (normalized) f /= 255.0f;\n      return f;\n    }\n    case AKT_SHORT: {\n      float f = (float)(*(const int16_t *)src);\n      if (normalized) {\n        f /= 32767.0f;\n        if (f < -1.0f) f = -1.0f;\n      }\n      return f;\n    }\n    case AKT_USHORT: {\n      float f = (float)(*(const uint16_t *)src);\n      if (normalized) f /= 65535.0f;\n      return f;\n    }\n    case AKT_INT:\n      return (float)(*(const int32_t *)src);\n    case AKT_UINT:\n      return (float)(*(const uint32_t *)src);\n    default:\n      return 0.0f;\n  }\n}\n\nAK_EXPORT\nsize_t\nak_accessorAsFloat(AkAccessor * __restrict acc,\n                   float      * __restrict out,\n                   size_t                  outCapacity) {\n  size_t   needed, perItemBytes;\n  uint32_t comps, perComponentBytes, v, c;\n  char    *src;\n  AkTypeId srcType;\n  bool     srcNorm;\n\n  if (!acc || !out\n      || !acc->buffer || !acc->buffer->data\n      || acc->count == 0)\n    return 0;\n\n  comps  = acc->componentCount;\n  needed = (size_t)comps * acc->count;\n  if (outCapacity < needed) return 0;\n\n  /* Read what's actually in the buffer. When AssetKit dequantized at\n     parse time, componentType is AKT_FLOAT and we just copy through;\n     when AK_OPT_PRESERVE_QUANTIZED_ATTRS kept the integers, componentType\n     still matches the source encoding. originalComponentType is for\n     callers who want to know the source format regardless. */\n  srcType           = acc->componentType;\n  srcNorm           = acc->normalized;\n  perComponentBytes = acc->bytesPerComponent;\n  perItemBytes      = acc->byteStride\n                       ? acc->byteStride\n                       : (size_t)comps * perComponentBytes;\n  src               = (char *)acc->buffer->data + acc->byteOffset;\n\n  for (v = 0; v < acc->count; v++) {\n    char *vsrc = src + (size_t)v * perItemBytes;\n    for (c = 0; c < comps; c++) {\n      const void *cp = vsrc + (size_t)c * perComponentBytes;\n      out[(size_t)v * comps + c]\n        = ak_accessor_componentToFloat(cp, srcType, srcNorm);\n    }\n  }\n\n  return needed;\n}\n\nAK_EXPORT\nvoid\nak_accessorMakeFloat(AkAccessor * __restrict acc) {\n  AkHeap   *heap;\n  AkBuffer *fbuf;\n  size_t    floatBufSize;\n  uint32_t  comps;\n\n  if (!acc\n      || acc->componentType == AKT_FLOAT\n      || !acc->buffer || !acc->buffer->data\n      || acc->count == 0\n      || acc->fillByteSize == 0)\n    return;\n\n  heap         = ak_heap_getheap(acc);\n  comps        = acc->componentCount;\n  floatBufSize = (size_t)comps * acc->count * sizeof(float);\n\n  /* Parent the new buffer on the accessor itself so its lifetime tracks\n     the accessor. The original quantized buffer keeps its existing\n     parent linkage, so callers that retained a reference (e.g. an\n     external GPU upload using the raw bytes) are unaffected. */\n  fbuf         = ak_heap_calloc(heap, acc, sizeof(*fbuf));\n  fbuf->data   = ak_heap_alloc(heap, fbuf, floatBufSize);\n  fbuf->length = floatBufSize;\n\n  if (!ak_accessorAsFloat(acc, fbuf->data, (size_t)comps * acc->count))\n    return;\n\n  acc->buffer            = fbuf;\n  acc->byteOffset        = 0;\n  acc->bytesPerComponent = sizeof(float);\n  acc->fillByteSize      = (size_t)comps * sizeof(float);\n  acc->byteStride        = acc->fillByteSize;\n  acc->componentType     = AKT_FLOAT;\n  acc->normalized        = false;\n}\n"
  },
  {
    "path": "src/accessor.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_accessor_h\n#define ak_accessor_h\n\n#include \"common.h\"\n\nAkAccessor*\nak_accessor_dup(AkAccessor *oldacc);\n\nAK_INLINE\nbool\nak_areSimilarLayouts(AkAccessor *acc1, AkAccessor *acc2) {\n  return (acc1 == acc2) || (\n                            acc1->componentType     == acc2->componentType\n                            && acc1->count          == acc2->count\n                            && acc1->componentCount == acc2->componentCount\n                            && acc1->componentSize  == acc2->componentSize\n                            );\n}\n\n\n#endif /* ak_accessor_h */\n"
  },
  {
    "path": "src/anim/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME}\n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/anim/bake.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Animation baking — per-node transform sampling\n * ==============================================\n *\n * Maya's joint convention emits each joint with up to six independent\n * <rotate> elements (jointOrient{XYZ} + rotate{XYZ}). The full local\n * matrix is only correct after all six are composed in document order\n * (handled by ak_transformCombine). Decomposed-property animation APIs\n * (Apple SCNNode.eulerAngles, three.js Object3D.rotation, Filament\n * TransformManager) cannot represent that — they offer three Euler\n * slots, period.\n *\n * Solution: bake. Sample every channel that targets any AkObject in\n * the node's transform chain on a shared time grid, run\n * ak_transformCombine per sample, emit a stream of 4×4 local matrices.\n * Bridges then attach a single transform-keyed animation per node.\n *\n * Renderers that compose bone matrices CPU-side every frame\n * (assetkit-opengl/gk pattern) can skip the bake — they already iterate\n * channels per frame. The helper is opt-in; ak_nodeNeedsBaking() is the\n * heuristic.\n */\n\n#include \"../common.h\"\n#include <stdint.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <math.h>\n\n\n/* Cap mostly to keep the per-node fixed-size scratch arrays small.\n   A pathological node would have to authored ~64 transform elements\n   or ~256 channels — neither shows up in real assets. */\n#define BAKE_MAX_XFORM_OBJECTS 64\n#define BAKE_MAX_CHANNELS      256\n#define BAKE_MAX_ANIM_STACK    256\n\n/* Maximum time gap between adjacent samples in the baked output.\n   Reason: consumers (Apple SCNAnimation, three.js KeyframeTrack, ...)\n   linearly interpolate the 4×4 matrix elements between adjacent\n   samples. Linear lerp between rotation matrices is NOT slerp — for\n   large angular deltas (e.g. a propeller spinning 348° in 1 second\n   with only two authored keyframes) the lerp goes through the\n   *interior* of the rotation manifold, producing a near-identity\n   intermediate matrix and a visibly tiny wobble instead of a full\n   spin. Inserting subdivisions every BAKE_MAX_STEP seconds bounds the\n   per-pair angular delta so the lerp approximates a slerp closely\n   enough for typical playback.\n\n   30 Hz target (~33 ms). Fine balance: dense enough to handle 360°/s\n   rotations without artifacts (max ~12° between samples), sparse\n   enough to keep memory + decode cost low for long animations. */\n#define BAKE_MAX_STEP 0.0333f\n\n/* Per-channel binding: where to write the sampled value, and where to\n   read it from. */\ntypedef struct ChannelBind {\n  AkObject     *target;     /* AkObject wrapping AkRotate/AkTranslate/... */\n  uint32_t      off;        /* logical slot in target (0..3 typical)      */\n  bool          isPartial;  /* single component vs whole vector           */\n\n  const float  *keyTimes;\n  size_t        keyCount;\n  const float  *keyValues;  /* contiguous; valueStride floats per frame   */\n  size_t        valueStride;\n\n  AkInterpolationType interp;\n\n  float         saved[16];  /* snapshot of target->pData at function entry */\n  size_t        savedLen;   /* number of floats valid in saved[]           */\n} ChannelBind;\n\nstatic int\nbake_floatcmp(const void *a, const void *b) {\n  float fa = *(const float *)a;\n  float fb = *(const float *)b;\n  return (fa > fb) - (fa < fb);\n}\n\n/* Snapshot length per AkObject type — how many floats live in pData. */\nstatic size_t\nbake_payloadFloatCount(AkObject *obj) {\n  switch ((AkTypeId)obj->type) {\n    case AKT_TRANSLATE: return 3;   /* AkTranslate.val[3]   */\n    case AKT_SCALE:     return 3;   /* AkScale.val[3]       */\n    case AKT_ROTATE:    return 4;   /* AkRotate.val[4]      */\n    case AKT_QUATERNION:return 4;   /* AkQuaternion.val[4]  */\n    case AKT_MATRIX:    return 16;  /* AkMatrix.val[4][4]   */\n    case AKT_SKEW:      return 7;   /* angle + 2 vec3       */\n    default:            return 0;\n  }\n}\n\n/* Linear interpolation between adjacent keyframes. STEP honored.\n   For BEZIER/HERMITE we fall back to LINEAR — the resulting bake is\n   already keyframe-aligned so the caller's interpolation in the engine\n   layer can handle the curve refinement (LINEAR between keyframes is\n   indistinguishable from the original curve at sample times that\n   equal a keyframe). */\nstatic float\nbake_sampleScalar(const ChannelBind *b, size_t componentIdx, float t) {\n  size_t ki, valStride;\n  float  t0, t1, v0, v1, alpha;\n\n  if (b->keyCount == 0) return 0.0f;\n\n  /* find first key with time >= t */\n  for (ki = 0; ki < b->keyCount && b->keyTimes[ki] < t; ki++) { }\n\n  valStride = b->valueStride;\n  if (ki == 0) {\n    return b->keyValues[componentIdx];\n  }\n  if (ki >= b->keyCount) {\n    return b->keyValues[(b->keyCount - 1) * valStride + componentIdx];\n  }\n\n  v0 = b->keyValues[(ki - 1) * valStride + componentIdx];\n  v1 = b->keyValues[ki       * valStride + componentIdx];\n  if (b->interp == AK_INTERPOLATION_STEP) return v0;\n\n  t0    = b->keyTimes[ki - 1];\n  t1    = b->keyTimes[ki];\n  alpha = (t1 > t0) ? (t - t0) / (t1 - t0) : 0.0f;\n  return v0 * (1.0f - alpha) + v1 * alpha;\n}\n\nstatic void\nbake_collectChannels(AkAnimation  * __restrict anim,\n                     AkContext    * __restrict actx,\n                     AkObject    ** __restrict xformObjects,\n                     int                       nXform,\n                     ChannelBind  * __restrict binds,\n                     int          * __restrict nBinds) {\n  AkAnimation     *stack[BAKE_MAX_ANIM_STACK], *next;\n  AkChannel       *ch;\n  AkAnimSampler   *samp;\n  AkInput         *inp, *inputInput, *outputInput;\n  AkResolvedTarget rt;\n  int              j, top;\n  bool             isOurs;\n\n  top = 0;\n  while (anim && *nBinds < BAKE_MAX_CHANNELS) {\n    for (ch = anim->channel; ch && *nBinds < BAKE_MAX_CHANNELS; ch = ch->next) {\n      rt = ak_channelTarget(actx, ch);\n      if (!rt.target) continue;\n\n      isOurs = false;\n      for (j = 0; j < nXform; j++) {\n        if (rt.target == xformObjects[j]) { isOurs = true; break; }\n      }\n      if (!isOurs) continue;\n\n      samp = ak_getObjectByUrl(&ch->source);\n      if (!samp) continue;\n\n      inputInput = outputInput = NULL;\n      for (inp = samp->input; inp; inp = inp->next) {\n        if      (inp->semantic == AK_INPUT_INPUT  && !inputInput)  inputInput  = inp;\n        else if (inp->semantic == AK_INPUT_OUTPUT && !outputInput) outputInput = inp;\n      }\n\n      if (!inputInput  || !inputInput->accessor  || !inputInput->accessor->buffer\n          || !inputInput->accessor->buffer->data\n          || !outputInput || !outputInput->accessor || !outputInput->accessor->buffer\n          || !outputInput->accessor->buffer->data)\n        continue;\n\n      binds[*nBinds].target      = rt.target;\n      binds[*nBinds].off         = rt.off;\n      binds[*nBinds].isPartial   = rt.isPartial;\n      binds[*nBinds].keyTimes    = (const float *)\n        ((const char *)inputInput->accessor->buffer->data\n         + inputInput->accessor->byteOffset);\n      binds[*nBinds].keyCount    = inputInput->accessor->count;\n      binds[*nBinds].keyValues   = (const float *)\n        ((const char *)outputInput->accessor->buffer->data\n         + outputInput->accessor->byteOffset);\n      binds[*nBinds].valueStride = outputInput->accessor->componentCount;\n      binds[*nBinds].interp      = samp->uniInterpolation;\n      (*nBinds)++;\n    }\n\n    if (anim->animation) {\n      next = (AkAnimation *)anim->base.next;\n      if (next && top < BAKE_MAX_ANIM_STACK)\n        stack[top++] = next;\n      anim = anim->animation;\n    } else if (anim->base.next) {\n      anim = (AkAnimation *)anim->base.next;\n    } else if (top > 0) {\n      anim = stack[--top];\n    } else {\n      anim = NULL;\n    }\n  }\n}\n\nAK_EXPORT\nbool\nak_nodeNeedsBaking(AkNode * __restrict node) {\n  AkObject *it;\n  uint32_t  rotateCount;\n\n  if (!node || !node->transform) return false;\n  \n  rotateCount = 0;\n\n  for (it = node->transform->base; it; it = it->next) {\n    if ((AkTypeId)it->type == AKT_ROTATE && ++rotateCount > 1) return true;\n  }\n\n  for (it = node->transform->item; it; it = it->next) {\n    if ((AkTypeId)it->type == AKT_ROTATE && ++rotateCount > 1) return true;\n  }\n\n  return false;\n}\n\nAK_EXPORT\nAkBakedAnimation *\nak_nodeBakeAnimation(AkDoc  * __restrict doc,\n                     AkNode * __restrict node) {\n  AkLibrary        *animLib;\n  AkOneWayIterBase *animIt;\n  AkObject         *xformObjects[BAKE_MAX_XFORM_OBJECTS];\n  AkObject         *it;\n  AkBakedAnimation *out;\n  float            *allTimes, *dense;\n  ChannelBind       binds[BAKE_MAX_CHANNELS];\n  AkContext         actx;\n  size_t            totalTimes, uniqueCount, denseCount, subdivs, d, s;\n  size_t            i, k, t_idx;\n  float             t0, t1, gap;\n  int               nXform, nBinds, j;\n\n  if (!doc || !node || !node->transform) return NULL;\n\n  /* 1. Snapshot the AkObjects that make up the node's transform chain.\n        Order in the array doesn't matter for matching, only ak_transformCombine\n        cares about chain order — and that's read directly from node->transform\n        each sample. */\n  nXform = 0;\n  for (it = node->transform->base;\n       it && nXform < BAKE_MAX_XFORM_OBJECTS;\n       it = it->next) {\n    xformObjects[nXform++] = it;\n  }\n\n  for (it = node->transform->item;\n       it && nXform < BAKE_MAX_XFORM_OBJECTS;\n       it = it->next) {\n    xformObjects[nXform++] = it;\n  }\n\n  if (nXform == 0) return NULL;\n\n  /* 2. Walk every animation channel; bind those whose resolved target\n        is one of our transform AkObjects. */\n  memset(&actx, 0, sizeof(actx));\n\n  actx.doc = doc;\n  nBinds   = 0;\n\n  for (animLib = doc->lib.animations; animLib; animLib = animLib->next) {\n    for (animIt = animLib->chld; animIt; animIt = animIt->next) {\n      bake_collectChannels((AkAnimation *)animIt, &actx,\n                           xformObjects, nXform, binds, &nBinds);\n      if (nBinds >= BAKE_MAX_CHANNELS) break;\n    }\n  }\n\n  if (nBinds == 0) return NULL;\n\n  /* 3. Snapshot animated AkObject payloads so we can restore them on\n        exit. This lets the caller continue using node->transform for\n        bind-pose composition without observing the mutations we make\n        per sample. */\n  for (j = 0; j < nBinds; j++) {\n    binds[j].savedLen = bake_payloadFloatCount(binds[j].target);\n    if (binds[j].savedLen > 0)\n      memcpy(binds[j].saved, binds[j].target->pData,\n             binds[j].savedLen * sizeof(float));\n  }\n\n  /* 4. Build the union time grid: concat all channels' keyTimes, sort,\n        dedupe in place. Allocates total then shrinks via uniqueCount. */\n  totalTimes = 0;\n  for (j = 0; j < nBinds; j++) totalTimes += binds[j].keyCount;\n\n  if (totalTimes == 0) {\n    /* restore + bail */\n    for (j = 0; j < nBinds; j++) {\n      if (binds[j].savedLen > 0)\n        memcpy(binds[j].target->pData, binds[j].saved,\n               binds[j].savedLen * sizeof(float));\n    }\n    return NULL;\n  }\n\n  allTimes = ak_calloc(NULL, sizeof(float) * totalTimes);\n  k = 0;\n  for (j = 0; j < nBinds; j++) {\n    memcpy(allTimes + k, binds[j].keyTimes,\n           binds[j].keyCount * sizeof(float));\n    k += binds[j].keyCount;\n  }\n  qsort(allTimes, totalTimes, sizeof(float), bake_floatcmp);\n\n  uniqueCount = 0;\n  for (i = 0; i < totalTimes; i++) {\n    if (uniqueCount == 0 || allTimes[i] > allTimes[uniqueCount - 1]) {\n      allTimes[uniqueCount++] = allTimes[i];\n    }\n  }\n\n  /* 4b. Densify: insert subdivisions between adjacent samples whose\n        gap exceeds BAKE_MAX_STEP. Without this, sparse keyframes (e.g.\n        2 frames spanning a 348° rotation) would force the consumer to\n        linearly interpolate matrix elements across the gap — that\n        misses the rotation manifold and renders as a near-identity\n        wobble. Densifying bounds the per-pair angular delta.\n\n        Use ceil rather than floor: a 50 ms gap with 33.3 ms step\n        truncates to 1 subdivision (still 50 ms wide) under floor\n        division, leaving the per-pair gap above the stated maximum.\n        ceilf bounds it. */\n  denseCount = 1;  /* first sample always emitted */\n  for (i = 1; i < uniqueCount; i++) {\n    gap = allTimes[i] - allTimes[i - 1];\n    if (gap > BAKE_MAX_STEP) {\n      denseCount += (size_t)ceilf(gap / BAKE_MAX_STEP);\n    } else {\n      denseCount += 1;\n    }\n  }\n\n  if (denseCount > uniqueCount) {\n    dense      = ak_calloc(NULL, sizeof(float) * denseCount);\n    d          = 0;\n    dense[d++] = allTimes[0];\n\n    for (i = 1; i < uniqueCount; i++) {\n      t0      = allTimes[i - 1];\n      t1      = allTimes[i];\n      gap     = t1 - t0;\n      subdivs = (gap > BAKE_MAX_STEP) ? (size_t)ceilf(gap / BAKE_MAX_STEP) : 1;\n\n      for (s = 1; s <= subdivs; s++) {\n        dense[d++] = t0 + (gap * (float)s / (float)subdivs);\n      }\n    }\n\n    ak_free(allTimes);\n    allTimes    = dense;\n    uniqueCount = denseCount;\n  }\n\n  /* 5. Allocate the result. The matrices/times buffers parent off `out`,\n        so a single ak_free(out) cleans up everything. */\n  out           = ak_calloc(NULL, sizeof(*out));\n  out->count    = (uint32_t)uniqueCount;\n  out->matrices = ak_calloc(out, sizeof(float) * 16 * uniqueCount);\n  out->times    = ak_calloc(out, sizeof(float) * uniqueCount);\n\n  /* 6. For each unique time, sample channels, mutate AkObjects in place,\n        then run the standard combine over node->transform. The combiner\n        sees current AkObject state, so the per-sample matrix reflects\n        all rotates / translates / scales of the chain. */\n  for (t_idx = 0; t_idx < uniqueCount; t_idx++) {\n    float t = allTimes[t_idx];\n    float matrix[16];\n\n    for (j = 0; j < nBinds; j++) {\n      ChannelBind *b   = &binds[j];\n      float       *dst = (float *)b->target->pData;\n      size_t       n   = b->savedLen;\n\n      if (b->isPartial) {\n        /* Single-component animation. The sampler's OUTPUT is scalar\n           per keyframe (componentCount==1), so the component index\n           into keyValues is 0. We write that into target->pData[off]. */\n        if (b->off < n)\n          dst[b->off] = bake_sampleScalar(b, 0, t);\n      } else {\n        /* Whole-target animation: OUTPUT has valueStride components\n           per keyframe; one-to-one with target->pData. */\n        size_t lim = b->valueStride < n ? b->valueStride : n;\n        for (k = 0; k < lim; k++)\n          dst[k] = bake_sampleScalar(b, k, t);\n      }\n    }\n\n    matrix[0]  = 1; matrix[1]  = 0; matrix[2]  = 0; matrix[3]  = 0;\n    matrix[4]  = 0; matrix[5]  = 1; matrix[6]  = 0; matrix[7]  = 0;\n    matrix[8]  = 0; matrix[9]  = 0; matrix[10] = 1; matrix[11] = 0;\n    matrix[12] = 0; matrix[13] = 0; matrix[14] = 0; matrix[15] = 1;\n    ak_transformCombine(node->transform, matrix);\n\n    memcpy(out->matrices + t_idx * 16, matrix, 16 * sizeof(float));\n    out->times[t_idx] = t;\n  }\n\n  /* 7. Restore static AkObject payloads. Bind pose is now untouched. */\n  for (j = 0; j < nBinds; j++) {\n    if (binds[j].savedLen > 0)\n      memcpy(binds[j].target->pData, binds[j].saved,\n             binds[j].savedLen * sizeof(float));\n  }\n\n  ak_free(allTimes);\n  return out;\n}\n"
  },
  {
    "path": "src/anim/conflict.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Animation conflict detection\n * ============================\n *\n * Two animations conflict iff any pair of their channels resolves to the\n * SAME animatable slot — i.e. they would write to the same memory at\n * playback time. Runtimes that play multiple animations in parallel use\n * this to decide which clips can co-exist; overlapping writes otherwise\n * produce undefined ordering.\n *\n * Resolution semantics (matches `ak_channelTarget`):\n *\n *   target  : the struct being animated (AkTransform component, AkInstanceMorph, ...)\n *   off     : LOGICAL slot/component index within target\n *   isPartial: whether the channel writes a single component (true) or the\n *              whole target value (false)\n *\n * Conflict rules between two resolved channels (rta, rtb):\n *\n *   1. rta.target  != rtb.target              → no conflict (disjoint memory)\n *   2. !rta.isPartial OR !rtb.isPartial       → CONFLICT\n *      (a whole-target write covers every slot, so any companion write\n *       on the same target is overlapped)\n *   3. both partial AND rta.off == rtb.off    → CONFLICT (same slot)\n *   4. both partial AND different off         → no conflict\n *\n * This is exhaustive at the AssetKit semantic layer. Bridges may layer\n * further keyPath-level checks on top when their target mapping fans\n * multiple AkObjects to the same engine-side property (e.g. AKT_MATRIX\n * and AKT_TRANSLATE both affect a single node transform).\n */\n\n#include \"../common.h\"\n\nAK_EXPORT\nbool\nak_animationsConflict(AkContext   * __restrict ctx,\n                      AkAnimation * __restrict a,\n                      AkAnimation * __restrict b) {\n  AkChannel        *cha, *chb;\n  AkResolvedTarget  rta, rtb;\n\n  if (!a || !b || a == b) return false;\n\n  for (cha = a->channel; cha; cha = cha->next) {\n    rta = ak_channelTarget(ctx, cha);\n    if (!rta.target) continue;\n\n    for (chb = b->channel; chb; chb = chb->next) {\n      rtb = ak_channelTarget(ctx, chb);\n      if (!rtb.target || rtb.target != rta.target) continue;\n\n      /* at least one side writes the whole target → overlap */\n      if (!rta.isPartial || !rtb.isPartial) return true;\n\n      /* both partial — overlap iff same slot */\n      if (rta.off == rtb.off) return true;\n    }\n  }\n\n  return false;\n}\n\nAK_EXPORT\nsize_t\nak_animationsCompatibleSet(AkContext         * __restrict ctx,\n                           AkAnimation       * __restrict primary,\n                           AkAnimation      ** __restrict candidates,\n                           size_t                         candidatesCount,\n                           AkAnimation      ** __restrict outCompatible) {\n  size_t selected, i, j;\n  bool   conflicts;\n\n  if (!outCompatible) return 0;\n\n  selected = 0;\n  if (primary) {\n    outCompatible[selected++] = primary;\n  }\n\n  if (!candidates || candidatesCount == 0) return selected;\n\n  for (i = 0; i < candidatesCount; i++) {\n    if (!candidates[i] || candidates[i] == primary) continue;\n\n    conflicts = false;\n    for (j = 0; j < selected; j++) {\n      if (ak_animationsConflict(ctx, candidates[i], outCompatible[j])) {\n        conflicts = true;\n        break;\n      }\n    }\n    if (!conflicts) {\n      outCompatible[selected++] = candidates[i];\n    }\n  }\n\n  return selected;\n}\n\nAK_EXPORT\nsize_t\nak_animationsCount(AkDoc * __restrict doc) {\n  AkLibrary        *lib;\n  AkOneWayIterBase *iter;\n  size_t            count;\n\n  if (!doc) return 0;\n\n  count = 0;\n  for (lib = doc->lib.animations; lib; lib = lib->next) {\n    for (iter = lib->chld; iter; iter = iter->next) {\n      count++;\n    }\n  }\n  return count;\n}\n\nAK_EXPORT\nsize_t\nak_animationsCompatibleSetFromDoc(AkContext     * __restrict ctx,\n                                  AkDoc         * __restrict doc,\n                                  AkAnimation   * __restrict primary,\n                                  AkAnimation  ** __restrict outCompatible) {\n  AkLibrary         *lib;\n  AkOneWayIterBase  *iter;\n  AkAnimation      **candidates;\n  size_t             count, i;\n\n  if (!doc || !outCompatible) {\n    /* still honor primary even with no candidates */\n    if (primary && outCompatible) {\n      outCompatible[0] = primary;\n      return 1;\n    }\n    return 0;\n  }\n\n  count = ak_animationsCount(doc);\n  if (count == 0) {\n    if (primary) { outCompatible[0] = primary; return 1; }\n    return 0;\n  }\n\n  candidates = alloca(sizeof(*candidates) * count);\n  i = 0;\n  for (lib = doc->lib.animations; lib; lib = lib->next) {\n    for (iter = lib->chld; iter; iter = iter->next) {\n      candidates[i++] = (AkAnimation *)iter;\n    }\n  }\n\n  return ak_animationsCompatibleSet(ctx, primary,\n                                    candidates, count, outCompatible);\n}\n"
  },
  {
    "path": "src/array.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"array.h\"\n#include <string.h>\n\nstatic bool\nak_strarray_is_sep(char c, char separator) {\n  return c == separator || c == '\\n' || c == '\\r' || c == '\\t';\n}\n\nstatic size_t\nak_strarray_count(const char *content, char separator) {\n  size_t count;\n  bool   inTok;\n\n  count = 0;\n  inTok = false;\n\n  if (!content)\n    return 0;\n\n  while (*content) {\n    if (ak_strarray_is_sep(*content, separator)) {\n      inTok = false;\n    } else if (!inTok) {\n      inTok = true;\n      count++;\n    }\n    content++;\n  }\n\n  return count;\n}\n\nAK_HIDE\nAkResult\nak_strtostr_array(AkHeap         * __restrict heap,\n                  void           * __restrict memParent,\n                  char                       *content,\n                  char                        separator,\n                  AkStringArray ** __restrict array) {\n  AkStringArray  *stringArray;\n  char           *pData;\n  char           *tok;\n  char            separatorStr[5];\n  size_t          arrayIndex;\n  size_t          itemCount;\n  size_t          arraySize;\n  size_t          arrayDataSize;\n\n  arrayIndex = 0;\n\n  if (!content || !array)\n    return AK_EINVAL;\n\n  itemCount  = ak_strarray_count(content, separator);\n\n  separatorStr[0] = separator;\n  separatorStr[1] = '\\n';\n  separatorStr[2] = '\\r';\n  separatorStr[3] = '\\t';\n  separatorStr[4] = '\\0';\n\n  /*\n   |pSTR1|pSTR2|pSTR3|STR1\\0STR2\\0STR3|\n\n   the last one is pointer to all data\n   */\n  arraySize = sizeof(char *) * (itemCount + 1);\n  arrayDataSize = strlen(content) + itemCount + 1 /* NULL */;\n\n  stringArray = ak_heap_alloc(heap,\n                              memParent,\n                              sizeof(*stringArray) + arraySize);\n  if (!stringArray)\n    return AK_ENOMEM;\n\n  pData = ak_heap_alloc(heap,\n                        stringArray,\n                        arrayDataSize);\n  if (!pData)\n    return AK_ENOMEM;\n\n  stringArray->count = itemCount;\n  stringArray->items[itemCount] = pData;\n  pData[0] = '\\0';\n\n  tok = strtok(content, separatorStr);\n  while (tok) {\n    strcpy(pData, tok);\n    stringArray->items[arrayIndex++] = pData;\n\n    pData += strlen(tok);\n    *pData++ = '\\0';\n\n    tok = strtok(NULL, separatorStr);\n  }\n\n  *array = stringArray;\n\n  return AK_OK;\n}\n\nAK_HIDE\nAkResult\nak_strtostr_arrayL(AkHeap * __restrict heap,\n                   void * __restrict memParent,\n                   char * stringRep,\n                   char separator,\n                   AkStringArrayL ** __restrict array) {\n  AkStringArrayL *stringArray;\n  char           *pData;\n  char           *tok;\n  char            separatorStr[5];\n  size_t          arrayIndex;\n  size_t          itemCount;\n  size_t          arraySize;\n  size_t          arrayDataSize;\n\n  arrayIndex = 0;\n\n  if (!stringRep || !array)\n    return AK_EINVAL;\n\n  itemCount  = ak_strarray_count(stringRep, separator);\n\n  separatorStr[0] = separator;\n  separatorStr[1] = '\\n';\n  separatorStr[2] = '\\r';\n  separatorStr[3] = '\\t';\n  separatorStr[4] = '\\0';\n\n  /*\n   |pSTR1|pSTR2|pSTR3|STR1\\0STR2\\0STR3|\n\n   the last one is pointer to all data\n   */\n  arraySize = sizeof(char *) * (itemCount + 1);\n  arrayDataSize = strlen(stringRep) + itemCount + 1 /* NULL */;\n\n  stringArray = ak_heap_alloc(heap,\n                              memParent,\n                              sizeof(*stringArray) + arraySize);\n  if (!stringArray)\n    return AK_ENOMEM;\n\n  pData = ak_heap_alloc(heap,\n                        stringArray,\n                        arrayDataSize);\n  if (!pData)\n    return AK_ENOMEM;\n\n  stringArray->count = itemCount;\n  stringArray->items[itemCount] = pData;\n  pData[0] = '\\0';\n\n  tok = strtok(stringRep, separatorStr);\n  while (tok) {\n    strcpy(pData, tok);\n    stringArray->items[arrayIndex++] = pData;\n\n    pData += strlen(tok);\n    *pData++ = '\\0';\n\n    tok = strtok(NULL, separatorStr);\n  }\n\n  *array = stringArray;\n\n  return AK_OK;\n}\n"
  },
  {
    "path": "src/array.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_array_h\n#define ak_array_h\n\n#include \"../include/ak/assetkit.h\"\n\nAK_HIDE\nAkResult\nak_strtostr_array(AkHeap         * __restrict heap,\n                  void           * __restrict memParent,\n                  char                       *content,\n                  char                        separator,\n                  AkStringArray ** __restrict array);\n\nAK_HIDE\nAkResult\nak_strtostr_arrayL(AkHeap          * __restrict heap,\n                   void            * __restrict memParent,\n                   char                        *content,\n                   char                         separator,\n                   AkStringArrayL ** __restrict array);\n\n#endif /* ak_array_h */\n"
  },
  {
    "path": "src/asset.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\nstatic\nAkTree**\nak_extraField(void * __restrict obj) {\n  AkHeap *heap;\n\n  if (!obj)\n    return NULL;\n\n  heap = ak_heap_getheap(obj);\n  if (heap && ak_heap_data(heap) == obj)\n    return &((AkDoc *)obj)->extra;\n\n  switch (ak_typeid(obj)) {\n    case AKT_NODE:\n      return &((AkNode *)obj)->extra;\n    case AKT_SCENE:\n      return &((AkVisualScene *)obj)->extra;\n    case AKT_GEOMETRY:\n      return &((AkGeometry *)obj)->extra;\n    case AKT_MESH:\n      return &((AkMesh *)obj)->extra;\n    case AKT_EFFECT:\n      return &((AkEffect *)obj)->extra;\n    case AKT_PROFILE:\n      return &((AkProfile *)obj)->extra;\n    case AKT_TECHNIQUE_FX:\n      return &((AkTechniqueFx *)obj)->extra;\n    case AKT_SAMPLER:\n    case AKT_SAMPLER2D:\n      return &((AkSampler *)obj)->extra;\n    default:\n      break;\n  }\n\n  return NULL;\n}\n\nAK_EXPORT\nAkTree*\nak_extra(void * __restrict obj) {\n  AkHeapNode *hnode;\n  AkTree    **extra;\n\n  if (!obj)\n    return NULL;\n\n  hnode = ak__alignof(obj);\n  if ((extra = ak_heap_ext_get(hnode, AK_HEAP_NODE_FLAGS_EXTRA)) && *extra)\n    return *extra;\n\n  if ((extra = ak_extraField(obj)))\n    return *extra;\n\n  return NULL;\n}\n\nAK_EXPORT\nvoid\nak_extra_set(void   * __restrict obj,\n             AkTree * __restrict extra) {\n  AkHeap     *heap;\n  AkHeapNode *hnode;\n  AkTree    **slot;\n  AkTree    **field;\n\n  if (!obj)\n    return;\n\n  heap  = ak_heap_getheap(obj);\n  hnode = ak__alignof(obj);\n  slot  = ak_heap_ext_add(heap, hnode, AK_HEAP_NODE_FLAGS_EXTRA);\n  *slot = extra;\n\n  if ((field = ak_extraField(obj)))\n    *field = extra;\n}\n\nAK_EXPORT\nvoid*\nak_getAssetInfo(void * __restrict obj,\n                size_t itemOffset) {\n  AkHeapNode *hnode;\n  char      **ai;\n  void      **found;\n\n  assert(obj && itemOffset < sizeof(AkAssetInf));\n\n  \n  hnode = ak__alignof(obj);\n\n  do {\n    if ((ai = ak_heap_ext_get(hnode, AK_HEAP_NODE_FLAGS_INF))\n        && (found = (void **)(*ai + itemOffset)))\n      return *found;\n\n    hnode = ak_heap_parent(hnode);\n    if (!hnode)\n      return NULL;\n  } while (true);\n}\n\nAK_EXPORT\nAkCoordSys*\nak_getCoordSys(void * __restrict obj) {\n  /* TODO: return default coord sys if null */\n  return ak_getAssetInfo(obj, offsetof(AkAssetInf, coordSys));\n}\n\nAK_EXPORT\nbool\nak_hasCoordSys(void * __restrict obj) {\n  AkHeapNode *hnode;\n  char      **ai;\n  void      **found;\n\n  hnode = ak__alignof(obj);\n  ai    = ak_heap_ext_get(hnode, AK_HEAP_NODE_FLAGS_INF);\n  if (!ai)\n    return false;\n\n  found = (void **)(*ai + offsetof(AkAssetInf, coordSys));\n  return found != NULL;\n}\n"
  },
  {
    "path": "src/assetkit.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../include/ak/assetkit.h\"\n\n#include \"utils.h\"\n#include \"io/dae/dae.h\"\n#include \"io/gltf/imp/gltf.h\"\n#include \"io/obj/obj.h\"\n#include \"io/stl/stl.h\"\n#include \"io/ply/ply.h\"\n#include \"io/3mf/imp/3mf.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdarg.h>\n\ntypedef struct {\n  const char * fext;\n  AkResult (*floader_fn)(AkDoc ** __restrict, const char * __restrict);\n} floader_t;\n\nAK_EXPORT\nAkResult\nak_load(AkDoc ** __restrict dest, const char * __restrict url, ...) {\n  floader_t  *floader;\n  const char *localurl;\n  int         file_type;\n  int         _err_no;\n\n  va_list pref_args;\n  va_start(pref_args, url);\n  file_type = va_arg(pref_args, int);\n  va_end(pref_args);\n\n  localurl = ak_getFile(url);\n  if (!localurl)\n    return AK_EBADF;\n\n  floader_t floaders[] = {\n    {\"dae\",  dae_doc},\n    {\"gltf\", gltf_gltf},\n    {\"glb\",  gltf_glb},\n    {\"obj\",  wobj_obj},\n    {\"stl\",  stl_stl},\n    {\"ply\",  ply_ply},\n    {\"3mf\",  imp_3mf},\n  };\n\n  floader = NULL;\n\n  if (file_type == AK_FILE_TYPE_AUTO) {\n    char * file_ext;\n    file_ext = strrchr(localurl, '.');\n    if (file_ext) {\n      int floader_len;\n      int i;\n\n      ++file_ext;\n      floader_len = AK_ARRAY_LEN(floaders);\n      for (i = 0; i < floader_len; i++) {\n        if (strcmp(file_ext, floaders[i].fext) == 0) {\n          floader = &floaders[i];\n          break;\n        }\n      }\n    } else {\n      /* TODO */\n    }\n  } else {\n    switch (file_type) {\n      case AK_FILE_TYPE_COLLADA: {\n        floader = &floaders[0];\n        break;\n      }\n      case AK_FILE_TYPE_GLTF: {\n        floader = &floaders[1];\n        break;\n      }\n      case AK_FILE_TYPE_WAVEFRONT:\n        floader = &floaders[3];\n        break;\n      case AK_FILE_TYPE_STL:\n        floader = &floaders[4];\n        break;\n      case AK_FILE_TYPE_PLY:\n        floader = &floaders[5];\n        break;\n      case AK_FILE_TYPE_3MF:\n        floader = &floaders[6];\n        break;\n      default:\n        *dest = NULL;\n        break;\n    }\n  }\n\n  if (floader)\n    _err_no = floader->floader_fn(dest, localurl);\n  else\n    goto err;\n\n  return _err_no;\nerr:\n  *dest = NULL;\n  return AK_ERR;\n}\n"
  },
  {
    "path": "src/base64.c",
    "content": "/*\n * Base64 encoding/decoding (RFC1341)\n * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>\n *\n * This software may be distributed under the terms of the BSD license.\n *\n * Modified by Recep Aslantas (github @recp)\n */\n\n#include \"base64.h\"\n#include \"simd/base64.h\"\n#include <string.h>\n\nstatic const unsigned char base64_table[65] =\n\t\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\nstatic const unsigned char base64_dtable[256] = {\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x3e, 0x80, 0x80, 0x80, 0x3f,\n\t0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80,\n\t0x80, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,\n\t0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,\n\t0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,\n\t0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80\n};\n\n/**\n * base64_encode - Base64 encode\n * @src: Data to be encoded\n * @len: Length of the data to be encoded\n * @out_len: Pointer to output length variable, or %NULL if not used\n * Returns: Allocated buffer of out_len bytes of encoded data,\n * or %NULL on failure\n *\n * Caller is responsible for freeing the returned buffer. Returned buffer is\n * nul terminated to make it easier to use as a C string. The nul terminator is\n * not included in out_len.\n */\nAK_HIDE\nunsigned char*\nbase64_encode(AkHeap              * __restrict heap,\n              void                * __restrict memparent,\n              const unsigned char * __restrict src,\n              size_t                           len,\n              size_t              * __restrict out_len) {\n\tunsigned char       *out, *pos;\n\tconst unsigned char *end, *in;\n\tsize_t               olen;\n\tint                  line_len;\n\n\tolen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */\n\tolen += olen / 72; /* line feeds */\n\tolen++; /* nul termination */\n  \n\tif (olen < len)\n\t\treturn NULL; /* integer overflow */\n\tout = ak_heap_alloc(heap, memparent, olen);\n\tif (out == NULL)\n\t\treturn NULL;\n\n\tend = src + len;\n\tin = src;\n\tpos = out;\n\tline_len = 0;\n\twhile (end - in >= 3) {\n\t\t*pos++ = base64_table[in[0] >> 2];\n\t\t*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];\n\t\t*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];\n\t\t*pos++ = base64_table[in[2] & 0x3f];\n\t\tin += 3;\n\t\tline_len += 4;\n\t\tif (line_len >= 72) {\n\t\t\t*pos++ = '\\n';\n\t\t\tline_len = 0;\n\t\t}\n\t}\n\n\tif (end - in) {\n\t\t*pos++ = base64_table[in[0] >> 2];\n\t\tif (end - in == 1) {\n\t\t\t*pos++ = base64_table[(in[0] & 0x03) << 4];\n\t\t\t*pos++ = '=';\n\t\t} else {\n\t\t\t*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];\n\t\t\t*pos++ = base64_table[(in[1] & 0x0f) << 2];\n\t\t}\n\t\t*pos++ = '=';\n\t\tline_len += 4;\n\t}\n\n\tif (line_len)\n\t\t*pos++ = '\\n';\n\n\t*pos = '\\0';\n\tif (out_len)\n\t\t*out_len = pos - out;\n\treturn out;\n}\n\n\n/**\n * base64_decode - Base64 decode\n * @src: Data to be decoded\n * @len: Length of the data to be decoded\n * @out_len: Pointer to output length variable\n * Returns: Allocated buffer of out_len bytes of decoded data,\n * or %NULL on failure\n *\n * Caller is responsible for freeing the returned buffer.\n */\nAK_HIDE\nunsigned char*\nbase64_decode_slow(AkHeap              * __restrict heap,\n                   void                * __restrict memparent,\n                   const unsigned char * __restrict src,\n                   size_t                           len,\n                   size_t              * __restrict out_len) {\n\tunsigned char dtable[256], *out, *pos, block[4], tmp;\n\tsize_t        i, count, olen;\n\tint           pad = 0;\n\n\tmemset(dtable, 0x80, 256);\n\n\tfor (i = 0; i < sizeof(base64_table) - 1; i++)\n\t\tdtable[base64_table[i]] = (unsigned char) i;\n\n\tdtable['='] = 0;\n\n\tcount = 0;\n\tfor (i = 0; i < len; i++) {\n\t\tif (dtable[src[i]] != 0x80)\n\t\t\tcount++;\n\t}\n\n\tif (count == 0 || count % 4)\n\t\treturn NULL;\n\n\tolen = count / 4 * 3;\n\tpos  = out = ak_heap_alloc(heap, memparent, olen);\n\tif (out == NULL)\n\t\treturn NULL;\n\n\tcount = 0;\n\tfor (i = 0; i < len; i++) {\n\t\ttmp = dtable[src[i]];\n\t\tif (tmp == 0x80)\n\t\t\tcontinue;\n\n\t\tif (src[i] == '=')\n\t\t\tpad++;\n\t\tblock[count] = tmp;\n\t\tcount++;\n\t\tif (count == 4) {\n\t\t\t*pos++ = (block[0] << 2) | (block[1] >> 4);\n\t\t\t*pos++ = (block[1] << 4) | (block[2] >> 2);\n\t\t\t*pos++ = (block[2] << 6) | block[3];\n\t\t\tcount = 0;\n\t\t\tif (pad) {\n\t\t\t\tif (pad == 1)\n\t\t\t\t\tpos--;\n\t\t\t\telse if (pad == 2)\n\t\t\t\t\tpos -= 2;\n\t\t\t\telse {\n\t\t\t\t\t/* Invalid padding */\n\t\t\t\t\tak_free(out);\n\t\t\t\t\treturn NULL;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t*out_len = pos - out;\n\treturn out;\n}\n\n/**\n * Fast path for compact data URIs. glTF embedded buffers/images are normally\n * emitted without whitespace, so avoid the validation/counting pass.\n */\nAK_HIDE\nunsigned char*\nbase64_decode(AkHeap              * __restrict heap,\n              void                * __restrict memparent,\n              const unsigned char * __restrict src,\n              size_t                           len,\n              size_t              * __restrict out_len) {\n\tunsigned char *out, *pos;\n\tsize_t         i, olen;\n\tsize_t         simd_consumed, simd_written;\n\tint            pad;\n\n\tif (len == 0 || (len & 3) != 0)\n\t\treturn base64_decode_slow(heap, memparent, src, len, out_len);\n\n\tpad = 0;\n\tif (src[len - 1] == '=') {\n\t\tpad++;\n\t\tif (src[len - 2] == '=')\n\t\t\tpad++;\n\t}\n\n\tolen = len / 4 * 3 - (size_t)pad;\n\tpos  = out = ak_heap_alloc(heap, memparent, olen ? olen : 1);\n\tif (out == NULL)\n\t\treturn NULL;\n\n\tif (!ak_simd_base64_decode(src, len, out, &simd_consumed, &simd_written)) {\n\t\tak_free(out);\n\t\treturn base64_decode_slow(heap, memparent, src, len, out_len);\n\t}\n\ti = simd_consumed;\n\tpos = out + simd_written;\n\n\tfor (; i < len; i += 4) {\n\t\tunsigned char a, b, c, d;\n\t\tint           final;\n\n\t\tfinal = i + 4 == len;\n\t\tif (src[i] == '=' || src[i + 1] == '='\n\t\t    || ((src[i + 2] == '=' || src[i + 3] == '=') && !final)\n\t\t    || (src[i + 2] == '=' && src[i + 3] != '=')) {\n\t\t\tak_free(out);\n\t\t\treturn base64_decode_slow(heap, memparent, src, len, out_len);\n\t\t}\n\n\t\ta = base64_dtable[src[i]];\n\t\tb = base64_dtable[src[i + 1]];\n\t\tc = src[i + 2] == '=' ? 0 : base64_dtable[src[i + 2]];\n\t\td = src[i + 3] == '=' ? 0 : base64_dtable[src[i + 3]];\n\n\t\tif ((a | b | c | d) & 0x80) {\n\t\t\tak_free(out);\n\t\t\treturn base64_decode_slow(heap, memparent, src, len, out_len);\n\t\t}\n\n\t\t*pos++ = (unsigned char)((a << 2) | (b >> 4));\n\t\tif (src[i + 2] != '=') {\n\t\t\t*pos++ = (unsigned char)((b << 4) | (c >> 2));\n\t\t\tif (src[i + 3] != '=')\n\t\t\t\t*pos++ = (unsigned char)((c << 6) | d);\n\t\t}\n\t}\n\n\t*out_len = (size_t)(pos - out);\n\treturn out;\n}\n\nAK_HIDE\nvoid\nbase64_buff(const char * __restrict b64,\n            size_t                  len,\n            AkBuffer   * __restrict buff) {\n  const char *b64Data;\n  const char *marker;\n  size_t      markerLen;\n\n  markerLen = sizeof(\";base64,\") - 1;\n  marker    = memchr(b64, ';', len);\n  if (!marker || (size_t)(len - (uintptr_t)(marker - b64)) <= markerLen)\n    return;\n\n  if (memcmp(marker, \";base64,\", markerLen) != 0)\n    return;\n\n  b64Data = marker + markerLen;\n  buff->data = base64_decode(ak_heap_getheap(buff),\n                             buff,\n                             (const unsigned char *)b64Data,\n                             len - (uintptr_t)(b64Data - b64),\n                             &buff->length);\n}\n"
  },
  {
    "path": "src/base64.h",
    "content": "/*\n * Base64 encoding/decoding (RFC1341)\n * Copyright (c) 2005, Jouni Malinen <j@w1.fi>\n *\n * This software may be distributed under the terms of the BSD license.\n *\n * Modified by Recep Aslantas (github @recp)\n */\n\n#ifndef BASE64_H\n#define BASE64_H\n\n#include <stdlib.h>\n#include <memory.h>\n#include \"../include/ak/memory.h\"\n#include \"../include/ak/source.h\"\n\nAK_HIDE\nunsigned char*\nbase64_encode(AkHeap              * __restrict heap,\n              void                * __restrict memparent,\n              const unsigned char * __restrict src,\n              size_t                           len,\n              size_t              * __restrict out_len);\n\nAK_HIDE\nunsigned char*\nbase64_decode(AkHeap              * __restrict heap,\n              void                * __restrict memparent,\n              const unsigned char * __restrict src,\n              size_t                          len,\n              size_t              * __restrict out_len);\n\nAK_HIDE\nvoid\nbase64_buff(const char * __restrict b64,\n            size_t                  len,\n            AkBuffer   * __restrict buff);\n\n#endif /* BASE64_H */\n"
  },
  {
    "path": "src/bbox/CMakeLists.txt",
    "content": "target_sources(${PROJECT_NAME} \n  PRIVATE\n  bbox.h\n  bbox.c\n  geom.c\n  mesh_prim.c\n  mesh.c\n  scene.c\n)\n"
  },
  {
    "path": "src/bbox/bbox.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"bbox.h\"\n#include <cglm/cglm.h>\n\nvoid\nak_bbox_pick(vec3 min, vec3 max, vec3 vec) {\n  glm_vec3_minv(min, vec, min);\n  glm_vec3_maxv(max, vec, max);\n}\n\nvoid\nak_bbox_pick_pbox(AkBoundingBox *parent,\n                  AkBoundingBox *chld) {\n  glm_vec3_minv(parent->min, chld->min, parent->min);\n  glm_vec3_maxv(parent->max, chld->max, parent->max);\n}\n\nvoid\nak_bbox_pick_pbox2(AkBoundingBox *parent,\n                   float vec1[3],\n                   float vec2[3]) {\n  glm_vec3_minv(parent->min, vec1, parent->min);\n  glm_vec3_minv(parent->min, vec2, parent->min);\n\n  glm_vec3_maxv(parent->max, vec1, parent->max);\n  glm_vec3_maxv(parent->max, vec2, parent->max);\n}\n\nvoid\nak_bbox_center(AkBoundingBox * __restrict bbox,\n               float center[3]) {\n  glm_vec3_center(bbox->min, bbox->max, center);\n}\n\nfloat\nak_bbox_radius(AkBoundingBox * __restrict bbox) {\n  return glm_vec3_distance(bbox->max, bbox->min) * 0.5f;\n}\n"
  },
  {
    "path": "src/bbox/bbox.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_src_bbox_h\n#define ak_src_bbox_h\n\n#include \"../common.h\"\n#include \"../../include/ak/bbox.h\"\n\nAK_INLINE\nvoid\nak_bbox_invalidate(AkBoundingBox * __restrict bbox) {\n  glm_vec3_broadcast(FLT_MAX,  bbox->min);\n  glm_vec3_broadcast(-FLT_MAX, bbox->max);\n}\n\nvoid\nak_bbox_pick(float min[3],\n             float max[3],\n             float vec[3]);\n\nvoid\nak_bbox_pick_pbox(AkBoundingBox *parent,\n                  AkBoundingBox *chld);\n\nvoid\nak_bbox_pick_pbox2(AkBoundingBox *parent,\n                   float vec1[3],\n                   float vec2[3]);\n\n#endif /* ak_src_bbox_h */\n"
  },
  {
    "path": "src/bbox/geom.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"bbox.h\"\n\nvoid\nak_bbox_geom(struct AkGeometry * __restrict geom) {\n  AkObject *primitive;\n\n  primitive = geom->gdata;\n  switch ((AkGeometryType)primitive->type) {\n    case AK_GEOMETRY_MESH:\n      ak_bbox_mesh(ak_objGet(primitive));\n      break;\n    case AK_GEOMETRY_SPLINE: /* TODO: */\n    case AK_GEOMETRY_BREP: /* TODO: */\n      break;\n  }\n}\n"
  },
  {
    "path": "src/bbox/mesh.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"bbox.h\"\n\nvoid\nak_bbox_mesh(struct AkMesh * __restrict mesh) {\n  AkMeshPrimitive *prim;\n  vec3             center;\n  int32_t          primcount;\n\n  primcount = 0;\n  prim      = mesh->primitive;\n\n  if (!mesh->bbox)\n    mesh->bbox = ak_heap_calloc(ak_heap_getheap(prim),\n                                ak_objFrom(mesh),\n                                sizeof(*mesh->bbox));\n\n  ak_bbox_invalidate(mesh->bbox);\n  \n  while (prim) {\n    ak_bbox_mesh_prim(prim);\n    primcount++;\n    prim = prim->next;\n  }\n\n  /* compute centroid */\n\n  if (!ak_opt_get(AK_OPT_COMPUTE_EXACT_CENTER)) {\n    ak_bbox_center(mesh->bbox, mesh->center);\n  } else {\n    glm_vec3_zero(center);\n\n    /* calculate exact center of primitive */\n    if (primcount > 0) {\n      while (prim) {\n        ak_bbox_mesh_prim(prim);\n\n        glm_vec3_add(prim->center, center, center);\n        primcount++;\n        prim = prim->next;\n      }\n      glm_vec3_divs(center, (float)primcount, mesh->center);\n    }\n  }\n}\n"
  },
  {
    "path": "src/bbox/mesh_prim.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"bbox.h\"\n#include <cglm/cglm.h>\n#include <float.h>\n\nvoid\nak_bbox_mesh_prim(struct AkMeshPrimitive * __restrict prim) {\n  AkHeap     *heap;\n  AkGeometry *geom;\n  AkMesh     *mesh;\n  AkBuffer   *posbuff;\n  char       *data;\n  AkAccessor *acc;\n  float      *vec;\n  vec3        center, min, max;\n  size_t      i, count, byteStride;\n  bool        exactCenter;\n\n  mesh    = prim->mesh;\n  geom    = mesh->geom;\n  posbuff = NULL;\n  acc     = NULL;\n\n  if (!prim->pos\n      || !(acc = prim->pos->accessor)\n      || !(posbuff = acc->buffer))\n    return;\n\n  data = ((char *)posbuff->data + acc->byteOffset);\n\n  glm_vec3_broadcast(FLT_MAX, min);\n  glm_vec3_broadcast(-FLT_MAX, max);\n  glm_vec3_broadcast(0.0f, center);\n\n  exactCenter = ak_opt_get(AK_OPT_COMPUTE_EXACT_CENTER);\n  byteStride  = acc->byteStride;\n\n  if (byteStride == 0)\n    byteStride = acc->fillByteSize;\n\n  /* we must walk through indices if exists because source may contain\n     unrelated data and this will cause get wrong box\n   */\n  if (prim->indices) {\n    AkUInt  *ind;\n    size_t   icount;\n    uint32_t st, vo;\n\n    icount     = prim->indices->count;\n    vo         = prim->pos->offset;\n    st         = prim->indexStride;\n    ind        = prim->indices->items;\n    count      = icount;\n    \n    if (!exactCenter) {\n      for (i = 0; i < icount; i += st) {\n        vec = (float *)(data + ind[i + vo] * byteStride);\n        glm_vec3_add(vec, center, center);\n        ak_bbox_pick(min, max, vec);\n      }\n    } else {\n      for (i = 0; i < icount; i += st) {\n        vec = (float *)(data + ind[i + vo] * byteStride);\n        ak_bbox_pick(min, max, vec);\n      }\n    }\n  } else {\n    count = acc->count;\n    if (!exactCenter) {\n      for (i = 0; i < count; i++) {\n        vec = (float *)(data + byteStride * i);\n        ak_bbox_pick(min, max, vec);\n      }\n    } else {\n      for (i = 0; i < count; i++) {\n        vec = (float *)(data + byteStride * i);\n        glm_vec3_add(vec, center, center);\n        ak_bbox_pick(min, max, vec);\n      }\n    }\n  }\n  \n  heap = ak_heap_getheap(prim);\n\n  if (!prim->bbox) {\n    prim->bbox = ak_heap_calloc(heap, prim, sizeof(*prim->bbox));\n    ak_bbox_invalidate(prim->bbox);\n  }\n\n  if (!mesh->bbox) {\n    mesh->bbox = ak_heap_calloc(heap, prim, sizeof(*prim->bbox));\n    ak_bbox_invalidate(mesh->bbox);\n  }\n\n  if (!geom->bbox) {\n    geom->bbox = ak_heap_calloc(heap, prim, sizeof(*prim->bbox));\n    ak_bbox_invalidate(geom->bbox);\n  }\n\n  glm_vec3_copy(min, prim->bbox->min);\n  glm_vec3_copy(max, prim->bbox->max);\n\n  ak_bbox_pick_pbox(mesh->bbox, prim->bbox);\n  ak_bbox_pick_pbox(geom->bbox, mesh->bbox);\n\n  /* compute centroid */\n  if (!ak_opt_get(AK_OPT_COMPUTE_EXACT_CENTER)) {\n    glm_vec3_center(prim->bbox->min, prim->bbox->max, prim->center);\n  } else if (count > 0) {\n    /* calculate exact center of primitive */\n    glm_vec3_divs(center, (float)count, center);\n  } else {\n    glm_vec3_zero(prim->center);\n  }\n}\n"
  },
  {
    "path": "src/bbox/scene.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"bbox.h\"\n#include <cglm/cglm.h>\n#include <float.h>\n\nstatic\nvoid\nak_bbox_node(AkHeap        * __restrict heap,\n             AkVisualScene * __restrict scene,\n             AkNode        * __restrict node,\n             mat4                       parentTrans);\n\nstatic\nvoid\nak_bbox_node(AkHeap        * __restrict heap,\n             AkVisualScene * __restrict scene,\n             AkNode        * __restrict node,\n             mat4                       parentTrans) {\n  AkFloat  (*matrixWorld)[4];\n\n  if (!node->matrix)\n    node->matrix = ak_heap_alloc(heap,\n                                 node,\n                                 sizeof(*node->matrix));\n\n  if (!node->matrixWorld)\n    node->matrixWorld = ak_heap_alloc(heap,\n                                      node,\n                                      sizeof(*node->matrixWorld));\n\n  matrixWorld = node->matrixWorld->val;\n\n  ak_transformCombine(node->transform, node->matrix->val[0]);\n  glm_mat4_mul(parentTrans, node->matrix->val, matrixWorld);\n\n  if (node->geometry) {\n    AkInstanceBase *geomInst;\n    AkGeometry     *geom;\n    AkBoundingBox   bbox;\n    vec4            min, max;\n\n    glm_vec3_broadcast(FLT_MAX, bbox.min);\n    glm_vec3_broadcast(-FLT_MAX, bbox.max);\n    min[3] = max[3] = 1.0f;\n\n    /* find bbox for node to avoid extra calc */\n    geomInst = &node->geometry->base;\n    while (geomInst) {\n      geom = ak_instanceObject(geomInst);\n      if (geom && geom->bbox)\n        ak_bbox_pick_pbox(&bbox, geom->bbox);\n\n      geomInst = geomInst->next;\n    }\n\n    glm_vec3_copy(bbox.min, min);\n    glm_vec3_copy(bbox.max, max);\n\n    glm_mat4_mulv(matrixWorld, min, min);\n    glm_mat4_mulv(matrixWorld, max, max);\n\n    if (scene->bbox->isvalid) {\n      ak_bbox_pick_pbox2(scene->bbox, min, max);\n    } else {\n      glm_vec3_copy(min, scene->bbox->min);\n      glm_vec3_copy(max, scene->bbox->max);\n      scene->bbox->isvalid = true;\n    }\n  }\n\n  if (node->node) {\n    AkNode *nodei;\n    if ((nodei = ak_instanceObjectNode(node))) {\n      do {\n        ak_bbox_node(heap, scene, nodei, matrixWorld);\n        nodei = nodei->next;\n      } while (nodei);\n    }\n  }\n\n  if (node->chld) {\n    AkNode *nodei;\n    nodei = node->chld;\n    do {\n      ak_bbox_node(heap, scene, nodei, matrixWorld);\n      nodei = nodei->next;\n    } while (nodei);\n  }\n}\n\nvoid\nak_bbox_scene(struct AkVisualScene * __restrict scene) {\n  mat4    trans = GLM_MAT4_IDENTITY_INIT;\n  AkHeap *heap;\n  AkNode *node;\n\n  node = scene->node;\n  heap = ak_heap_getheap(scene);\n\n  if (!scene->bbox) {\n    scene->bbox = ak_heap_calloc(heap, scene, sizeof(*scene->bbox));\n    ak_bbox_invalidate(scene->bbox);\n  }\n\n  while (node) {\n    ak_bbox_node(heap, scene, node, trans);\n    node = node->next;\n  }\n}\n"
  },
  {
    "path": "src/bitwise/CMakeLists.txt",
    "content": "target_sources(${PROJECT_NAME} \n  PRIVATE\n  bitwise.h\n)\n"
  },
  {
    "path": "src/bitwise/bitwise.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_bitwise_h\n#define ak_bitwise_h\n\n#include \"../common.h\"\n#include <limits.h>\n\nAK_INLINE\nuint32_t\nak_bitw_ctz(uint32_t x) {\n#if __has_builtin(__builtin_ctz)\n\treturn __builtin_ctz(x);\n#else\n\tuint32_t i;\n\n\ti = 0;\n\twhile ((x >>= 1) > 0)\n\t\ti++;\n\treturn i;\n#endif\n}\n\nAK_INLINE\nuint32_t\nak_bitw_ffs(uint32_t x) {\n#if __has_builtin(__builtin_ffs)\n\treturn __builtin_ffs(x);\n#else\n\treturn ak_bitw_ctz(x) + 1;\n#endif\n}\n\nAK_INLINE\nuint32_t\nak_bitw_clz(uint32_t x) {\n#if __has_builtin(__builtin_clz)\n\treturn __builtin_clz(x);\n#else\n\treturn sizeof(uint32_t) * CHAR_BIT - ak_bitw_ffs(x);\n#endif\n}\n\n#endif /* ak_bitwise_h */\n"
  },
  {
    "path": "src/camera/CMakeLists.txt",
    "content": "target_sources(${PROJECT_NAME} \n  PRIVATE\n  cam.c\n)\n"
  },
  {
    "path": "src/camera/cam.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../default/cam.h\"\n#include <cglm/cglm.h>\n\nAK_EXPORT\nAkResult\nak_firstCamera(AkDoc     * __restrict doc,\n               AkCamera ** camera,\n               float     * matrix,\n               float     * projMatrix) {\n  AkHeap        *heap;\n  AkVisualScene *scene;\n  AkNode        *camNode;\n  AkCamera      *cam;\n\n  if (!doc->scene.visualScene)\n    goto efound;\n\n  scene = ak_instanceObject(doc->scene.visualScene);\n  if (!scene->firstCamNode)\n    goto efound;\n\n  heap    = ak_heap_getheap(doc);\n  camNode = scene->firstCamNode;\n\n  /* view matrix */\n  if (matrix)\n    ak_transformCombineWorld(camNode, matrix);\n\n  if (camera || projMatrix) {\n    cam = ak_instanceObject(camNode->camera);\n\n    if (!cam) {\n      if (ak_opt_get(AK_OPT_ADD_DEFAULT_CAMERA)) {\n        AkInstanceBase *cameraInst;\n        cam = (AkCamera *)ak_defaultCamera(camNode);\n\n        cameraInst = ak_instanceMake(heap, camNode, cam);\n        ak_instanceListEmpty(scene->cameras);\n        ak_instanceListAdd(scene->cameras, cameraInst);\n\n        cameraInst->next = camNode->camera;\n        camNode->camera  = cameraInst;\n\n        ak_libAddCamera(doc, cam);\n      } else {\n        goto efound;\n      }\n    }\n\n    if (camera)\n      *camera = cam;\n\n    if (projMatrix) {\n      switch ((int)cam->optics->tcommon->type) {\n        case AK_PROJECTION_PERSPECTIVE: {\n          AkPerspective *perspective;\n          perspective = (AkPerspective *)cam->optics->tcommon;\n\n          glm_perspective(perspective->yfov,\n                          perspective->aspectRatio,\n                          perspective->znear,\n                          perspective->zfar,\n                          (vec4 *)projMatrix);\n          break;\n        }\n\n        case AK_PROJECTION_ORTHOGRAPHIC: {\n          AkOrthographic *ortho;\n          ortho = (AkOrthographic *)cam->optics->tcommon;\n\n          glm_ortho(-ortho->xmag,\n                     ortho->xmag,\n                    -ortho->ymag,\n                     ortho->ymag,\n                     ortho->znear,\n                     ortho->zfar,\n                     (vec4 *)projMatrix);\n          break;\n        }\n      }\n    }\n  }\n\n  return AK_OK;\n\nefound:\n  if (camera)\n    *camera = NULL;\n\n  return AK_EFOUND;\n}\n\n\nAK_EXPORT\nconst AkCamera*\nak_defaultCamera(void * __restrict memparent) {\n  AkHeap   *heap;\n  AkCamera *cam;\n  const AkCamera *defcam;\n\n  defcam = ak_def_camera();\n\n  if (memparent)\n    heap = ak_heap_getheap(memparent);\n  else\n    heap = ak_heap_default();\n\n  cam = ak_heap_calloc(heap,\n                         memparent,\n                         sizeof(*cam));\n  memcpy(cam, defcam, sizeof(*defcam));\n  cam->optics = ak_heap_calloc(heap,\n                               cam,\n                               sizeof(*cam->optics));\n  cam->optics->tcommon = ak_heap_calloc(heap,\n                                        cam,\n                                        sizeof(AkPerspective));\n\n  memcpy(cam->optics->tcommon,\n         defcam->optics->tcommon,\n         sizeof(AkPerspective));\n\n  return cam;\n}\n\nAK_EXPORT\nAkCamera *\nak_camMakePerspective(AkDoc * __restrict doc,\n                      void  * __restrict memparent,\n                      float yfov,\n                      float aspect,\n                      float znear,\n                      float zfar) {\n  AkHeap        *heap;\n  AkCamera      *cam;\n  AkPerspective *persp;\n\n  if (!doc) return NULL;\n\n  heap        = ak_heap_getheap(doc);\n  cam         = ak_heap_calloc(heap, memparent ? memparent : (void *)doc,\n                                     sizeof(*cam));\n  cam->optics = ak_heap_calloc(heap, cam, sizeof(*cam->optics));\n\n  /* Concrete projection lives in the camera's heap region so it's\n     freed with the camera. */\n  persp                = ak_heap_calloc(heap, cam, sizeof(*persp));\n  persp->base.type     = AK_PROJECTION_PERSPECTIVE;\n  persp->yfov          = yfov;\n  persp->aspectRatio   = aspect;\n  persp->znear         = znear;\n  persp->zfar          = zfar;\n  cam->optics->tcommon = (AkProjection *)persp;\n\n  ak_libAddCamera(doc, cam);\n  return cam;\n}\n\nAK_EXPORT\nAkCamera *\nak_camMakeOrthographic(AkDoc * __restrict doc,\n                       void  * __restrict memparent,\n                       float xmag,\n                       float ymag,\n                       float aspect,\n                       float znear,\n                       float zfar) {\n  AkHeap         *heap;\n  AkCamera       *cam;\n  AkOrthographic *ortho;\n\n  if (!doc) return NULL;\n\n  heap        = ak_heap_getheap(doc);\n  cam         = ak_heap_calloc(heap, memparent ? memparent : (void *)doc,\n                                     sizeof(*cam));\n  cam->optics = ak_heap_calloc(heap, cam, sizeof(*cam->optics));\n\n  ortho                = ak_heap_calloc(heap, cam, sizeof(*ortho));\n  ortho->base.type     = AK_PROJECTION_ORTHOGRAPHIC;\n  ortho->xmag          = xmag;\n  ortho->ymag          = ymag;\n  ortho->aspectRatio   = aspect;\n  ortho->znear         = znear;\n  ortho->zfar          = zfar;\n  cam->optics->tcommon = (AkProjection *)ortho;\n\n  ak_libAddCamera(doc, cam);\n  return cam;\n}\n"
  },
  {
    "path": "src/common.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n#include <json/json.h>\n\n\nAK_HIDE int\nak_enumpair_cmp(const void * a, const void * b) {\n  return strcmp(((const ak_enumpair *)a)->key,\n                ((const ak_enumpair *)b)->key);\n}\n\nAK_HIDE int\nak_enumpair_cmp2(const void * a, const void * b) {\n  return strcmp(a, ((const ak_enumpair *)b)->key);\n}\n\nAK_HIDE int\nak_enumpair_json_val_cmp(const void * a, const void * b) {\n  const char *s;\n  \n  if (!(s = json_string(a)))\n    return -1;\n\n  return strncmp(s, ((const ak_enumpair *)b)->key, ((json_t *)a)->valsize);\n}\n"
  },
  {
    "path": "src/common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_src_common_h\n#define ak_src_common_h\n\n#define AK_INTERN\n\n#include \"../include/ak/assetkit.h\"\n#include \"../include/ak/options.h\"\n#include \"../include/ak/map.h\"\n#include \"../include/ak/type.h\"\n#include \"../include/ak/url.h\"\n#include \"../include/ak/transform.h\"\n\n#include \"mem/common.h\"\n\n#ifndef CGLM_ALL_UNALIGNED\n#  define CGLM_ALL_UNALIGNED\n#endif\n\n#include <cglm/cglm.h>\n\n#include <ds/rb.h>\n#include <ds/forward-list.h>\n#include <ds/forward-list-sep.h>\n\n#include <stddef.h>\n#include <sys/types.h>\n#include <string.h>\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdarg.h>\n\n#ifdef _MSC_VER\n#  define strncasecmp _strnicmp\n#  define strcasecmp  _stricmp\n#  define strtok_r    strtok_s\n#  define mktemp      _mktemp\n#  define ASM __asm\n# else\n#  define ASM __asm__\n#endif\n\n#ifdef __GNUC__\n#  define AK_DESTRUCTOR __attribute__((destructor))\n#  define AK_CONSTRUCTOR __attribute__((constructor))\n#else\n#  define AK_DESTRUCTOR\n#  define AK_CONSTRUCTOR\n#endif\n\n#define AK__UNUSED(X) (void)X\n\n#define I2P (void *)(intptr_t)\n\n/*!\n * @brief get sign of 32 bit integer as +1 or -1\n *\n * @param X integer value\n */\n#define AK_GET_SIGN(X) ((X >> 31) - (-X >> 31))\n\n#define AK_ARRAY_SEP_CHECK (c == ' ' || c == '\\n' || c == '\\t' \\\n                              || c == '\\r' || c == '\\f' || c == '\\v')\n\n#define AK_ARRAY_SEPLINE_CHECK (c == ' ' || c == '\\t'  || c == '\\f' || c == '\\v')\n#define AK_ARRAY_SPACE_CHECK (c == ' ' || c == '\\t' || c == '\\f' || c == '\\v')\n#define AK_ARRAY_NLINE_CHECK (c == '\\n' || c == '\\r')\n\ntypedef struct ak_enumpair {\n  const char *key;\n  AkEnum      val;\n} ak_enumpair;\n\ntypedef struct {\n  const char * name;\n  AkEnum       val;\n} dae_enum;\n\nAK_HIDE int\nak_enumpair_cmp(const void * a, const void * b);\n\nAK_HIDE int\nak_enumpair_cmp2(const void * a, const void * b);\n\nAK_HIDE int\nak_enumpair_json_val_cmp(const void * a, const void * b);\n\nAK_EXPORT\nint\nak_cmp_str(void * key1, void *key2);\n\nAK_EXPORT\nint\nak_cmp_ptr(void *key1, void *key2);\n\nAK_EXPORT\nint\nak_cmp_i32(void *key1, void *key2);\n\nAK_EXPORT\nint\nak_cmp_vec3(void * key1, void *key2);\n\nAK_EXPORT\nint\nak_cmp_ivec3(void *key1, void *key2);\n\nAK_EXPORT\nint\nak_cmp_vec4(void *key1, void *key2);\n\ntypedef int (*AkCmpFn)(void * key1, void *key2);\n\n#endif /* ak_src_common_h */\n"
  },
  {
    "path": "src/coord/CMakeLists.txt",
    "content": "target_sources(${PROJECT_NAME} \n  PRIVATE\n  camera.c\n  common.h\n  common.c\n  doc.c\n  geom.c\n  mesh.c\n  node.c\n  scene.c\n  transform.c\n  transforms.c\n  vector.c\n)\n"
  },
  {
    "path": "src/coord/camera.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"common.h\"\n#include <cglm/cglm.h>\n\nAK_HIDE\nvoid\nak_coordRotForFixedCoord(AkCoordSys        *oldCoordSys,\n                         AkCoordSys        *newCoordSys,\n                         AkAxisOrientation *oldAxes,\n                         AkAxisOrientation *newAxes,\n                         vec4               fwdAxis,\n                         vec4               upAxis);\n\nAK_HIDE\nvoid\nak_coordRotForFixedCoord(AkCoordSys        *oldCoordSys,\n                         AkCoordSys        *newCoordSys,\n                         AkAxisOrientation *oldAxes,\n                         AkAxisOrientation *newAxes,\n                         vec4               fwdAxis,\n                         vec4               upAxis) {\n  ivec3          camOriOld, camOriNew;\n  vec3           v1, v2, v3, tmp;\n  AkAxisAccessor a0, a1;\n\n  ak_coordAxisCamAccessors(newCoordSys,\n                           &a0,\n                           &a1);\n\n  ak_coordAxisOri(oldCoordSys, *oldAxes, camOriOld);\n  ak_coordAxisOri(newCoordSys, *newAxes, camOriNew);\n\n  glm_vec3_broadcast(0.0f, v1);\n  glm_vec3_broadcast(0.0f, v2);\n\n  /* we want to rotate from new to old!!! */\n  v1[abs(camOriNew[2]) - 1] = (float)glm_sign(camOriNew[2]);\n  v2[abs(camOriOld[2]) - 1] = (float)glm_sign(camOriOld[2]);\n  glm_vec3_cross(v1, v2, v3);\n\n  /* angle for forward axis */\n  fwdAxis[3] = glm_vec3_angle(v1, v2);\n  if (fwdAxis[3] != 0.0f) {\n    /* forward axis */\n    glm_vec3_copy(v3, fwdAxis);\n\n    /* convert to current coord sys */\n    AK_CVT_VEC(fwdAxis)\n  }\n\n  /* up axis */\n  glm_vec3_broadcast(0.0f, v1);\n  glm_vec3_broadcast(0.0f, v2);\n\n  v1[abs(camOriNew[1]) - 1] = (float)glm_sign(camOriNew[1]);\n  v2[abs(camOriOld[1]) - 1] = (float)glm_sign(camOriOld[1]);\n\n  /* rotate with fwd to find new up (rotated) */\n  glm_vec3_rotate(v1, fwdAxis[3], v3);\n\n  glm_vec3_cross(v1, v2, v3);\n\n  /* angle for up axis */\n  upAxis[3] = glm_vec3_angle(v1, v2);\n\n  /* up direction */\n  if (upAxis[3] != 0.0f) {\n    /* up axis */\n    glm_vec3_copy(v3, upAxis);\n\n    /* convert to current coord sys */\n    AK_CVT_VEC(upAxis)\n\n    /* rotate found axis with fwd */\n    if (fwdAxis[3] != 0.0f)\n      glm_vec3_rotate(upAxis, -fwdAxis[3], fwdAxis);\n  }\n}\n\nAK_EXPORT\nvoid\nak_coordFixCamOri(AkCoordSys *oldCoordSys,\n                  AkCoordSys *newCoordSys,\n                  AkFloat4x4  transform) {\n  vec4 fwdAxis, upAxis;\n\n  if (ak_coordOrientationIsEq(oldCoordSys, newCoordSys))\n    return;\n\n  memset(fwdAxis, 0, sizeof(fwdAxis));\n  memset(upAxis,  0, sizeof(upAxis));\n\n  ak_coordRotForFixedCoord(oldCoordSys,\n                           newCoordSys,\n                           &oldCoordSys->cameraOrientation,\n                           &newCoordSys->cameraOrientation,\n                           fwdAxis,\n                           upAxis);\n\n  /* apply rotation for forward direction */\n  if (fwdAxis[3] != 0.0f)\n    glm_rotate(transform, fwdAxis[3], fwdAxis);\n\n  /* apply rotation for up direction */\n  if (upAxis[3] != 0.0f)\n    glm_rotate(transform, upAxis[3], upAxis);\n}\n\nAK_EXPORT\nvoid\nak_coordRotNodeForFixedCoord(AkDoc     *doc,\n                             void      *memparent,\n                             AkObject **destTransform) {\n  AkHeap     *heap;\n  AkCoordSys *oldCoordSys,  *newCoordSys;\n  AkObject   *transformFwd, *transformUp;\n  AkRotate   *rotate;\n  vec4        fwdAxis, upAxis;\n\n  oldCoordSys = doc->coordSys;\n  newCoordSys = (void *)ak_opt_get(AK_OPT_COORD);\n\n  *destTransform = NULL;\n  if (ak_coordOrientationIsEq(oldCoordSys, newCoordSys))\n    return;\n\n  memset(fwdAxis, 0, sizeof(fwdAxis));\n  memset(upAxis,  0, sizeof(upAxis));\n\n  ak_coordRotForFixedCoord(oldCoordSys,\n                           newCoordSys,\n                           &oldCoordSys->cameraOrientation,\n                           &newCoordSys->cameraOrientation,\n                           fwdAxis,\n                           upAxis);\n\n  heap = ak_heap_getheap(doc);\n\n  /* we assume that we only need two rotation for fwd and up (not right) */\n\n  /* rotation for forward direction */\n  if (fwdAxis[3] != 0.0f) {\n    transformFwd = ak_objAlloc(heap,\n                               memparent,\n                               sizeof(*rotate),\n                               AKT_ROTATE,\n                               true);\n\n    rotate = ak_objGet(transformFwd);\n    glm_vec4_copy(fwdAxis, rotate->val);\n\n    *destTransform = transformFwd;\n  }\n\n  /* rotation for up direction */\n  if (upAxis[3] != 0.0f) {\n    transformUp = ak_objAlloc(heap,\n                              memparent,\n                              sizeof(*rotate),\n                              AKT_ROTATE,\n                              true);\n\n    rotate = ak_objGet(transformUp);\n    glm_vec4_copy(upAxis, rotate->val);\n\n    if (*destTransform)\n      (*destTransform)->next = transformUp;\n    else\n      *destTransform = transformUp;\n  }\n}\n"
  },
  {
    "path": "src/coord/common.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\n#include \"../common.h\"\n#include \"common.h\"\n#include <cglm/cglm.h>\n\nvoid\nak_coordAxisOri(AkCoordSys * __restrict coordSys,\n                AkAxisOrientation       axis,\n                int                     ori[3]) {\n  int axisOri[3];\n  int coord[3];\n  int i, j;\n\n  ak_coordAxisToiVec3(axis, axisOri);\n  ak_coordToiVec3(coordSys, coord);\n\n  for (i = 0; i < 3; i++) {\n    for (j = 0; j < 3; j++)\n      if (axisOri[i] == coord[j])\n        ori[i] = (j + 1) * glm_sign(axisOri[j]);\n      else if (abs(axisOri[i]) == abs(coord[j]))\n        ori[i] = -(j + 1) * glm_sign(axisOri[j]);\n  }\n}\n\nvoid\nak_coordAxisOriAbs(AkCoordSys * __restrict coordSys,\n                   AkAxisOrientation       axis,\n                   int                     newAxisOri[3]) {\n  ak_coordAxisOri(coordSys, axis, newAxisOri);\n\n  newAxisOri[0] = abs(newAxisOri[0]) - 1;\n  newAxisOri[1] = abs(newAxisOri[1]) - 1;\n  newAxisOri[2] = abs(newAxisOri[2]) - 1;\n}\n"
  },
  {
    "path": "src/coord/common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_coord_common_h\n#define ak_coord_common_h\n\n#include \"../common.h\"\n\n#define AK__Z_RH {AK_AXIS_POSITIVE_X, AK_AXIS_POSITIVE_Z, AK_AXIS_POSITIVE_Y}\n#define AK__Y_RH {AK_AXIS_POSITIVE_X, AK_AXIS_POSITIVE_Y, AK_AXIS_NEGATIVE_Z}\n#define AK__X_RH {AK_AXIS_NEGATIVE_Y, AK_AXIS_POSITIVE_X, AK_AXIS_NEGATIVE_Z}\n\n#define AK__Z_LH {AK_AXIS_POSITIVE_X, AK_AXIS_POSITIVE_Z, AK_AXIS_NEGATIVE_Y}\n#define AK__Y_LH {AK_AXIS_POSITIVE_X, AK_AXIS_POSITIVE_Y, AK_AXIS_POSITIVE_Z}\n#define AK__X_LH {AK_AXIS_NEGATIVE_Y, AK_AXIS_POSITIVE_X, AK_AXIS_POSITIVE_Z}\n\n#define AK_COORD(P0, P1, P2) &(AkCoordSys){P0, P1, P2}\n\n#define AK_CVT_VEC_TO(X0, X1)                                                 \\\n  X1[a1.up]    = X0[a0.up]    * a0.s_up    * a1.s_up;                         \\\n  X1[a1.right] = X0[a0.right] * a0.s_right * a1.s_right;                      \\\n  X1[a1.fwd]   = X0[a0.fwd]   * a0.s_fwd   * a1.s_fwd;\n\n#define AK_CVT_VEC_NOSIGN_TO(X0, X1)                                          \\\n  X1[a1.up]    = X0[a0.up];                                                   \\\n  X1[a1.right] = X0[a0.right];                                                \\\n  X1[a1.fwd]   = X0[a0.fwd];\n\n#define AK_CVT_VEC(X)                                                         \\\n  AK_CVT_VEC_TO(X, tmp)                                                       \\\n  X[0] = tmp[0];                                                              \\\n  X[1] = tmp[1];                                                              \\\n  X[2] = tmp[2];\n\n#define AK_CVT_VEC_NOSIGN(X)                                                  \\\n  AK_CVT_VEC_NOSIGN_TO(X, tmp)                                                \\\n  X[0] = tmp[0];                                                              \\\n  X[1] = tmp[1];                                                              \\\n  X[2] = tmp[2];\n\nAK_INLINE\nvoid\nak_coordAxisAccessors(AkCoordSys * __restrict oldCoordSys,\n                      AkCoordSys * __restrict newCoordSys,\n                      AkAxisAccessor * __restrict a0,\n                      AkAxisAccessor * __restrict a1) {\n  AkAxisOrientation oldAxis, newAxis;\n\n  oldAxis = oldCoordSys->axis;\n  newAxis = newCoordSys->axis;\n\n  a0->s_up    = AK_GET_SIGN(oldAxis.up);\n  a0->s_right = AK_GET_SIGN(oldAxis.right);\n  a0->s_fwd   = AK_GET_SIGN(oldAxis.fwd);\n  a0->up      = abs(oldAxis.up)    - 1;\n  a0->right   = abs(oldAxis.right) - 1;\n  a0->fwd     = abs(oldAxis.fwd)   - 1;\n\n  a1->s_up    = AK_GET_SIGN(newAxis.up);\n  a1->s_right = AK_GET_SIGN(newAxis.right);\n  a1->s_fwd   = AK_GET_SIGN(newAxis.fwd);\n  a1->up      = abs(newAxis.up)    - 1;\n  a1->right   = abs(newAxis.right) - 1;\n  a1->fwd     = abs(newAxis.fwd)   - 1;\n}\n\nAK_INLINE\nvoid\nak_coordAxisCamAccessors(AkCoordSys * __restrict newCoordSys,\n                         AkAxisAccessor * __restrict a0,\n                         AkAxisAccessor * __restrict a1) {\n  AkAxisOrientation oldAxis, newAxis;\n\n  oldAxis = newCoordSys->cameraOrientation;\n  newAxis = newCoordSys->axis;\n\n  a0->s_up    = AK_GET_SIGN(oldAxis.up);\n  a0->s_right = AK_GET_SIGN(oldAxis.right);\n  a0->s_fwd   = AK_GET_SIGN(oldAxis.fwd);\n  a0->up      = abs(oldAxis.up)    - 1;\n  a0->right   = abs(oldAxis.right) - 1;\n  a0->fwd     = abs(oldAxis.fwd)   - 1;\n\n  a1->s_up    = AK_GET_SIGN(newAxis.up);\n  a1->s_right = AK_GET_SIGN(newAxis.right);\n  a1->s_fwd   = AK_GET_SIGN(newAxis.fwd);\n  a1->up      = abs(newAxis.up)    - 1;\n  a1->right   = abs(newAxis.right) - 1;\n  a1->fwd     = abs(newAxis.fwd)   - 1;\n}\n\nvoid\nak_coordAxisOri(AkCoordSys * __restrict coordSys,\n                AkAxisOrientation       axis,\n                int                     newAxisOri[3]);\n\nvoid\nak_coordAxisOriLocate(int coord1[3],\n                      int coord2[3],\n                      int    ori[3]);\n\nvoid\nak_coordAxisOriAbs(AkCoordSys * __restrict coordSys,\n                   AkAxisOrientation       axis,\n                   int                     newAxisOri[3]);\n\n#endif /* ak_coord_common_h */\n"
  },
  {
    "path": "src/coord/doc.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n\nAK_EXPORT\nvoid\nak_changeCoordSys(AkDoc * __restrict doc,\n                  AkCoordSys * newCoordSys) {\n  AkLibrary  *libGeom;\n  AkGeometry *geom;\n\n  libGeom = doc->lib.geometries;\n\n  while (libGeom) {\n    geom = (void *)libGeom->chld;\n\n    while (geom) {\n      ak_changeCoordSysGeom(geom, newCoordSys);\n      geom = (void *)geom->base.next;\n    }\n\n    libGeom = libGeom->next;\n  }\n\n  doc->coordSys = newCoordSys;\n}\n"
  },
  {
    "path": "src/coord/geom.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n\nAK_EXPORT\nvoid\nak_changeCoordSysGeom(AkGeometry * __restrict geom,\n                      AkCoordSys * newCoordSys) {\n  AkObject *primitive;\n\n  primitive = geom->gdata;\n  switch ((AkGeometryType)primitive->type) {\n    case AK_GEOMETRY_MESH:\n      ak_changeCoordSysMesh(ak_objGet(primitive), newCoordSys);\n      break;\n    case AK_GEOMETRY_SPLINE: /* TODO: */\n    case AK_GEOMETRY_BREP: /* TODO: */\n      break;\n  }\n}\n"
  },
  {
    "path": "src/coord/mesh.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n\n/* TODO: use mutex */\n\nAK_EXPORT\nvoid\nak_changeCoordSysMesh(AkMesh * __restrict mesh,\n                      AkCoordSys * newCoordSys) {\n  AkMeshPrimitive *primi;\n  AkDoc           *doc;\n  AkHeap          *heap;\n  AkInput         *input;\n  AkMap           *map;\n  AkMapItem       *mapi;\n\n  heap = ak_heap_getheap(mesh->geom);\n  doc  = heap->data;\n  map  = ak_map_new(NULL);\n\n  /* find sources to update */\n  if (!(primi = mesh->primitive))\n    return;\n\n  while (primi) {\n    input = primi->input;\n    while (input) {\n      /* TODO: other semantics which are depend on coord sys */\n      if (input->semantic == AK_INPUT_POSITION\n          || input->semantic == AK_INPUT_NORMAL) {\n        if (!input->accessor) {\n          input = input->next;\n          continue;\n        }\n        \n        ak_map_addptr(map, input->accessor);\n      }\n      input = input->next;\n    }\n    primi = primi->next;\n  }\n\n  mapi = map->root;\n  while (mapi) {\n    AkAccessor *acci;\n    AkBuffer   *buffi;\n\n    acci  = ak_getId(mapi);\n    buffi = acci->buffer;\n    if (!buffi) {\n      mapi = mapi->next;\n      continue;\n    }\n\n    /* TODO: INT, DOUBLE.. */\n    if (acci->componentType == AKT_FLOAT) {\n      ak_coordCvtVectors(doc->coordSys,\n                         buffi->data,\n                         buffi->length / acci->bytesPerComponent,\n                         newCoordSys);\n    }\n    mapi = mapi->next;\n  }\n\n  ak_map_destroy(map);\n}\n"
  },
  {
    "path": "src/coord/node.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../../include/ak/coord-util.h\"\n#include \"common.h\"\n#include <cglm/cglm.h>\n\nAK_HIDE\nbool\nak_fixInstanceTransform(AkNode         *node,\n                        AkInstanceBase *inst,\n                        AkCoordSys     *instCoordSys,\n                        AkCoordSys     *coordSys);\n\nAK_HIDE\nbool\nak_fixInstanceTransform(AkNode         *node,\n                        AkInstanceBase *inst,\n                        AkCoordSys     *instCoordSys,\n                        AkCoordSys     *coordSys) {\n  AkHeap *heap;\n  AkNode *subNode;\n\n  heap    = ak_heap_getheap(inst);\n  subNode = ak_instanceMoveToSubNodeIfNeeded(node, inst);\n\n  if (!subNode)\n    return false;\n\n  if (!subNode->transform)\n    subNode->transform = ak_heap_calloc(heap,\n                                        subNode,\n                                        sizeof(*subNode->transform));\n\n  /* fixup transform[s] */\n  ak_coordFindTransform(subNode->transform,\n                        instCoordSys,\n                        coordSys);\n\n  return true;\n}\n\nAK_EXPORT\nvoid\nak_fixNodeCoordSys(AkNode * __restrict node) {\n  AkHeap         *heap;\n  void           *parentObject;\n  AkCoordSys     *coordSys, *parentCoordSys, *instCoordSys, *coordSysToFix;\n  AkInstanceBase *inst;\n  AkInstanceBase *instArray[] = {(AkInstanceBase *)node->geometry,\n                                 (AkInstanceBase *)node->node,\n                                 node->camera,\n                                 node->light};\n  int             i, instArrayLen, instTransCount;\n  bool            hasCoordSys;\n\n  hasCoordSys    = ak_hasCoordSys(node);\n  coordSys       = ak_getCoordSys(node);\n  instArrayLen   = AK_ARRAY_LEN(instArray);\n  parentCoordSys = NULL;\n  coordSysToFix  = NULL;\n  instTransCount = 0;\n\n  /* fix instance transforms */\n  for (i = 0; i < instArrayLen; i++) {\n    inst = instArray[i];\n    while (inst) {\n      void *instObj;\n      instObj = ak_instanceObject(inst);\n      if (instObj && ak_hasCoordSys(instObj)) {\n        instCoordSys = ak_getCoordSys(instObj);\n        if (instCoordSys != coordSys) {\n          bool movedToSub;\n          movedToSub = ak_fixInstanceTransform(node,\n                                               inst,\n                                               instCoordSys,\n                                               coordSys);\n          if (!movedToSub) {\n            coordSysToFix = instCoordSys;\n            instTransCount++;\n          }\n        }\n      }\n      inst = inst->next;\n    }\n  }\n\n  heap = ak_heap_getheap(node);\n  switch (ak_opt_get(AK_OPT_COORD_CONVERT_TYPE)) {\n    case AK_COORD_CVT_FIX_TRANSFORM: {\n      if (instTransCount == 1 && coordSysToFix)\n        coordSys = coordSysToFix;\n\n      parentObject = node->parent;\n      if (!parentObject)\n        parentObject = ak_mem_parent(node);\n\n      if (parentObject)\n        parentCoordSys = ak_getCoordSys(parentObject);\n      else\n        parentCoordSys = AK_YUP;\n\n      if (parentCoordSys == coordSys)\n        return;\n\n      if (hasCoordSys\n          /* don't change nodes in library_nodes */\n          || (!node->parent && ak_typeid(parentObject) == AKT_SCENE)) {\n\n        if (!node->transform)\n          node->transform = ak_heap_calloc(heap,\n                                           node,\n                                           sizeof(*node->transform));\n\n        /* fixup transform[s] */\n        ak_coordFindTransform(node->transform,\n                              coordSys,\n                              parentCoordSys);\n      }\n      break;\n    }\n    case AK_COORD_CVT_ALL:\n      if (node->transform)\n        ak_coordCvtNodeTransforms(ak_heap_data(heap), node);\n      break;\n    default:\n      break;\n  }\n}\n"
  },
  {
    "path": "src/coord/scene.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../../include/ak/coord-util.h\"\n#include \"common.h\"\n#include <cglm/cglm.h>\n\nAK_EXPORT\nvoid\nak_fixSceneCoordSys(struct AkVisualScene * __restrict scene) {\n  AkHeap         *heap;\n  AkCoordSys     *newCoordSys;\n  AkNode         *node;\n\n  heap        = ak_heap_getheap(scene);\n  newCoordSys = (void *)ak_opt_get(AK_OPT_COORD);\n  node        = scene->node;\n  while (node) {\n    if (!node->transform)\n      node->transform = ak_heap_calloc(heap,\n                                       node,\n                                       sizeof(*node->transform));\n\n    /* fixup transform[s] */\n    ak_coordFindTransform(node->transform,\n                          ak_getCoordSys(node),\n                          newCoordSys);\n\n    node = node->next;\n  }\n}\n"
  },
  {
    "path": "src/coord/transform.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"common.h\"\n#include <cglm/cglm.h>\n#include <float.h>\n\nAK_EXPORT\nvoid\nak_coordCvtTransform(AkCoordSys *oldCoordSystem,\n                     AkFloat4x4  transform,\n                     AkCoordSys *newCoordSystem) {\n  mat4               rot, scale;\n  vec4               pos;\n  vec3               scalev, angles, tmp;\n  AkAxisAccessor     a0, a1;\n  ivec3              eulerNew;\n  AkAxisRotDirection rotDirection;\n\n  rotDirection = (oldCoordSystem->rotDirection + 1)\n                      * (newCoordSystem->rotDirection + 1);\n\n  ak_coordAxisAccessors(oldCoordSystem, newCoordSystem, &a0, &a1);\n\n  /* decompose rotation and scaling factors */\n  glm_decompose_rs(transform, rot, scalev);\n\n  /* extract euler angles XYZ */\n  glm_euler_angles(rot, angles);\n  AK_CVT_VEC(angles);\n\n  /* find new euler sequence */\n  ak_coordAxisOriAbs(newCoordSystem,\n                     oldCoordSystem->axis,\n                     eulerNew);\n\n  /* apply new rotation direction */\n  glm_vec3_scale(angles, rotDirection, angles);\n\n  /* apply new rotation */\n  glm_euler_by_order(angles,\n                     glm_euler_order(eulerNew),\n                     rot);\n\n  /* find new scaling factors */\n  AK_CVT_VEC_NOSIGN(scalev);\n\n  /* apply new scaling factors */\n  glm_mat4_copy(GLM_MAT4_IDENTITY, scale);\n  scale[0][0] = scalev[0];\n  scale[1][1] = scalev[1];\n  scale[2][2] = scalev[2];\n\n  glm_vec4_copy(transform[3], pos);\n  glm_mul(rot, scale, transform);\n\n  /* apply new translation */\n  AK_CVT_VEC(pos)\n\n  glm_vec4_copy(pos, transform[3]);\n}\n\nAK_EXPORT\nvoid\nak_coordFindTransform(AkTransform *transform,\n                      AkCoordSys  *oldCoordSys,\n                      AkCoordSys  *newCoordSys) {\n  AkHeap        *heap;\n  AkObject      *firstTrans, *lastTrans;\n  AkAxisAccessor a0, a1;\n  ivec3          oriOld, oriNew;\n  vec3           x1, y1, z1, oth, axis;\n  float          angle;\n\n  if (oldCoordSys == newCoordSys\n      || ak_coordOrientationIsEq(oldCoordSys, newCoordSys))\n    return;\n\n  firstTrans = lastTrans = NULL;\n\n  heap = ak_heap_getheap(transform);\n  if (!transform->base)\n    ak_transformFreeBase(transform);\n\n  ak_coordAxisAccessors(oldCoordSys, newCoordSys, &a0, &a1);\n\n  ak_coordToiVec3(oldCoordSys, oriOld);\n  ak_coordToiVec3(newCoordSys, oriNew);\n\n  glm_vec3_broadcast(0.0f, x1);\n  glm_vec3_broadcast(0.0f, y1);\n  glm_vec3_broadcast(0.0f, z1);\n  x1[abs(oriOld[0]) - 1] = (float)glm_sign(oriOld[0]);\n  y1[abs(oriOld[1]) - 1] = (float)glm_sign(oriOld[1]);\n  z1[abs(oriOld[2]) - 1] = (float)glm_sign(oriOld[2]);\n\n  /* step-1: X axis; a rotation will be enough */\n\n  /* find rotation axis */\n  glm_vec3_broadcast(0.0f, oth);\n  oth[abs(oriNew[0]) - 1] = (float)glm_sign(oriNew[0]);\n  glm_vec3_cross(x1, oth, axis);\n\n  /* angle for x axis */\n  angle = glm_vec3_angle(x1, oth);\n\n  /* append transform */\n  if (angle != 0.0f) {\n    AkObject *rotObj;\n    AkRotate *rot;\n\n    rotObj = ak_objAlloc(heap,\n                         transform,\n                         sizeof(*rotObj),\n                         AKT_ROTATE,\n                         true);\n\n    rot = ak_objGet(rotObj);\n    glm_vec3_copy(axis, rot->val);\n    rot->val[3] = angle;\n\n\n    /* rotate y and z */\n    glm_vec3_rotate(y1, angle, axis);\n    glm_vec3_rotate(z1, angle, axis);\n\n    firstTrans = lastTrans = rotObj;\n  }\n\n  /* step-2: Y axis; an extra rotation around X will be enough */\n\n  /* find rotation axis */\n  glm_vec3_broadcast(0.0f, oth);\n  oth[abs(oriNew[1]) - 1] = (float)glm_sign(oriNew[1]);\n  glm_vec3_cross(y1, oth, axis);\n\n  /* angle for y axis */\n  angle = glm_vec3_angle(y1, oth);\n\n  /* append transform */\n  if (angle != 0.0f) {\n    AkObject *rotObj;\n    AkRotate *rot;\n\n    rotObj = ak_objAlloc(heap,\n                         transform,\n                         sizeof(*rotObj),\n                         AKT_ROTATE,\n                         true);\n\n    rot = ak_objGet(rotObj);\n    glm_vec3_copy(axis, rot->val);\n    rot->val[3] = angle;\n\n    /* rotate z, we know that x will keep it orientation */\n    glm_vec3_rotate(z1, angle, axis);\n\n    if (lastTrans)\n      lastTrans->next = rotObj;\n    else\n      firstTrans = rotObj;\n    lastTrans = rotObj;\n  }\n\n  /* step-3: Z axis; we can't add rotation, we need negative scale */\n\n  /* check old and new Z axes are same */\n  glm_vec3_broadcast(0.0f, oth);\n  oth[abs(oriNew[2]) - 1] = (float)glm_sign(oriNew[2]);\n  if (!glm_vec3_eqv_eps(z1, oth)) {\n    /* fix Z by negative scale */\n    AkObject *scaleObj;\n    AkScale  *scale;\n\n    scaleObj = ak_objAlloc(heap,\n                           transform,\n                           sizeof(*scaleObj),\n                           AKT_SCALE,\n                           true);\n\n    scale = ak_objGet(scaleObj);\n    scale->val[0] =  1.0f;\n    scale->val[1] =  1.0f;\n    scale->val[2] = -1.0f;\n\n    if (lastTrans)\n      lastTrans->next = scaleObj;\n    else\n      firstTrans = scaleObj;\n  }\n\n  transform->base = firstTrans;\n}\n"
  },
  {
    "path": "src/coord/transforms.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"common.h\"\n#include <cglm/cglm.h>\n\nAK_EXPORT\nvoid\nak_coordCvtNodeTransforms(AkDoc  * __restrict doc,\n                          AkNode * __restrict node) {\n  AkCoordSys    *oldCoordSys, *newCoordsys;\n  AkObject      *transform, *lastTransform;\n  vec3           tmp;\n  AkAxisAccessor a0, a1;\n\n  if (!node->transform)\n    return;\n\n  oldCoordSys = doc->coordSys;\n  newCoordsys = (void *)ak_opt_get(AK_OPT_COORD);\n\n  ak_coordAxisAccessors(oldCoordSys, newCoordsys, &a0, &a1);\n\n  transform = lastTransform = node->transform->item;\n\n  while (transform) {\n    switch (transform->type) {\n      case AKT_MATRIX: {\n        AkMatrix *matrix;\n        matrix = ak_objGet(transform);\n\n        ak_coordCvtTransform(oldCoordSys,\n                             matrix->val,\n                             newCoordsys);\n        break;\n      }\n      case AKT_LOOKAT: {\n        AkLookAt *lookAt;\n        lookAt = ak_objGet(transform);\n\n        /* convert eye vector */\n        AK_CVT_VEC(lookAt->val[0]);\n\n        /* convert center vector */\n        AK_CVT_VEC(lookAt->val[1]);\n\n        /* convert up vector */\n        AK_CVT_VEC(lookAt->val[2]);\n        break;\n      }\n      case AKT_ROTATE: {\n        AkRotate *rotate;\n        rotate = ak_objGet(transform);\n\n        AK_CVT_VEC(rotate->val);\n        break;\n      }\n      case AKT_QUATERNION: {\n        AkQuaternion *quat;\n        float        *val;\n\n        quat = ak_objGet(transform);\n        val  = quat->val;\n\n        AK_CVT_VEC(val);\n        break;\n      }\n      case AKT_SCALE: {\n        AkScale *scale;\n        scale = ak_objGet(transform);\n\n        AK_CVT_VEC_NOSIGN(scale->val);\n        break;\n      }\n      case AKT_TRANSLATE: {\n        AkTranslate *translate;\n\n        translate = ak_objGet(transform);\n        AK_CVT_VEC(translate->val);\n        break;\n      }\n      case AKT_SKEW: {\n        AkSkew *skew;\n        skew = ak_objGet(transform);\n\n        /* TODO: */\n        break;\n      }\n    }\n\n    lastTransform = transform;\n    transform = transform->next;\n  }\n\n  /* extra rotation for camera orientation */\n  if (node->flags & AK_NODEF_FIXED_COORD) {\n    AkObject *extraTransformItem;\n    ak_coordRotNodeForFixedCoord(doc, node, &extraTransformItem);\n\n    if (extraTransformItem) {\n      if (lastTransform)\n        lastTransform->next = extraTransformItem;\n      else {\n        node->transform = ak_heap_calloc(ak_heap_getheap(extraTransformItem),\n                                         node,\n                                         sizeof(*node->transform));\n        node->transform->item = extraTransformItem;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/coord/vector.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"common.h\"\n\nAK_EXPORT\nvoid\nak_coordCvtVectorTo(AkCoordSys *oldCoordSystem,\n                    float      *oldVector,\n                    AkCoordSys *newCoordSystem,\n                    float      *newVector) {\n  AkAxisAccessor a0, a1;\n\n  ak_coordAxisAccessors(oldCoordSystem, newCoordSystem, &a0, &a1);\n  AK_CVT_VEC_TO(oldVector, newVector)\n}\n\nAK_EXPORT\nvoid\nak_coordCvtVector(AkCoordSys *oldCoordSystem,\n                  float      *vector,\n                  AkCoordSys *newCoordSystem) {\n  float tmp[3];\n  ak_coordCvtVectorTo(oldCoordSystem,\n                      vector,\n                      newCoordSystem,\n                      tmp);\n\n  vector[0] = tmp[0];\n  vector[1] = tmp[1];\n  vector[2] = tmp[2];\n}\n\nAK_EXPORT\nvoid\nak_coordCvtVectors(AkCoordSys *oldCoordSystem,\n                   float      *vectorArray,\n                   size_t      len,\n                   AkCoordSys *newCoordSystem) {\n  AkAxisAccessor a0, a1;\n\n  size_t i;\n  float  tmp[3];\n\n  ak_coordAxisAccessors(oldCoordSystem, newCoordSystem, &a0, &a1);\n\n  for (i = 0; i < len; i += 3) {\n    AK_CVT_VEC_TO((vectorArray + i), tmp)\n    vectorArray[i]     = tmp[0];\n    vectorArray[i + 1] = tmp[1];\n    vectorArray[i + 2] = tmp[2];\n  }\n}\n"
  },
  {
    "path": "src/data.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"data.h\"\n#include <assert.h>\n\nAkDataContext*\nak_data_new(void   *memparent,\n            size_t  nodeitems,\n            size_t  itemsize,\n            AkCmpFn cmp) {\n  AkHeap        *heap;\n  AkDataContext *ctx;\n\n  heap = ak_heap_getheap(memparent);\n  ctx  = ak_heap_calloc(heap, memparent, sizeof(*ctx));\n\n  ctx->nodesize = nodeitems * itemsize;\n  ctx->itemsize = itemsize;\n  ctx->heap     = heap;\n  ctx->cmp      = cmp;\n\n  return ctx;\n}\n\nint\nak_data_append(AkDataContext *dctx, void *item) {\n  AkDataChunk *chunk;\n  void        *mem;\n  size_t       size;\n\n  size = dctx->itemsize;\n  assert(dctx->nodesize > size);\n\n  if (dctx->usedsize + size > dctx->size) {\n    chunk = ak_heap_alloc(dctx->heap,\n                          dctx,\n                          sizeof(*chunk) + dctx->nodesize);\n    chunk->usedsize = 0;\n    chunk->next     = NULL;\n\n    if (dctx->last)\n      dctx->last->next = chunk;\n\n    dctx->last  = chunk;\n    dctx->size += dctx->nodesize;\n\n    if (!dctx->data)\n      dctx->data = chunk;\n  } else {\n    chunk = dctx->last;\n  }\n\n  mem = chunk->data + chunk->usedsize;\n  memcpy(mem, item, size);\n\n  chunk->usedsize += size;\n  dctx->usedsize  += size;\n  dctx->itemcount++;\n\n  return (int)dctx->itemcount - 1;\n}\n\nint\nak_data_append_unq(AkDataContext *dctx, void *item) {\n  int idx;\n\n  idx = ak_data_exists(dctx, item);\n  if (idx != -1)\n    return idx;\n\n  return ak_data_append(dctx, item);\n}\n\nvoid\nak_data_walk(AkDataContext *dctx) {\n  AkDataChunk *chunk;\n  void  *data;\n  size_t isz, csz, i, index;\n\n  if (!dctx->data)\n    return;\n\n  isz   = dctx->itemsize;\n  chunk = dctx->data;\n  index = 0;\n\n  while (chunk) {\n    csz = dctx->nodesize - chunk->usedsize;\n    for (i = isz; i < csz; i += isz) {\n      data = chunk->data;\n      dctx->walkFn(dctx, data, index++);\n    }\n    chunk = chunk->next;\n  }\n}\n\nint\nak_data_exists(AkDataContext *dctx, void *item) {\n  AkDataChunk *chunk;\n  char  *pmem;\n  size_t isz, csz, i;\n  int    idx;\n  bool   found;\n\n  if (!dctx->data)\n    return -1;\n\n  found = false;\n  idx   = 0;\n  isz   = dctx->itemsize;\n  chunk = dctx->data;\n\n  while (chunk) {\n    csz  = chunk->usedsize;\n    pmem = chunk->data;\n\n    for (i = 0; i < csz; i += isz) {\n      if (dctx->cmp(pmem, item) == 0)\n        return idx;\n\n      pmem += isz;\n      idx++;\n    }\n    chunk = chunk->next;\n  }\n\n  if (!found)\n    idx = -1;\n\n  return idx;\n}\n\nsize_t\nak_data_join(AkDataContext *dctx,\n             void          *buff,\n             size_t         bytesoff,\n             size_t         stride) {\n  AkDataChunk *chunk;\n  char        *pmem, *data;\n  size_t isz, csz, i, count;\n\n  count = 0;\n\n  if (!dctx->data)\n    return count;\n\n  isz   = dctx->itemsize;\n  chunk = dctx->data;\n\n  pmem = buff;\n  \n  if (bytesoff > 0)\n    pmem += bytesoff;\n  \n  if (stride == 0)\n    stride = isz;\n\n  while (chunk) {\n    csz  = chunk->usedsize;\n    data = chunk->data;\n    for (i = 0; i < csz; i += isz) {\n      memcpy(pmem, data, isz);\n      pmem += stride;\n      data += isz;\n      count++;\n    }\n    chunk = chunk->next;\n  }\n\n  return count;\n}\n"
  },
  {
    "path": "src/data.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_data_h\n#define ak_data_h\n\n#include \"common.h\"\n\nstruct AkDataContext;\n\ntypedef void (*AkDataContextWalkFn)(struct AkDataContext *dctx,\n                                    void                 *item,\n                                    size_t                index);\n\ntypedef struct AkDataChunk {\n  struct AkDataChunk *next;\n  size_t              usedsize;\n  char                data[];\n} AkDataChunk;\n\ntypedef struct AkDataContext {\n  void                *memparent;\n  AkHeap              *heap;\n  AkDataChunk         *data;\n  AkDataChunk         *last;\n  AkCmpFn              cmp;\n  AkDataContextWalkFn  walkFn;\n  size_t               nodesize;\n  size_t               size;\n  size_t               usedsize;\n  size_t               itemsize;\n  size_t               itemcount;\n} AkDataContext;\n\nAkDataContext*\nak_data_new(void   *memparent,\n            size_t  nodeitems,\n            size_t  itemsize,\n            AkCmpFn cmp);\n\n/*!\n * @brief join batch data into continued array\n *\n * @param dctx     data context\n * @param buff     buffer\n * @param stride   bytes stride\n * @param buff     bytesgap gap between item stride\n *\n * @return array count\n */\nsize_t\nak_data_join(AkDataContext *dctx,\n             void          *buff,\n             size_t         stride,\n             size_t         bytesgap);\n\n/*!\n * @brief append item, this will copy data by size of itemsize into context\n *\n * @param dctx data context\n * @param item item\n *\n * @return index\n */\nint\nak_data_append(AkDataContext *dctx, void *item);\n\n/*!\n * @brief append if not exists and return appended item index\n *\n * @param dctx data context\n * @param item item\n *\n * @return index\n */\nint\nak_data_append_unq(AkDataContext *dctx, void *item);\n\n/*!\n * @brief walk through by walkFn\n *\n * @param dctx data context\n */\nvoid\nak_data_walk(AkDataContext *dctx);\n\n/*!\n * @brief check if item is exists\n *\n * @param dctx data context\n * @param item item\n *\n * @return returns item index\n */\nint\nak_data_exists(AkDataContext *dctx, void *item);\n\n#endif /* ak_data_h */\n"
  },
  {
    "path": "src/decoders/gltf/draco/assetkit_draco.cc",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <draco/compression/decode.h>\n\n#include <memory>\n#include <errno.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n\n#if defined(_WIN32)\n#  define AK_DRACO_EXPORT __declspec(dllexport)\n#else\n#  define AK_DRACO_EXPORT __attribute__((visibility(\"default\")))\n#endif\n\ntypedef struct AkHeap AkHeap;\ntypedef struct AkMeshPrimitive AkMeshPrimitive;\ntypedef struct FListItem FListItem;\ntypedef struct RBTree RBTree;\n\ntypedef enum AkTypeId {\n  AKT_NONE   = 0,\n  AKT_INT    = 6,\n  AKT_FLOAT  = 10,\n  AKT_BYTE   = 29,\n  AKT_UBYTE  = 30,\n  AKT_SHORT  = 31,\n  AKT_USHORT = 32,\n  AKT_UINT   = 28\n} AkTypeId;\n\ntypedef enum json_type {\n  JSON_OBJECT = 1,\n  JSON_ARRAY  = 2,\n  JSON_STRING = 3\n} json_type_t;\n\ntypedef struct json_t {\n  struct json_t *parent;\n  struct json_t *next;\n  const char    *key;\n  void          *value;\n  int32_t        valsize;\n  int32_t        keysize;\n  json_type_t    type;\n} json_t;\n\ntypedef struct AkBuffer {\n  const char *name;\n  void       *data;\n  size_t      length;\n} AkBuffer;\n\ntypedef struct AkAccessor {\n  AkBuffer *buffer;\n  const char *name;\n  void       *min;\n  void       *max;\n  size_t      byteOffset;\n  size_t      byteStride;\n  size_t      byteLength;\n  uint32_t    count;\n  uint32_t    bytesPerComponent;\n  int32_t     componentSize;\n  AkTypeId    componentType;\n  uint32_t    componentCount;\n  size_t      fillByteSize;\n  int32_t     gpuTarget;\n  bool        normalized;\n  AkTypeId    originalComponentType;\n  bool        originallyNormalized;\n} AkAccessor;\n\ntypedef struct AkLibrary {\n  struct AkLibrary *next;\n  const char       *name;\n  void             *extra;\n  void             *chld;\n  uint64_t          count;\n} AkLibrary;\n\ntypedef struct AkLibraries {\n  AkLibrary *cameras;\n  AkLibrary *lights;\n  AkLibrary *effects;\n  AkLibrary *libimages;\n  AkLibrary *materials;\n  AkLibrary *geometries;\n  AkLibrary *controllers;\n  AkLibrary *visualScenes;\n  AkLibrary *nodes;\n  AkLibrary *animations;\n  FListItem *buffers;\n  FListItem *accessors;\n  FListItem *textures;\n  FListItem *samplers;\n  FListItem *images;\n  void      *morphs;\n  void      *skins;\n} AkLibraries;\n\ntypedef struct AkDoc {\n  void        *inf;\n  void        *coordSys;\n  void        *unit;\n  void        *extra;\n  void        *reserved;\n  void        *userData;\n  float        loadMillis;\n  AkLibraries  lib;\n} AkDoc;\n\ntypedef struct AkBufferView {\n  AkBuffer   *buffer;\n  const char *name;\n  size_t      byteOffset;\n  size_t      byteLength;\n  size_t      byteStride;\n} AkBufferView;\n\ntypedef struct AkGLTFState {\n  AkHeap       *heap;\n  AkDoc        *doc;\n  json_t       *root;\n  void         *tmpParent;\n  FListItem    *buffers;\n  RBTree       *bufferMap;\n  FListItem    *bufferViews;\n  RBTree       *skinBound;\n  RBTree       *meshTargets;\n  void         *bindata;\n  void         *defaultMaterial;\n  void         *meshopt;\n  void         *draco;\n  size_t        bindataLen;\n  bool          stop;\n  bool          isbinary;\n} AkGLTFState;\n\nextern \"C\" {\nvoid *ak_heap_alloc(AkHeap *heap, void *parent, size_t size);\nvoid *ak_heap_calloc(AkHeap *heap, void *parent, size_t size);\nvoid  flist_sp_insert(FListItem **first, void *value);\nvoid *flist_sp_at(FListItem **first, int32_t index);\n}\n\nextern \"C\" {\n#include \"io/gltf/strpool.h\"\n}\n\nstatic\nint32_t\nak_draco_json_int32(const json_t * __restrict obj, int32_t def) {\n  char *end;\n  long  val;\n\n  if (!obj || obj->type != JSON_STRING || !obj->value)\n    return def;\n\n  errno = 0;\n  val   = strtol((const char *)obj->value, &end, 10);\n  if (errno != 0 || end == (const char *)obj->value)\n    return def;\n\n  return (int32_t)val;\n}\n\nstatic\njson_t*\nak_draco_json_get(const json_t * __restrict object,\n                  const char   * __restrict key) {\n  json_t *it;\n  size_t  keysize;\n\n  if (!object || object->type != JSON_OBJECT || !key)\n    return NULL;\n\n  keysize = strlen(key);\n  it      = (json_t *)object->value;\n  while (it\n         && ((size_t)it->keysize != keysize\n             || strncmp(it->key, key, keysize) != 0))\n    it = it->next;\n\n  return it;\n}\n\n#define json_get   ak_draco_json_get\n#define json_int32 ak_draco_json_int32\n\nstatic\njson_t*\nak_draco_json_getn(const json_t * __restrict object,\n                   const char   * __restrict key,\n                   size_t                    keysize) {\n  json_t *it;\n\n  if (!object || object->type != JSON_OBJECT || !key)\n    return NULL;\n\n  it = (json_t *)object->value;\n  while (it\n         && ((size_t)it->keysize != keysize\n             || strncmp(it->key, key, keysize) != 0))\n    it = it->next;\n\n  return it;\n}\n\nstatic\nsize_t\nak_draco_component_size(AkTypeId type) {\n  switch (type) {\n    case AKT_BYTE:\n    case AKT_UBYTE:\n      return 1;\n    case AKT_SHORT:\n    case AKT_USHORT:\n      return 2;\n    case AKT_INT:\n    case AKT_UINT:\n    case AKT_FLOAT:\n      return 4;\n    default:\n      break;\n  }\n\n  return 0;\n}\n\nstatic\nbool\nak_draco_store_value(const draco::PointAttribute * __restrict att,\n                     draco::PointIndex                       pidx,\n                     AkAccessor                 * __restrict acc,\n                     char                       * __restrict dst) {\n  draco::AttributeValueIndex avi;\n  int8_t                     comps;\n\n  if (!att || !acc || !dst)\n    return false;\n\n  avi   = att->mapped_index(pidx);\n  comps = (int8_t)acc->componentCount;\n\n  switch (acc->componentType) {\n    case AKT_BYTE:\n      return att->ConvertValue<int8_t>(avi, comps, (int8_t *)dst);\n    case AKT_UBYTE:\n      return att->ConvertValue<uint8_t>(avi, comps, (uint8_t *)dst);\n    case AKT_SHORT:\n      return att->ConvertValue<int16_t>(avi, comps, (int16_t *)dst);\n    case AKT_USHORT:\n      return att->ConvertValue<uint16_t>(avi, comps, (uint16_t *)dst);\n    case AKT_INT:\n      return att->ConvertValue<int32_t>(avi, comps, (int32_t *)dst);\n    case AKT_UINT:\n      return att->ConvertValue<uint32_t>(avi, comps, (uint32_t *)dst);\n    case AKT_FLOAT:\n      return att->ConvertValue<float>(avi, comps, (float *)dst);\n    default:\n      break;\n  }\n\n  return false;\n}\n\nstatic\nbool\nak_draco_fill_attribute(AkGLTFState              * __restrict gst,\n                        const draco::Mesh        * __restrict mesh,\n                        AkAccessor               * __restrict acc,\n                        const draco::PointAttribute * __restrict att) {\n  AkBuffer *buff;\n  char     *dst;\n  size_t    compSize;\n  size_t    stride;\n  size_t    len;\n  uint32_t  i;\n\n  if (!gst || !mesh || !acc || !att)\n    return false;\n\n  if (acc->count != (uint32_t)mesh->num_points())\n    return false;\n\n  compSize = ak_draco_component_size(acc->componentType);\n  if (compSize == 0 || acc->componentCount == 0)\n    return false;\n\n  stride = compSize * acc->componentCount;\n  len    = stride * acc->count;\n  buff   = (AkBuffer *)ak_heap_calloc(gst->heap, gst->doc, sizeof(*buff));\n\n  buff->data   = ak_heap_alloc(gst->heap, buff, len);\n  buff->length = len;\n  dst          = (char *)buff->data;\n\n  for (i = 0; i < acc->count; i++) {\n    if (!ak_draco_store_value(att,\n                              draco::PointIndex(i),\n                              acc,\n                              dst + (size_t)i * stride))\n      return false;\n  }\n\n  acc->buffer            = buff;\n  acc->byteOffset        = 0;\n  acc->bytesPerComponent = (uint32_t)compSize;\n  acc->fillByteSize      = stride;\n  acc->byteStride        = stride;\n  acc->byteLength        = len;\n\n  flist_sp_insert(&gst->doc->lib.buffers, buff);\n\n  return true;\n}\n\nstatic\nbool\nak_draco_write_index(char     * __restrict dst,\n                     AkTypeId              type,\n                     uint32_t              val) {\n  switch (type) {\n    case AKT_UBYTE:\n      if (val > UINT8_MAX) return false;\n      *(uint8_t *)dst = (uint8_t)val;\n      return true;\n    case AKT_USHORT:\n      if (val > UINT16_MAX) return false;\n      *(uint16_t *)dst = (uint16_t)val;\n      return true;\n    case AKT_UINT:\n      *(uint32_t *)dst = val;\n      return true;\n    default:\n      break;\n  }\n\n  return false;\n}\n\nstatic\nbool\nak_draco_fill_indices(AkGLTFState       * __restrict gst,\n                      const draco::Mesh * __restrict mesh,\n                      AkAccessor        * __restrict acc) {\n  AkBuffer *buff;\n  char     *dst;\n  size_t    compSize;\n  size_t    len;\n  uint32_t  faceCount;\n  uint32_t  idxCount;\n  uint32_t  f;\n  uint32_t  c;\n  uint32_t  outIdx;\n  const draco::Mesh::Face *face;\n\n  if (!gst || !mesh || !acc)\n    return false;\n\n  faceCount = (uint32_t)mesh->num_faces();\n  idxCount  = faceCount * 3;\n\n  if (acc->count != idxCount)\n    return false;\n\n  compSize = ak_draco_component_size(acc->componentType);\n  if (compSize == 0)\n    return false;\n\n  len          = compSize * idxCount;\n  buff         = (AkBuffer *)ak_heap_calloc(gst->heap, gst->doc, sizeof(*buff));\n  buff->data   = ak_heap_alloc(gst->heap, buff, len);\n  buff->length = len;\n  dst          = (char *)buff->data;\n  outIdx       = 0;\n  face         = NULL;\n\n  for (f = 0; f < faceCount; f++) {\n    face = &mesh->face(draco::FaceIndex(f));\n\n    for (c = 0; c < 3; c++, outIdx++) {\n      if (!ak_draco_write_index(dst + (size_t)outIdx * compSize,\n                                acc->componentType,\n                                (uint32_t)(*face)[c].value()))\n        return false;\n    }\n  }\n\n  acc->buffer            = buff;\n  acc->byteOffset        = 0;\n  acc->bytesPerComponent = (uint32_t)compSize;\n  acc->componentCount    = 1;\n  acc->fillByteSize      = compSize;\n  acc->byteStride        = compSize;\n  acc->byteLength        = len;\n\n  flist_sp_insert(&gst->doc->lib.buffers, buff);\n\n  return true;\n}\n\nstatic\nbool\nak_draco_fill_primitive(AkGLTFState       * __restrict gst,\n                        AkMeshPrimitive  * __restrict prim,\n                        const json_t     * __restrict jprim,\n                        const json_t     * __restrict jdraco,\n                        const draco::Mesh * __restrict mesh) {\n  const json_t *jattrs;\n  const json_t *jdattrs;\n  const json_t *jattr;\n  const json_t *jdattr;\n  const json_t *jidx;\n  AkAccessor   *acc;\n  int32_t       accIdx;\n  int32_t       attId;\n\n  if (!gst || !prim || !jprim || !jdraco || !mesh)\n    return false;\n\n  jidx = json_get(jprim, _s_gltf_indices);\n  if (jidx) {\n    accIdx = json_int32(jidx, -1);\n    acc    = (AkAccessor *)flist_sp_at(&gst->doc->lib.accessors, accIdx);\n    if (!acc || !ak_draco_fill_indices(gst, mesh, acc))\n      return false;\n  }\n\n  jattrs  = json_get(jprim,   _s_gltf_attributes);\n  jdattrs = json_get(jdraco,  _s_gltf_attributes);\n  if (!jattrs || !jdattrs)\n    return false;\n\n  jattr = (json_t *)jattrs->value;\n  while (jattr) {\n    jdattr = ak_draco_json_getn(jdattrs, jattr->key, (size_t)jattr->keysize);\n    if (!jdattr)\n      return false;\n\n    accIdx = json_int32(jattr,  -1);\n    attId  = json_int32(jdattr, -1);\n    acc    = (AkAccessor *)flist_sp_at(&gst->doc->lib.accessors, accIdx);\n    if (!acc)\n      return false;\n\n    if (!ak_draco_fill_attribute(gst,\n                                 mesh,\n                                 acc,\n                                 mesh->GetAttributeByUniqueId((uint32_t)attId)))\n      return false;\n\n    jattr = jattr->next;\n  }\n\n  return true;\n}\n\nextern \"C\"\nAK_DRACO_EXPORT\nint\nak_draco_decode_gltf_primitive(AkGLTFState     *gst,\n                               AkMeshPrimitive *prim,\n                               const json_t    *jprim,\n                               const json_t    *jdraco) {\n  AkBufferView *bv;\n  AkBuffer     *buff;\n  const json_t *it;\n  const char   *src;\n  size_t        off;\n  int32_t       bvIdx;\n\n  draco::Decoder       dec;\n  draco::DecoderBuffer dbuf;\n  std::unique_ptr<draco::Mesh> mesh;\n\n  if (!gst || !prim || !jprim || !jdraco)\n    return -1;\n\n  it    = json_get(jdraco, _s_gltf_bufferView);\n  bvIdx = it ? json_int32(it, -1) : -1;\n  if (bvIdx < 0)\n    return -1;\n\n  bv = (AkBufferView *)flist_sp_at(&gst->bufferViews, bvIdx);\n  if (!bv || !(buff = bv->buffer) || !buff->data)\n    return -1;\n\n  if (bv->byteOffset > buff->length\n      || bv->byteLength == 0\n      || bv->byteLength > buff->length - bv->byteOffset)\n    return -1;\n\n  off = bv->byteOffset;\n  src = (const char *)buff->data + off;\n  dbuf.Init(src, bv->byteLength);\n\n  {\n    draco::StatusOr<std::unique_ptr<draco::Mesh> > meshRes =\n      dec.DecodeMeshFromBuffer(&dbuf);\n\n    if (!meshRes.ok())\n      return -1;\n\n    mesh = std::move(meshRes).value();\n  }\n  if (!mesh)\n    return -1;\n\n  return ak_draco_fill_primitive(gst, prim, jprim, jdraco, mesh.get()) ? 0 : -1;\n}\n"
  },
  {
    "path": "src/decoders/gltf/ktx2/assetkit_ktx2.cc",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * AssetKit KTX2 / BasisU image decoder shim — KHR_texture_basisu support.\n *\n * Compile: links against `libktx.a` (KhronosGroup/KTX-Software) which\n * provides KTX2 container parsing + BasisU transcoding (UASTC / ETC1S →\n * RGBA8 by default; the host can request a different target). Build flag:\n * AK_BUILD_GLTF_KTX2_DECODER. CMake FetchContent pulls KTX-Software when\n * not pre-installed.\n *\n * Runtime entry: AssetKit dlopens this dylib via\n * AK_OPT_GLTF_KTX2_DECODER_PATH (or autoload) and calls\n * `assetkit_ktx2_decode` with raw KTX2 bytes (read from a bufferView or\n * resolved URI). The shim transcodes to RGBA8 and returns the decoded\n * pixel buffer + width/height/channels so the caller can wire it into an\n * AkImage / AkBuffer pair.\n *\n * Why a separate shim and not in-tree libktx: KTX-Software is large\n * (>15 MB built) and pulls in BasisU's substantial codec (~5 MB). Most\n * AssetKit consumers don't ship KTX2-encoded textures; making it\n * runtime-loadable keeps the core build slim. Apps that need it drop\n * the dylib next to their binary.\n */\n\n#include <ktx.h>\n\n#include <algorithm>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n\n#if defined(_WIN32)\n#  define AK_KTX2_EXPORT __declspec(dllexport)\n#else\n#  define AK_KTX2_EXPORT __attribute__((visibility(\"default\")))\n#endif\n\n/*--------------------------------------------------------------------*/\n/* Decoded image descriptor returned to AssetKit. The shim allocates  */\n/* `data` with malloc; the caller must `free` it after copying into   */\n/* an AkBuffer (or hand ownership over). Mirrors the simple pattern   */\n/* of WebP/HEIF decoder shims commonly found in 3D asset toolchains.  */\n/*--------------------------------------------------------------------*/\n\nextern \"C\" {\n\n/* Per-mip descriptor inside the decoded image's flat buffer. */\ntypedef struct AkKTX2MipLevel {\n  uint32_t width;\n  uint32_t height;\n  uint32_t byteOffset;   /* offset into AkKTX2DecodedImage.data */\n  uint32_t byteLength;   /* width × height × channels */\n} AkKTX2MipLevel;\n\ntypedef struct AkKTX2DecodedImage {\n  uint8_t        *data;       /* RGBA8 pixel storage, all mips concatenated */\n  size_t          dataLength; /* total bytes across all mips */\n  uint32_t        width;      /* base mip width  */\n  uint32_t        height;     /* base mip height */\n  uint32_t        channels;   /* 4 for RGBA8 */\n  uint32_t        mipCount;   /* number of mip levels stored */\n  AkKTX2MipLevel *mips;       /* array of length `mipCount`; allocated with malloc;\n                                 caller frees with `free(mips)` after consuming */\n  /* Reserved: KTX2 carries cubemap face count, layer count, srgb flag.\n     For the assetlook bridge we only care about 2D mip pyramids. */\n  uint32_t        reserved[2];\n} AkKTX2DecodedImage;\n\ntypedef struct AkKTX2Decoder {\n  void *userdata;\n  /* Decode a KTX2 byte buffer to RGBA8. Returns 0 on success. The\n     caller frees `out->data` via `free()` once consumed. */\n  int  (*decode)(const uint8_t      *data,\n                 size_t              size,\n                 AkKTX2DecodedImage *out);\n  void (*close)(void *ud);\n} AkKTX2Decoder;\n\ntypedef int (*AkKTX2DecoderCreateFn)(AkKTX2Decoder *out);\n\n}  /* extern \"C\" */\n\n/*--------------------------------------------------------------------*/\n/* libktx integration.                                                 */\n/*--------------------------------------------------------------------*/\n\nnamespace {\n\n/* Return the requested transcode format for our target. RGBA8 is the\n   universal-fallback that's CPU-decodable on any platform; on macOS\n   we hand the resulting bytes straight to CGBitmapContextCreate /\n   SCNMaterialProperty. Future: pick BC7 / ASTC for GPU-direct upload\n   when the renderer hints support — out of scope here. */\nconstexpr ktx_transcode_fmt_e kTargetFormat = KTX_TTF_RGBA32;\n\nint\nktx2_decode_to_rgba8(const uint8_t      *data,\n                     size_t              size,\n                     AkKTX2DecodedImage *out) {\n  if (!data || size == 0 || !out)\n    return -1;\n  std::memset(out, 0, sizeof(*out));\n\n  ktxTexture2 *tex = nullptr;\n  KTX_error_code rc =\n      ktxTexture2_CreateFromMemory(data,\n                                   size,\n                                   KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT,\n                                   &tex);\n  if (rc != KTX_SUCCESS || !tex)\n    return -2;\n\n  /* Transcode if the texture is BasisU-compressed (UASTC or ETC1S). */\n  if (ktxTexture2_NeedsTranscoding(tex)) {\n    rc = ktxTexture2_TranscodeBasis(tex, kTargetFormat, 0);\n    if (rc != KTX_SUCCESS) {\n      ktxTexture_Destroy(ktxTexture(tex));\n      return -3;\n    }\n  }\n\n  const uint32_t baseW    = tex->baseWidth;\n  const uint32_t baseH    = tex->baseHeight;\n  const uint32_t mipCount = tex->numLevels > 0 ? tex->numLevels : 1;\n\n  /* Two-pass: (1) compute total bytes + per-mip dimensions, (2) allocate\n     a single contiguous buffer + copy each mip in. Concatenated layout\n     keeps the caller's free() simple (one buffer, one free). */\n  AkKTX2MipLevel *mips = (AkKTX2MipLevel *)std::malloc(\n      sizeof(AkKTX2MipLevel) * mipCount);\n  if (!mips) {\n    ktxTexture_Destroy(ktxTexture(tex));\n    return -5;\n  }\n\n  size_t totalBytes = 0;\n  for (uint32_t mip = 0; mip < mipCount; ++mip) {\n    const uint32_t w = std::max<uint32_t>(1u, baseW  >> mip);\n    const uint32_t h = std::max<uint32_t>(1u, baseH >> mip);\n    const size_t   sz = (size_t)w * h * 4;\n\n    mips[mip].width      = w;\n    mips[mip].height     = h;\n    mips[mip].byteOffset = (uint32_t)totalBytes;\n    mips[mip].byteLength = (uint32_t)sz;\n    totalBytes          += sz;\n  }\n\n  uint8_t *pixels = (uint8_t *)std::malloc(totalBytes);\n  if (!pixels) {\n    std::free(mips);\n    ktxTexture_Destroy(ktxTexture(tex));\n    return -6;\n  }\n\n  for (uint32_t mip = 0; mip < mipCount; ++mip) {\n    ktx_size_t mipOffset = 0;\n    rc = ktxTexture_GetImageOffset(ktxTexture(tex), mip, 0, 0, &mipOffset);\n    if (rc != KTX_SUCCESS) {\n      std::free(pixels);\n      std::free(mips);\n      ktxTexture_Destroy(ktxTexture(tex));\n      return -4;\n    }\n    std::memcpy(pixels + mips[mip].byteOffset,\n                ktxTexture_GetData(ktxTexture(tex)) + mipOffset,\n                mips[mip].byteLength);\n  }\n\n  out->data       = pixels;\n  out->dataLength = totalBytes;\n  out->width      = baseW;\n  out->height     = baseH;\n  out->channels   = 4;\n  out->mipCount   = mipCount;\n  out->mips       = mips;\n\n  ktxTexture_Destroy(ktxTexture(tex));\n  return 0;\n}\n\n}  /* anonymous namespace */\n\nextern \"C\" AK_KTX2_EXPORT\nint\nassetkit_ktx2_decode(const uint8_t      *data,\n                     size_t              size,\n                     AkKTX2DecodedImage *out) {\n  return ktx2_decode_to_rgba8(data, size, out);\n}\n\nextern \"C\" AK_KTX2_EXPORT\nint\nassetkit_ktx2_create(AkKTX2Decoder *out) {\n  if (!out)\n    return -1;\n  out->userdata = nullptr;\n  out->decode   = ktx2_decode_to_rgba8;\n  out->close    = nullptr;\n  return 0;\n}\n"
  },
  {
    "path": "src/decoders/gltf/meshopt/assetkit_meshoptimizer.cc",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stddef.h>\n\n#include \"meshoptimizer.h\"\n\n#if defined(_WIN32)\n#  define AK_MESHOPT_EXPORT __declspec(dllexport)\n#else\n#  define AK_MESHOPT_EXPORT __attribute__((visibility(\"default\")))\n#endif\n\ntypedef enum AkMeshoptMode {\n  AK_MESHOPT_MODE_UNKNOWN    = 0,\n  AK_MESHOPT_MODE_ATTRIBUTES = 1,\n  AK_MESHOPT_MODE_TRIANGLES  = 2,\n  AK_MESHOPT_MODE_INDICES    = 3\n} AkMeshoptMode;\n\ntypedef enum AkMeshoptFilter {\n  AK_MESHOPT_FILTER_NONE        = 0,\n  AK_MESHOPT_FILTER_OCTAHEDRAL  = 1,\n  AK_MESHOPT_FILTER_QUATERNION  = 2,\n  AK_MESHOPT_FILTER_EXPONENTIAL = 3\n} AkMeshoptFilter;\n\nextern \"C\"\nAK_MESHOPT_EXPORT\nint\nak_meshopt_decode_gltf_buffer(void                *destination,\n                              size_t               destination_size,\n                              const unsigned char *buffer,\n                              size_t               buffer_size,\n                              size_t               count,\n                              size_t               stride,\n                              int                  mode,\n                              int                  filter) {\n  int res;\n\n  if (!destination\n      || !buffer\n      || stride == 0\n      || count > ((size_t)-1) / stride\n      || destination_size < count * stride)\n    return -1;\n\n  res = -1;\n  switch ((AkMeshoptMode)mode) {\n    case AK_MESHOPT_MODE_ATTRIBUTES:\n      res = meshopt_decodeVertexBuffer(destination,\n                                       count,\n                                       stride,\n                                       buffer,\n                                       buffer_size);\n      break;\n    case AK_MESHOPT_MODE_TRIANGLES:\n      res = meshopt_decodeIndexBuffer(destination,\n                                      count,\n                                      stride,\n                                      buffer,\n                                      buffer_size);\n      break;\n    case AK_MESHOPT_MODE_INDICES:\n      res = meshopt_decodeIndexSequence(destination,\n                                        count,\n                                        stride,\n                                        buffer,\n                                        buffer_size);\n      break;\n    default:\n      return -1;\n  }\n\n  if (res != 0)\n    return res;\n\n  switch ((AkMeshoptFilter)filter) {\n    case AK_MESHOPT_FILTER_OCTAHEDRAL:\n      meshopt_decodeFilterOct(destination, count, stride);\n      break;\n    case AK_MESHOPT_FILTER_QUATERNION:\n      meshopt_decodeFilterQuat(destination, count, stride);\n      break;\n    case AK_MESHOPT_FILTER_EXPONENTIAL:\n      meshopt_decodeFilterExp(destination, count, stride);\n      break;\n    default:\n      break;\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "src/decoders/gltf/spz/assetkit_spz.cc",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * AssetKit Gaussian Splat decoder shim — SPZ format (Niantic Spatial).\n *\n * Compile: links against `libspz.a` (https://github.com/nianticlabs/spz).\n * Build flag: AK_BUILD_GLTF_SPZ_DECODER. CMake FetchContent pulls SPZ if not\n * pre-installed (mirroring Draco / meshopt patterns).\n *\n * Usage at runtime: AssetKit dlopens this dylib (`AK_OPT_GLTF_GSPLAT_DECODER_PATH`\n * or autoload). When a glTF primitive carries the future\n * `EXT_gsplat_compression_spz` (or any compression sub-extension that names\n * \"spz\" as the format), the dispatcher calls `decodePrimitive` here. We\n * read the SPZ payload from the referenced bufferView, decode via\n * `spz::loadSpzFromMemory`, then allocate fresh AssetKit accessors and\n * populate the primitive's input chain with standard\n * KHR_gaussian_splatting attributes (POSITION / ROTATION / SCALE / OPACITY /\n * COLOR_0 + SH coefficients in COLOR_1..N if the file ships them).\n *\n * Out-of-band format wrapper note: glTF doesn't yet ratify a per-format\n * compression sub-extension for SPZ. Until it does, apps targeting this\n * decoder should agree on a vendor-prefixed extension name (e.g.\n * `EXT_gsplat_compression_spz`) and pass the bufferView index in\n * `compression.bufferView`. The shim below treats whichever sub-extension\n * the dispatcher hands it as opaque — it just needs the bufferView index.\n */\n\n#include <spz/load-spz.h>\n\n#include <ak/assetkit.h>\n\n#include <cmath>\n#include <memory>\n#include <cstdint>\n#include <cstdlib>\n#include <cstring>\n#include <vector>\n\n#if defined(_WIN32)\n#  define AK_GSPLAT_EXPORT __declspec(dllexport)\n#else\n#  define AK_GSPLAT_EXPORT __attribute__((visibility(\"default\")))\n#endif\n\ntypedef struct json_t          json_t;\n\n/*--------------------------------------------------------------------*/\n/* AssetKit accessor + buffer helpers, scoped to this shim. We mirror */\n/* the Draco shim's storage strategy: each decoded attribute lives in */\n/* its own AkBuffer + AkAccessor pair, attached to the primitive's    */\n/* input chain. Lifetime is the primitive's heap parent.              */\n/*--------------------------------------------------------------------*/\n\nnamespace {\n\nconstexpr size_t kFloatSize = sizeof(float);\n\nstruct GLTFStateLite {\n  AkHeap *heap;\n  void   *doc;\n  /* unused remainder; we only touch `heap` and the document's\n     buffer/accessor flists which we don't access directly here */\n};\n\n/* Allocate an AkBuffer of given byte length under the primitive heap;\n   copy the source vector into it. */\nAkBuffer*\nmake_buffer_from_vector(AkHeap *heap, void *parent,\n                        const std::vector<float> &src) {\n  const size_t   bytes = src.size() * kFloatSize;\n  AkBuffer      *buf   = (AkBuffer *)ak_heap_calloc(heap, parent, sizeof(AkBuffer));\n  if (!buf) return nullptr;\n  buf->length = bytes;\n  buf->data   = ak_heap_alloc(heap, buf, bytes);\n  if (!buf->data) return nullptr;\n  std::memcpy(buf->data, src.data(), bytes);\n  return buf;\n}\n\n/* Single-component-type accessor over a buffer of `count` × `comps` floats. */\nAkAccessor*\nmake_float_accessor(AkHeap *heap, void *parent,\n                    AkBuffer *buf, uint32_t count, uint32_t comps) {\n  AkComponentSize componentSize = AK_COMPONENT_SIZE_SCALAR;\n\n  if (comps == 2)\n    componentSize = AK_COMPONENT_SIZE_VEC2;\n  else if (comps == 3)\n    componentSize = AK_COMPONENT_SIZE_VEC3;\n  else if (comps == 4)\n    componentSize = AK_COMPONENT_SIZE_VEC4;\n\n  AkAccessor *acc = (AkAccessor *)ak_heap_calloc(heap, parent, sizeof(AkAccessor));\n  if (!acc) return nullptr;\n  acc->buffer            = buf;\n  acc->byteOffset        = 0;\n  acc->count             = count;\n  acc->bytesPerComponent = (uint32_t)kFloatSize;\n  acc->componentSize     = componentSize;\n  acc->componentType     = AKT_FLOAT;\n  acc->componentCount    = comps;\n  acc->fillByteSize      = (size_t)comps * kFloatSize;\n  acc->byteStride        = acc->fillByteSize;\n  acc->byteLength        = (size_t)count * acc->fillByteSize;\n  acc->originalComponentType = AKT_FLOAT;\n  return acc;\n}\n\n/* Append an input to the primitive's input chain. Caller holds the head;\n   we do simple prepend to match how the rest of the loader builds it. */\nAkInput*\nprepend_input(AkHeap *heap, AkMeshPrimitive *prim,\n              AkAccessor *acc, AkInputSemantic sem,\n              const char *semanticRaw, uint32_t set) {\n  AkInput *inp = (AkInput *)ak_heap_calloc(heap, prim, sizeof(AkInput));\n  if (!inp) return nullptr;\n  inp->accessor    = acc;\n  inp->semantic    = sem;\n  inp->semanticRaw = ak_heap_strdup(heap, inp, semanticRaw);\n  inp->set         = set;\n\n  inp->next   = prim->input;\n  prim->input = inp;\n  prim->inputCount++;\n  return inp;\n}\n\n}  /* anonymous namespace */\n\n/*--------------------------------------------------------------------*/\n/* Decoder entrypoint — invoked by AssetKit when a primitive's        */\n/* gaussian-splat compression sub-extension references SPZ data.      */\n/*                                                                     */\n/* Args:                                                               */\n/*   gst           — AkGLTFState* (opaque to us; we only need heap)   */\n/*   prim          — primitive to populate                             */\n/*   jprim         — primitive JSON (unused, kept for parity)          */\n/*   jcompression  — the sub-extension JSON (carries `bufferView`)    */\n/*--------------------------------------------------------------------*/\n\n/* Forward declarations so `assetkit_gsplat_create` can wire both function\n   pointers in the decoder struct regardless of definition order below. */\nextern \"C\" AK_GSPLAT_EXPORT\nint\nak_spz_decodeBytes(AkHeap            *heap,\n                   AkMeshPrimitive   *prim,\n                   const uint8_t     *data,\n                   size_t             size);\n\nextern \"C\" AK_GSPLAT_EXPORT\nint\nak_spz_decodePrimitive(struct AkGLTFState    *gst_opaque,\n                       AkMeshPrimitive       *prim,\n                       const json_t          * /*jprim*/,\n                       const json_t          *jcompression) {\n  if (!gst_opaque || !prim || !jcompression)\n    return -1;\n\n  /* Pull `heap` out of AkGLTFState. The state's first field is a heap\n     pointer (matches the layout the Draco shim relies on). */\n  AkHeap *heap = *reinterpret_cast<AkHeap **>(gst_opaque);\n\n  /* Locate the bufferView the compression sub-extension points at,\n     resolve to its raw bytes. The dispatcher should have surfaced the\n     bufferView pointer already; we assume the sub-extension provides\n     a \"bufferView\": <int> field. AssetKit's flist of bufferViews is\n     reachable but indexing requires the full state — defer that to the\n     dispatcher in ext.c, which calls us with `jcompression` already\n     containing pre-resolved buffer pointer + size in fields the spec\n     locks down once approved. For now we expect a `data` ptr + `size`\n     callback wired by the dispatcher. */\n  /* TODO: once a per-format compression sub-extension is ratified, the\n     dispatcher in src/io/gltf/imp/core/ext.c will resolve the\n     bufferView and pass `data`+`size` here directly. The skeleton\n     above leaves that wiring open. */\n  (void)heap;\n  (void)prim;\n  return -1;  /* not yet wired — bufferView resolution path pending spec */\n}\n\nextern \"C\" AK_GSPLAT_EXPORT\nint\nassetkit_gsplat_create(AkGaussianSplatDecoder *out) {\n  if (!out)\n    return -1;\n  out->userdata        = nullptr;\n  out->decodeBytes     = ak_spz_decodeBytes;       /* preferred path */\n  out->decodePrimitive = ak_spz_decodePrimitive;   /* legacy fallback */\n  out->close           = nullptr;\n  return 0;\n}\n\n/*--------------------------------------------------------------------*/\n/* SPZ → AssetKit conversion (helper used by the decoder once the     */\n/* dispatcher delivers raw bytes). Public so a host integration test  */\n/* can exercise the path with a known SPZ blob.                       */\n/*--------------------------------------------------------------------*/\n\nextern \"C\" AK_GSPLAT_EXPORT\nint\nak_spz_decodeBytes(AkHeap            *heap,\n                   AkMeshPrimitive   *prim,\n                   const uint8_t     *data,\n                   size_t             size) {\n  if (!heap || !prim || !data || size == 0)\n    return -1;\n\n  spz::GaussianCloud cloud;\n  try {\n    spz::UnpackOptions opts;\n    cloud = spz::loadSpz(data, size, opts);\n  } catch (...) {\n    return -2;\n  }\n  if (cloud.numPoints <= 0)\n    return -3;\n\n  const uint32_t n = static_cast<uint32_t>(cloud.numPoints);\n\n  /* libspz returns *encoded* fields per the SPZ format convention:\n       scales : log-scale       → caller must exp(x)\n       alphas : pre-sigmoid       → caller must sigmoid(x) = 1/(1+exp(-x))\n       colors : SH DC component   → caller must 0.5 + 0.282095·x  (clamp 0..1)\n     We decode in-shim so AssetKit consumers see standard renderer-ready\n     values: scale (linear units), opacity (0..1), color (0..1 RGB).\n     Higher-order SH coefficients pass through unchanged — renderers that\n     care evaluate them direction-dependent.\n     NaN/Inf guards: corrupted SPZ payloads or extreme outliers in legit\n     data can produce NaN after exp() or sigmoid() when raw values are\n     beyond representable range. We sanitize each field as it's converted\n     so a single bad splat doesn't blow out the whole renderer. */\n  auto sanitize = [](float v, float fallback) {\n    return std::isfinite(v) ? v : fallback;\n  };\n\n  /* POSITION (vec3) — sanitize NaN/Inf to origin (visually clusters\n     bad splats at the model origin where they're easy to spot). */\n  {\n    std::vector<float> safePos(cloud.positions.size());\n    for (size_t i = 0; i < cloud.positions.size(); ++i)\n      safePos[i] = sanitize(cloud.positions[i], 0.0f);\n    if (auto *buf = make_buffer_from_vector(heap, prim, safePos)) {\n      auto *acc = make_float_accessor(heap, prim, buf, n, 3);\n      prepend_input(heap, prim, acc, AK_INPUT_POSITION, \"POSITION\", 0);\n    }\n  }\n\n  /* ROTATION (vec4 quat xyzw) — sanitize NaN to identity quat (0,0,0,1).\n     A degenerate quat can collapse to a non-rotation, but identity\n     means the splat aligns with object axes which is renderable. */\n  {\n    std::vector<float> safeRot(cloud.rotations.size());\n    for (size_t i = 0; i < cloud.rotations.size(); i += 4) {\n      float x = sanitize(cloud.rotations[i + 0], 0.0f);\n      float y = sanitize(cloud.rotations[i + 1], 0.0f);\n      float z = sanitize(cloud.rotations[i + 2], 0.0f);\n      float w = sanitize(cloud.rotations[i + 3], 1.0f);\n      // If the quat collapsed (all zeros after sanitize), force identity.\n      if (x == 0.0f && y == 0.0f && z == 0.0f && w == 0.0f) {\n        w = 1.0f;\n      }\n      safeRot[i + 0] = x;\n      safeRot[i + 1] = y;\n      safeRot[i + 2] = z;\n      safeRot[i + 3] = w;\n    }\n    if (auto *buf = make_buffer_from_vector(heap, prim, safeRot)) {\n      auto *acc = make_float_accessor(heap, prim, buf, n, 4);\n      prepend_input(heap, prim, acc, AK_INPUT_OTHER, \"ROTATION\", 0);\n    }\n  }\n\n  /* SCALE (vec3) — decode log → linear via exp(x). Clamp the log range\n     before exp() so an extreme value doesn't produce inf scale. Real\n     splat scales are sub-meter; even log(100) = ~4.6 is generous. */\n  {\n    std::vector<float> linearScales(cloud.scales.size());\n    for (size_t i = 0; i < cloud.scales.size(); ++i) {\n      float logS = sanitize(cloud.scales[i], 0.0f);  // 0 in log → 1 linear\n      // Clamp log to ±15 (linear range ≈ 3·10⁻⁷ … 3·10⁶). Beyond this\n      // is virtually-certainly bad data.\n      if (logS < -15.0f) logS = -15.0f;\n      if (logS >  15.0f) logS =  15.0f;\n      linearScales[i] = std::exp(logS);\n    }\n    if (auto *buf = make_buffer_from_vector(heap, prim, linearScales)) {\n      auto *acc = make_float_accessor(heap, prim, buf, n, 3);\n      prepend_input(heap, prim, acc, AK_INPUT_OTHER, \"SCALE\", 0);\n    }\n  }\n\n  /* OPACITY (float) — decode logit → 0..1 via sigmoid. NaN logit defaults\n     to 0 (sigmoid(0) = 0.5 ≈ \"moderately visible\"). */\n  {\n    std::vector<float> opacities(cloud.alphas.size());\n    for (size_t i = 0; i < cloud.alphas.size(); ++i) {\n      const float x = sanitize(cloud.alphas[i], 0.0f);\n      // sigmoid(x) on extreme x is fine: sigmoid(±∞) → 0/1. We sanitized\n      // for NaN; ±Inf path produces clean 0 or 1.\n      opacities[i] = 1.0f / (1.0f + std::exp(-x));\n    }\n    if (auto *buf = make_buffer_from_vector(heap, prim, opacities)) {\n      auto *acc = make_float_accessor(heap, prim, buf, n, 1);\n      prepend_input(heap, prim, acc, AK_INPUT_OTHER, \"OPACITY\", 0);\n    }\n  }\n\n  /* COLOR_0 (vec3) — decode SH DC component → 0..1 RGB.\n     Formula `0.5 + 0.282095·x` is the SH band-0 inverse normalization\n     constant (1 / (2·sqrt(π)) ≈ 0.282095). Clamp to [0,1] to guard\n     against extrapolation on splats with extreme DC values; sanitize\n     NaN→0 first so the clamp produces 0.5 (mid-gray fallback). */\n  {\n    std::vector<float> rgb(cloud.colors.size());\n    constexpr float kSH0 = 0.282094791773878f;  /* 1 / (2*sqrt(pi)) */\n    for (size_t i = 0; i < cloud.colors.size(); ++i) {\n      float dc = sanitize(cloud.colors[i], 0.0f);\n      float v  = 0.5f + kSH0 * dc;\n      if (v < 0.0f) v = 0.0f;\n      if (v > 1.0f) v = 1.0f;\n      rgb[i] = v;\n    }\n    if (auto *buf = make_buffer_from_vector(heap, prim, rgb)) {\n      auto *acc = make_float_accessor(heap, prim, buf, n, 3);\n      prepend_input(heap, prim, acc, AK_INPUT_COLOR, \"COLOR\", 0);\n    }\n  }\n\n  /* Higher-order SH coefficients (COLOR_1..COLOR_N) — vec3 each, packed\n     as `numPoints × shDim × 3`. shDim depends on the spherical-harmonic\n     degree (degree 1 → 3 dirs, degree 2 → 8 dirs, degree 3 → 15 dirs).\n     We split into per-dir accessors so renderers that read COLOR_n can\n     fetch any subset. NaN sanitize → 0 so a bad coefficient gives no\n     view-dependent contribution rather than blowing out colors. */\n  if (cloud.shDegree > 0 && !cloud.sh.empty()) {\n    const int shDim = cloud.shDegree * (cloud.shDegree + 2);\n    for (int d = 0; d < shDim; ++d) {\n      std::vector<float> band(static_cast<size_t>(n) * 3);\n      for (uint32_t i = 0; i < n; ++i) {\n        const size_t srcBase = (size_t)i * (size_t)shDim * 3 + (size_t)d * 3;\n        const size_t dstBase = (size_t)i * 3;\n        band[dstBase + 0] = sanitize(cloud.sh[srcBase + 0], 0.0f);\n        band[dstBase + 1] = sanitize(cloud.sh[srcBase + 1], 0.0f);\n        band[dstBase + 2] = sanitize(cloud.sh[srcBase + 2], 0.0f);\n      }\n      auto *buf = make_buffer_from_vector(heap, prim, band);\n      auto *acc = make_float_accessor(heap, prim, buf, n, 3);\n      prepend_input(heap, prim, acc, AK_INPUT_COLOR, \"COLOR\",\n                    static_cast<uint32_t>(d + 1));\n    }\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "src/default/CMakeLists.txt",
    "content": "target_sources(${PROJECT_NAME} \n  PRIVATE\n  semantic.h\n  type.c\n  type.h\n  opt.c\n  opt.h\n  semantic.c\n  material.c\n  material.h\n  id.c\n  light.c\n  light.h\n  coord.c\n  cmp.c\n  cam.h\n  cam.c\n)\n"
  },
  {
    "path": "src/default/cam.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cam.h\"\n#include <cglm/cglm.h>\n\nAkPerspective ak_def_cam_tcommon = {\n  .base = {\n\t.type = AK_PROJECTION_PERSPECTIVE,\n\t.tag = 0\n  },\n  .yfov = GLM_PI_4f,\n  .xfov = GLM_PI_2f,\n  .aspectRatio = 0.5f,\n  .znear = 0.01f,\n  .zfar = 100.0f\n};\n\nAkOptics ak_def_cam_optics = {\n  .tcommon   = &ak_def_cam_tcommon.base,\n  .technique = NULL\n};\n\nconst AkCamera ak_def_cam = {\n  .name   = \"default\",\n  .optics = &ak_def_cam_optics\n};\n\nconst AkCamera*\nak_def_camera(void) {\n  return &ak_def_cam;\n}\n"
  },
  {
    "path": "src/default/cam.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_def_cam_h\n#define ak_def_cam_h\n\n#include \"../common.h\"\n\nconst AkCamera*\nak_def_camera(void);\n\n#endif /* ak_def_cam_h */\n"
  },
  {
    "path": "src/default/cmp.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include <string.h>\n\nAK_EXPORT\nint\nak_cmp_str(void * key1, void *key2) {\n  return strcmp((char *)key1, (char *)key2);\n}\n\nAK_EXPORT\nint\nak_cmp_ptr(void *key1, void *key2) {\n  if (key1 > key2)\n    return 1;\n  else if (key1 < key2)\n    return -1;\n  return 0;\n}\n\nAK_EXPORT\nint\nak_cmp_i32(void *key1, void *key2) {\n  int32_t a, b;\n\n  a = *(int32_t *)key1;\n  b = *(int32_t *)key2;\n\n  return a - b;\n}\n\nAK_EXPORT\nint\nak_cmp_vec3(void *key1, void *key2) {\n  float *v1, *v2;\n\n  v1  = key1;\n  v2  = key2;\n\n  if (v1[0] > v2[0])\n    return 1;\n  else if (v1[0] < v2[0])\n    return -1;\n\n  if (v1[1] > v2[1])\n    return 1;\n  else if (v1[1] < v2[1])\n    return -1;\n\n  if (v1[2] > v2[2])\n    return 1;\n  else if (v1[2] < v2[2])\n    return -1;\n\n  return 0;\n}\n\nAK_EXPORT\nint\nak_cmp_ivec3(void *key1, void *key2) {\n  int32_t *v1, *v2;\n\n  v1  = key1;\n  v2  = key2;\n\n  if (v1[0] > v2[0])\n    return 1;\n  else if (v1[0] < v2[0])\n    return -1;\n\n  if (v1[1] > v2[1])\n    return 1;\n  else if (v1[1] < v2[1])\n    return -1;\n\n  if (v1[2] > v2[2])\n    return 1;\n  else if (v1[2] < v2[2])\n    return -1;\n\n  return 0;\n}\n\nAK_EXPORT\nint\nak_cmp_vec4(void *key1, void *key2) {\n  float *v1, *v2;\n\n  v1  = key1;\n  v2  = key2;\n\n  if (v1[0] > v2[0])\n    return 1;\n  else if (v1[0] < v2[0])\n    return -1;\n\n  if (v1[1] > v2[1])\n    return 1;\n  else if (v1[1] < v2[1])\n    return -1;\n\n  if (v1[2] > v2[2])\n    return 1;\n  else if (v1[2] < v2[2])\n    return -1;\n\n  if (v1[3] > v2[3])\n    return 1;\n  else if (v1[3] < v2[3])\n    return -1;\n  \n  return 0;\n}\n"
  },
  {
    "path": "src/default/coord.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../coord/common.h\"\n\n/* Right Hand (Default) */\nAkCoordSys AK__Y_RH_VAL =         {AK__Y_RH, AK_AXIS_ROT_DIR_RH, AK__Y_RH};\nAkCoordSys *AK_YUP      = &AK__Y_RH_VAL;\nAkCoordSys *AK_ZUP      = AK_COORD(AK__Z_RH, AK_AXIS_ROT_DIR_RH, AK__Y_RH);\nAkCoordSys *AK_XUP      = AK_COORD(AK__X_RH, AK_AXIS_ROT_DIR_RH, AK__Y_RH);\n\n/* Left Hand */\nAkCoordSys *AK_ZUP_LH   = AK_COORD(AK__Z_LH, AK_AXIS_ROT_DIR_LH, AK__Y_LH);\nAkCoordSys *AK_YUP_LH   = AK_COORD(AK__Y_LH, AK_AXIS_ROT_DIR_LH, AK__Y_LH);\nAkCoordSys *AK_XUP_LH   = AK_COORD(AK__X_LH, AK_AXIS_ROT_DIR_LH, AK__Y_LH);\n\nAkCoordSys *AK_DEFAULT_COORD = &AK__Y_RH_VAL;\n"
  },
  {
    "path": "src/default/id.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n\nconst char * AK_DEF_ID_PRFX = \"id-\";\n"
  },
  {
    "path": "src/default/light.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"light.h\"\n\nAkDirectionalLight akdef_light_tcommon = {\n  .direction = {0.0f, 0.0f, -1.0f},\n  .type      = AK_LIGHT_TYPE_DIRECTIONAL,\n  .color     = { {1.0, 1.0, 1.0, 1.0} },\n  .ctype     = 0\n};\n\nAkLight akdef_light = {\n  .name      = \"default\",\n  .tcommon   = &akdef_light_tcommon,\n  .technique = NULL,\n  .extra     = NULL,\n  .next      = NULL\n};\n\nconst AkLight*\nak_def_light(void) {\n  return &akdef_light;\n}\n"
  },
  {
    "path": "src/default/light.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_def_light_h\n#define ak_def_light_h\n\n#include \"../common.h\"\n\nconst AkLight*\nak_def_light(void);\n\n#endif /* ak_def_light_h */\n"
  },
  {
    "path": "src/default/material.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"material.h\"\n#include \"../common.h\"\n\n//float ak__def_transpval = 1.0f;\n//AkFloatOrParam ak__def_transparency = {\n//  .val   = &ak__def_transpval,\n//  .param = NULL\n//};\n//\n//AkFloatOrParam*\n//ak_def_transparency(void) {\n//  return &ak__def_transparency;\n//}\n"
  },
  {
    "path": "src/default/material.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_def_material_h\n#define ak_def_material_h\n\n#include \"../common.h\"\n\n//AkFloatOrParam*\n//ak_def_transparency(void);\n\n#endif /* ak_def_material_h */\n"
  },
  {
    "path": "src/default/opt.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"opt.h\"\n#include <string.h>\n#include <assert.h>\n\nextern AkCoordSys   AK__Y_RH_VAL;\nextern const char * AK_DEF_ID_PRFX;\n\nconst char *ak__def_techniques[] = {\"common\", NULL};\n\nuintptr_t AK_OPTIONS[] =\n{\n  false,                           /* 0:  _INDICES_NONE                */\n  false,                           /* 1:  _INDICES_SINGLE_INTERLEAVED  */\n  false,                           /* 2:  _INDICES_SINGLE_SEPARATE     */\n  false,                           /* 3:  _INDICES_SINGLE              */\n  true,                            /* 4:  _NO_INDICES_INTERLEAVED      */\n  false,                           /* 5:  _NO_INDICES_SEPARATE         */\n  (uintptr_t)&AK__Y_RH_VAL,        /* 6:  _COORD                       */\n  (uintptr_t)&AK_DEF_ID_PRFX,      /* 7:  _DEFAULT_ID_PREFIX           */\n  false,                           /* 8:  _COMPUTE_BBOX                */\n  true,                            /* 9: _TRIANGULATE                 */\n  true,                            /* 10: _GEN_NORMALS_IF_NEEDED       */\n  AK_PROFILE_TYPE_COMMON,          /* 11: _DEFAULT_PROFILE             */\n  true,                            /* 12: _EFFECT_PROFILE              */\n  (uintptr_t)ak__def_techniques,   /* 13: _TECHNIQUE                   */\n  (uintptr_t)ak__def_techniques,   /* 14: _TECHNIQUE_FX                */\n  false,                           /* 15: _ZERO_INDEXED_INPUT          */\n  true,                            /* 16: _IMAGE_LOAD_FLIP_VERTICALLY  */\n  true,                            /* 17: _ADD_DEFAULT_CAMERA          */\n  false,                           /* 18: _ADD_DEFAULT_LIGHT           */\n  AK_COORD_CVT_DEFAULT,            /* 19: _COORD_CONVERT_TYPE          */\n  true,                            /* 20: _BUGFIXES                    */\n  false,                           /* 21: _COMPUTE_EXACT_CENTER        */\n\n#ifndef _MSC_VER\n  true,                            /* 22: _USE_MMAP                    */\n#else\n  false,\n#endif\n  true,                            /* 23: _GEN_TANGENTS_IF_NEEDED      */\n  false,                           /* 24: _CVT_TRIANGLESTRIP           */\n  false,                           /* 25: _CVT_TRIANGLEFAN             */\n  false,                           /* 26: _CVT_LINELOOP                */\n  false,                           /* 27: _CVT_LINESTRIP               */\n  false,                           /* 28: _PRESERVE_QUANTIZED_ATTRS    */\n  true,                            /* 29: _GLTF_EXT_DECODER_AUTOLOAD   */\n  (uintptr_t)NULL,                 /* 30: _GLTF_MESHOPT_DECODER_PATH   */\n  (uintptr_t)NULL,                 /* 31: _GLTF_DRACO_DECODER_PATH     */\n  (uintptr_t)NULL,                 /* 32: _GLTF_GSPLAT_DECODER_PATH    */\n  (uintptr_t)NULL                  /* 33: _GLTF_KTX2_DECODER_PATH      */\n};\n\nAK_EXPORT\nvoid\nak_opt_set(AkOption option, uintptr_t value) {\n  assert((uint32_t)option < AK_ARRAY_LEN(AK_OPTIONS));\n\n  AK_OPTIONS[option] = value;\n}\n\nAK_EXPORT\nuintptr_t\nak_opt_get(AkOption option) {\n  assert((uint32_t)option < AK_ARRAY_LEN(AK_OPTIONS));\n\n  return AK_OPTIONS[option];\n}\n\nAK_EXPORT\nvoid\nak_opt_set_str(AkOption option, const char *value) {\n  assert((uint32_t)option < AK_ARRAY_LEN(AK_OPTIONS));\n\n  AK_OPTIONS[option] = (uintptr_t)ak_strdup(NULL, value);\n}\n"
  },
  {
    "path": "src/default/opt.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_def_opt_h\n#define ak_def_opt_h\n\n#include \"../common.h\"\n\ntypedef struct AkOptionItem {\n  AkEnum    name;\n  uintptr_t value;\n} AkOptionItem;\n\n#endif /* ak_def_opt_h */\n"
  },
  {
    "path": "src/default/semantic.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"semantic.h\"\n\n/* Prefix desc: P represents param, T represents type */\n\n#define AK_PUSER  0 /* undefined by lib */\n#define AK_PXYZ   1\n\n#define AK_TFLOAT 0\n\nAkTypeDesc ak_def_dtypes[] = {\n  {\"float\", AKT_FLOAT, sizeof(float), 0}\n};\n\nAkInputSemanticPair ak_def_params[] = {\n  {0, NULL, NULL},\n  {3, &ak_def_dtypes[AK_TFLOAT], (char*[]){\"X\", \"Y\", \"Z\"}}\n};\n\nconst AkInputSemanticPair* ak_def_sm_pairs[] = {\n  /* _OTHER           */ &ak_def_params[AK_PUSER],\n  /* _BINORMAL        */ &ak_def_params[AK_PUSER],\n  /* _COLOR           */ &ak_def_params[AK_PUSER],\n  /* _CONTINUITY      */ &ak_def_params[AK_PUSER],\n  /* _IMAGE           */ &ak_def_params[AK_PUSER],\n  /* _INPUT           */ &ak_def_params[AK_PUSER],\n  /* _IN_TANGENT      */ &ak_def_params[AK_PUSER],\n  /* _INTERPOLATION   */ &ak_def_params[AK_PUSER],\n  /* _INV_BIND_MATRIX */ &ak_def_params[AK_PUSER],\n  /* _JOINT           */ &ak_def_params[AK_PUSER],\n  /* _LINEAR_STEPS    */ &ak_def_params[AK_PUSER],\n  /* _MORPH_TARGET    */ &ak_def_params[AK_PUSER],\n  /* _MORPH_WEIGHT    */ &ak_def_params[AK_PUSER],\n\n  /* _NORMAL          */ &ak_def_params[AK_PXYZ],\n\n  /* _OUTPUT          */ &ak_def_params[AK_PUSER],\n  /* _OUT_TANGENT     */ &ak_def_params[AK_PUSER],\n\n  /* _POSITION        */ &ak_def_params[AK_PXYZ],\n\n  /* _TANGENT         */ &ak_def_params[AK_PUSER],\n  /* _TEXBINORMAL     */ &ak_def_params[AK_PUSER],\n  /* _TEXCOORD        */ &ak_def_params[AK_PUSER],\n  /* _TEXTANGENT      */ &ak_def_params[AK_PUSER],\n  /* _UV              */ &ak_def_params[AK_PUSER],\n  /* _VERTEX          */ &ak_def_params[AK_PUSER],\n  /* _WEIGHT          */ &ak_def_params[AK_PUSER]\n};\n\nconst AkInputSemanticPair**\nak_def_semantic(void) {\n  return ak_def_sm_pairs;\n}\n\nuint32_t\nak_def_semanticc(void) {\n  return AK_ARRAY_LEN(ak_def_sm_pairs);\n}\n"
  },
  {
    "path": "src/default/semantic.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_def_semantic_h\n#define ak_def_semantic_h\n\n#include \"../common.h\"\n\ntypedef struct AkInputSemanticPair {\n  uint32_t        count;\n  AkTypeDesc     *type;\n  char           **params;\n} AkInputSemanticPair;\n\nconst AkInputSemanticPair**\nak_def_semantic(void);\n\nuint32_t\nak_def_semanticc(void);\n\n#endif /* ak_def_semantic_h */\n"
  },
  {
    "path": "src/default/type.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"type.h\"\n#include \"../common.h\"\n\nAkTypeDesc ak_def_type_descs[] = {\n  {\"float\",     AKT_FLOAT,     sizeof(AkFloat),            0},\n  {\"float2\",    AKT_FLOAT2,    sizeof(AkFloat2),           0},\n  {\"float3\",    AKT_FLOAT3,    sizeof(AkFloat3),           0},\n  {\"float4\",    AKT_FLOAT4,    sizeof(AkFloat4),           0},\n  {\"float2x2\",  AKT_FLOAT2x2,  sizeof(AkFloat2),           0},\n  {\"float3x3\",  AKT_FLOAT3x3,  sizeof(AkFloat2[2]),        0},\n  {\"float4x4\",  AKT_FLOAT4x4,  sizeof(AkFloat4[4]),        0},\n\n  {\"int\",       AKT_INT,       sizeof(AkInt),              0},\n  {\"int2\",      AKT_INT2,      sizeof(AkInt[2]),           0},\n  {\"int3\",      AKT_INT3,      sizeof(AkInt[3]),           0},\n  {\"int4\",      AKT_INT4,      sizeof(AkInt[4]),           0},\n\n  {\"bool\",      AKT_BOOL,      sizeof(AkBool),             0},\n  {\"bool2\",     AKT_BOOL2,     sizeof(AkBool[2]),          0},\n  {\"bool3\",     AKT_BOOL3,     sizeof(AkBool[3]),          0},\n  {\"bool4\",     AKT_BOOL4,     sizeof(AkBool[4]),          0},\n\n  {\"string\",    AKT_STRING,    sizeof(AkString),           0},\n\n  /* for glTF  */\n  {\"uint\",      AKT_UINT,      sizeof(AkUInt),             0},\n  {\"byte\",      AKT_BYTE,      sizeof(char),               0},\n  {\"ubyte\",     AKT_UBYTE,     sizeof(unsigned char),      0},\n  {\"short\",     AKT_SHORT,     sizeof(short),              0},\n  {\"ushort\",    AKT_USHORT,    sizeof(unsigned short),     0},\n\n  {\"sampler2D\", AKT_SAMPLER2D, sizeof(AkSampler),          0},\n\n  /* for PLY  */\n  {\"char\",      AKT_BYTE,      sizeof(char),               0},\n  {\"uchar\",     AKT_UBYTE,     sizeof(char),               0},\n  {\"double\",    AKT_DOUBLE,    sizeof(AkDouble),           0},\n  \n  {\"int8\",      AKT_INT,       sizeof(char),               0},\n  {\"uint8\",     AKT_UINT,      sizeof(char),               0},\n  {\"int16\",     AKT_SHORT,     sizeof(AkInt16),            0},\n  {\"uint16\",    AKT_USHORT,    sizeof(AkUInt16),           0},\n  {\"int32\",     AKT_UINT,      sizeof(AkInt),              0},\n  {\"uint32\",    AKT_UINT,      sizeof(AkUInt),             0},\n  {\"float32\",   AKT_FLOAT,     sizeof(AkFloat),            0},\n  {\"float64\",   AKT_DOUBLE,    sizeof(AkDouble),           0},\n\n  {NULL,        0,             0,                          0}\n};\n\nconst AkTypeDesc*\nak_def_typedesc(void) {\n  return ak_def_type_descs;\n}\n"
  },
  {
    "path": "src/default/type.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_def_type_h\n#define ak_def_type_h\n\n#include \"../common.h\"\n\nconst AkTypeDesc*\nak_def_typedesc(void);\n\n#endif /* ak_def_type_h */\n"
  },
  {
    "path": "src/endian.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef endian_h\n#define endian_h\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"../include/ak/common.h\"\n#include <ctype.h>\n\n#if _MSC_VER\n# if (defined(_M_IX86) || defined(_M_X64))\n#  define ARCH_X86\n# endif\n# if (defined(_M_AMD64) || defined(_M_X64))\n#  define ARCH_X86_64\n# endif\n#else\n# ifdef __i386__\n#  define ARCH_X86\n# endif\n# ifdef __x86_64__\n#  define ARCH_X86_64\n# endif\n#endif\n\nAK_INLINE\nuint16_t\nbswapu16(uint16_t val) {\n#if !defined(_MSC_VER)\n  return (uint16_t)((val << 8) | (val >> 8));\n#else\n  return _byteswap_ushort(val);\n#endif\n}\n\nAK_INLINE\nuint32_t\nbswapu32(uint32_t val) {\n  #if defined(__llvm__)\n    return __builtin_bswap32(val);\n  #elif defined(ARCH_X86)\n  # if !defined(_MSC_VER)\n    __asm__ (\"bswap   %0\" : \"+r\" (val));\n    return val;\n  # else\n    return _byteswap_ulong(val);\n  # endif\n  #else\n    val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);\n    return (val << 16) | (val >> 16);\n  #endif\n}\n\nAK_INLINE\nuint64_t\nbswapu64(uint64_t val) {\n  #if defined(__llvm__)\n    return __builtin_bswap64(val);\n  #elif defined(ARCH_X86_64)\n  # if !defined(_MSC_VER)\n    __asm__ (\"bswap   %0\" : \"+r\" (val));\n    return val;\n  # else\n    return _byteswap_uint64(val);\n  # endif\n  #elif defined(ARCH_X86)\n  # if !defined(_MSC_VER)\n    __asm__ (\"bswap   %%eax\\n\\t\"\n             \"bswap   %%edx\\n\\t\"\n             \"xchgl   %%eax, %%edx\"\n             : \"+A\" (val));\n    return val;\n  # else\n    return _byteswap_uint64(val);\n  # endif\n  #else\n    /*\n    return ((((val) & 0xff00000000000000ull) >> 56)\n          | (((val) & 0x00ff000000000000ull) >> 40)\n          | (((val) & 0x0000ff0000000000ull) >> 24)\n          | (((val) & 0x000000ff00000000ull) >> 8)\n          | (((val) & 0x00000000ff000000ull) << 8)\n          | (((val) & 0x0000000000ff0000ull) << 24)\n          | (((val) & 0x000000000000ff00ull) << 40)\n          | (((val) & 0x00000000000000ffull) << 56));\n    */\n    val = ((val << 8) & 0xFF00FF00FF00FF00ULL)\n        | ((val >> 8) & 0x00FF00FF00FF00FFULL);\n    val = ((val << 16) & 0xFFFF0000FFFF0000ULL)\n        | ((val >> 16) & 0x0000FFFF0000FFFFULL);\n    return (val << 32) | (val >> 32);\n  #endif\n}\n\n/* helper for little endian */\n\n/* 16-bit */\n#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n# define le_16(X, DATA)                                                       \\\n  do {                                                                        \\\n    memcpy(&X, DATA, 2);                                                      \\\n    DATA = (char *)DATA + 2;                                                  \\\n  } while (0)\n#else\n# define le_16(X, DATA)                                                       \\\n  do {                                                                        \\\n    memcpy(&X, DATA, 2);                                                      \\\n    X    = bswapu16((uint16_t)X);                                             \\\n    DATA = (char *)DATA + 2;                                                  \\\n  } while (0)\n#endif\n\n/* 32-bit */\n#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n# define le_32(X, DATA)                                                       \\\n  do {                                                                        \\\n    memcpy(&X, DATA, 4);                                                      \\\n    DATA = (char *)DATA + 4;                                                  \\\n  } while (0)\n#else\n# define le_32(X, DATA)                                                       \\\n  do {                                                                        \\\n    memcpy(&X, DATA, 4);                                                      \\\n    X    = bswapu32((uint32_t)X);                                             \\\n    DATA = (char *)DATA + 4;                                                  \\\n  } while (0)\n#endif\n\n/* 64-bit */\n#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n# define le_64(X, DATA)                                                       \\\n  do {                                                                        \\\n    memcpy(&X, DATA, 8);                                                      \\\n    DATA = (char *)DATA + 8;                                                  \\\n  } while (0)\n#else\n# define le_64(X, DATA)                                                       \\\n  do {                                                                        \\\n    memcpy(&X, DATA, 8);                                                      \\\n    X    = bswapu64((uint64_t)X);                                             \\\n    DATA = (char *)DATA + 8;                                                  \\\n  } while (0)\n#endif\n\n/* helper for big endian */\n\n/* 16-bit */\n#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n# define be_16(X, DATA)                                                       \\\n  do {                                                                        \\\n    memcpy(&X, DATA, 2);                                                      \\\n    DATA = (char *)DATA + 2;                                                  \\\n  } while (0)\n#else\n# define be_16(X, DATA)                                                       \\\n  do {                                                                        \\\n    memcpy(&X, DATA, 2);                                                      \\\n    X    = bswapu16((uint16_t)X);                                             \\\n    DATA = (char *)DATA + 2;                                                  \\\n  } while (0)\n#endif\n\n/* 32-bit */\n#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n# define be_32(X, DATA)                                                       \\\n  do {                                                                        \\\n    memcpy(&X, DATA, 4);                                                      \\\n    DATA = (char *)DATA + 4;                                                  \\\n  } while (0)\n#else\n# define be_32(X, DATA)                                                       \\\n  do {                                                                        \\\n    memcpy(&X, DATA, 4);                                                      \\\n    X    = bswapu32((uint32_t)X);                                             \\\n    DATA = (char *)DATA + 4;                                                  \\\n  } while (0)\n#endif\n\n/* 64-bit */\n#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n# define be_64(X, DATA)                                                       \\\n  do {                                                                        \\\n    memcpy(&X, DATA, 8);                                                      \\\n    DATA = (char *)DATA + 8;                                                  \\\n  } while (0)\n#else\n# define be_64(X, DATA)                                                       \\\n  do {                                                                        \\\n    memcpy(&X, DATA, 8);                                                      \\\n    X    = bswapu64((uint64_t)X);                                             \\\n    DATA = (char *)DATA + 8;                                                  \\\n  } while (0)\n#endif\n\n#define memcpy_endian64(isLittleEndian, X, DATA)                              \\\n  do {                                                                        \\\n    if (isLittleEndian) {                                                     \\\n      le_64(X, DATA);                                                         \\\n    } else {                                                                  \\\n      be_64(X, DATA);                                                         \\\n    }                                                                         \\\n  } while (0)\n \n#define memcpy_endian32(isLittleEndian, X, DATA)                              \\\n  do {                                                                        \\\n    if (isLittleEndian) {                                                     \\\n      le_32(X, DATA);                                                         \\\n    } else {                                                                  \\\n      be_32(X, DATA);                                                         \\\n    }                                                                         \\\n  } while (0)\n \n#define memcpy_endian16(isLittleEndian, X, DATA)                              \\\n  do {                                                                        \\\n    if (isLittleEndian) {                                                     \\\n      le_16(X, DATA);                                                         \\\n    } else {                                                                  \\\n      be_16(X, DATA);                                                         \\\n    }                                                                         \\\n  } while (0)\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* endian_h */\n"
  },
  {
    "path": "src/find.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n\nAK_EXPORT\nvoid *\nak_getId(void * __restrict objptr) {\n  return ak_mem_getId((void *)objptr);\n}\n\nAK_EXPORT\nAkResult\nak_setId(void * __restrict objptr,\n         const char * __restrict objectId) {\n  ak_mem_setId(objptr, (void *)objectId);\n  return AK_OK;\n}\n\nAK_EXPORT\nAkResult\nak_moveId(void * __restrict objptrOld,\n          void * __restrict objptrNew) {\n  char *objectId;\n\n  objectId = ak_getId(objptrOld);\n  if (objectId) {\n    ak_heap_setpm(objectId,\n                  objptrNew);\n\n    ak_setId(objptrOld, NULL);\n    ak_setId(objptrNew, objectId);\n  }\n  return AK_OK;\n}\n\nAK_EXPORT\nvoid *\nak_getObjectById(AkDoc * __restrict doc,\n                 const char * __restrict objectId) {\n  void *foundObject;\n\n  ak_mem_getMemById(doc,\n                    (void *)objectId,\n                    &foundObject);\n\n  return foundObject;\n}\n"
  },
  {
    "path": "src/geom/CMakeLists.txt",
    "content": "target_sources(${PROJECT_NAME} \n  PRIVATE\n  mesh.c\n)\n"
  },
  {
    "path": "src/geom/mesh.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n\nAK_EXPORT\nuint32_t\nak_meshInputCount(AkMesh * __restrict mesh) {\n  AkMeshPrimitive *prim;\n  uint32_t         count;\n\n  count = 0;\n  prim  = mesh->primitive;\n  while (prim) {\n    count += prim->inputCount;\n    prim = prim->next;\n  }\n\n  return count;\n}\n"
  },
  {
    "path": "src/id.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n#include \"id.h\"\n#include <math.h>\n#include <stdio.h>\n#include <string.h>\n\nAK_HIDE\nvoid\nak_id_newheap(AkHeap * __restrict heap) {\n  size_t *idp;\n  void   *idpstr;\n\n  heap->idheap = ak_heap_new(NULL, NULL, NULL);\n\n  /* default prefix */\n  idpstr = *(void **)ak_opt_get(AK_OPT_DEFAULT_ID_PREFIX);\n  idp    = ak_heap_alloc(heap->idheap, NULL, sizeof(size_t));\n  *idp   = 1;\n  ak_heap_setId(heap->idheap,\n                ak__alignof(idp),\n                idpstr);\n\n  ak_heap_attach(heap, heap->idheap);\n}\n\nAK_EXPORT\nconst char *\nak_generatId(AkDoc      * __restrict doc,\n             void       * __restrict parentmem,\n             const char * __restrict prefix) {\n  return ak_id_gen(ak_heap_getheap(doc),\n                   parentmem,\n                   prefix);\n}\n\nAK_HIDE\nconst char *\nak_id_gen(AkHeap     * __restrict heap,\n          void       * __restrict parentmem,\n          const char * __restrict prefix) {\n  AkHeap     *idheap;\n  size_t     *idp;\n  void       *idpf;\n  char       *id;\n  const char *idpstr;\n  size_t      size, tknsize;\n  AkResult    ret;\n\n  if (!prefix)\n    idpstr = *(const char **)ak_opt_get(AK_OPT_DEFAULT_ID_PREFIX);\n  else\n    idpstr = prefix;\n\n  idheap = heap->idheap;\n  if (!idheap) {\n    ak_id_newheap(heap);\n    idheap = heap->idheap;\n  }\n\n  ret = ak_heap_getMemById(idheap, (void *)idpstr, &idpf);\n\n  if (ret != AK_OK) {\n    idp  = ak_heap_alloc(idheap, NULL, sizeof(size_t));\n    *idp = 1;\n    ak_heap_setId(idheap,\n                  ak__alignof(idp),\n                  (void *)idpstr);\n  } else {\n    idp = idpf;\n  }\n\n  size = strlen(idpstr);\n\n  do {\n    AkHeapNode *node;\n    AkResult    ret;\n\n    /* we ensure that token > 0 when in ctor */\n    tknsize = (size_t)log10((double)*idp) + 1;\n\n    id = ak_heap_alloc(heap, parentmem, size + tknsize + 1);\n    id[size + tknsize] = '\\0';\n\n    strcpy(id, idpstr);\n    sprintf(id + size, \"%zu\", *idp);\n\n    ret = ak_heap_getNodeById(heap, (void *)id, &node);\n\n    ++(*idp);\n\n    /* ok no dups */\n    if (ret == AK_EFOUND)\n      break;\n\n    ak_free(id);\n  } while (true);\n  \n  return id;\n}\n"
  },
  {
    "path": "src/id.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_id_h\n#define ak_id_h\n\n#include \"common.h\"\n\nAK_HIDE\nvoid\nak_id_newheap(AkHeap * __restrict heap);\n\nAK_HIDE\nconst char *\nak_id_gen(AkHeap     * __restrict heap,\n          void       * __restrict parentmem,\n          const char * __restrict prefix);\n\n#endif /* ak_id_h */\n"
  },
  {
    "path": "src/image/CMakeLists.txt",
    "content": "target_sources(${PROJECT_NAME} \n  PRIVATE\n  image.c\n)\n"
  },
  {
    "path": "src/image/image.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../../include/ak/bbox.h\"\n#include \"../../include/ak/path.h\"\n#include <limits.h>\n\n#ifdef _MSC_VER\n#  ifndef PATH_MAX\n#    define PATH_MAX 260\n#  endif\n#endif\n\ntypedef struct AkImageConf {\n  AkImageLoadFromFileFn   loadFromFile;\n  AkImageLoadFromMemoryFn loadFromMemory;\n} AkImageConf;\n\nstatic AkImageConf ak__img_conf = {0};\n\nAK_EXPORT\nvoid\nak_imageInitLoader(AkImageLoadFromFileFn   fromFile,\n                   AkImageLoadFromMemoryFn fromMemory) {\n  ak__img_conf.loadFromFile         = fromFile;\n  ak__img_conf.loadFromMemory       = fromMemory;\n}\n\nAK_EXPORT\nvoid\nak_imageLoad(AkImage * __restrict image) {\n  AkHeap        *heap;\n  AkDoc         *doc;\n  AkImageData   *idata;\n  unsigned char *data;\n  bool           flipImage;\n\n  if (image->data)\n    return;\n\n  idata     = NULL;\n  data      = NULL;\n  heap      = ak_heap_getheap(image);\n  doc       = ak_heap_data(heap);\n  flipImage = false;\n\n  /* glTF uses top-left as origin */\n  if (doc->inf->flipImage) {\n    flipImage = ak_opt_get(AK_OPT_IMAGE_LOAD_FLIP_VERTICALLY);\n  }\n\n  if (image->initFrom) {\n    AkInitFrom *initFrom;\n    int         x, y, ch;\n\n    initFrom = image->initFrom;\n    if (initFrom->ref) {\n      char        pathbuf[PATH_MAX];\n      const char *path;\n\n      if (!ak__img_conf.loadFromFile)\n        return;\n\n      path                       = ak_fullpath(doc, initFrom->ref, pathbuf);\n      initFrom->resolvedFullPath = ak_strdup(initFrom, pathbuf);\n      image->data                = ak__img_conf.loadFromFile(heap, image, path, flipImage);\n    } else if (initFrom->buff && initFrom->buff->data) {\n      if (!ak__img_conf.loadFromMemory)\n        return;\n\n      image->data = ak__img_conf.loadFromMemory(heap, image, initFrom->buff, flipImage);\n    } else if (initFrom->hex) {\n      /* TODO: */\n    }\n  }\n}\n"
  },
  {
    "path": "src/instance/CMakeLists.txt",
    "content": "target_sources(${PROJECT_NAME} \n  PRIVATE\n  inst.c\n  list.c\n)\n"
  },
  {
    "path": "src/instance/inst.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../id.h\"\n\nAK_EXPORT\nAkInstanceGeometry*\nak_instanceMakeGeom(AkHeap     * __restrict heap,\n                    void       * __restrict memparent,\n                    AkGeometry * __restrict object) {\n  AkInstanceGeometry *instGeom;\n  \n  instGeom               = ak_heap_calloc(heap, memparent, sizeof(*instGeom));\n  instGeom->base.url.ptr = object;\n  instGeom->base.object  = object;\n  instGeom->base.type    = AK_INSTANCE_GEOMETRY;\n\n  return instGeom;\n}\n\nAK_EXPORT\nAkInstanceBase*\nak_instanceMake(AkHeap * __restrict heap,\n                void   * __restrict memparent,\n                void   * __restrict object) {\n  AkInstanceBase *instance;\n  const char     *id;\n\n  if (!object || !memparent || !heap)\n    return NULL;\n\n  instance = ak_heap_calloc(heap,\n                            memparent,\n                            sizeof(*instance));\n\n  /* we already have the object */\n  instance->object = object;\n\n  /* target must have id or we will generate an id for it */\n  id = ak_getId(object);\n  if (!id)\n    id = ak_id_gen(heap,\n                   object,\n                   NULL);\n\n  ak_url_init_with_id(heap->allocator,\n                      instance,\n                      (char *)id,\n                      &instance->url);\n\n  return instance;\n}\n\nAK_EXPORT\nvoid *\nak_instanceObject(AkInstanceBase *instanceBase) {\n  if (!instanceBase)\n    return NULL;\n\n  if (!instanceBase->object)\n    instanceBase->object = ak_getObjectByUrl(&instanceBase->url);\n\n  return instanceBase->object;\n}\n\nAK_EXPORT\nAkNode *\nak_instanceObjectNode(AkNode * node) {\n  AkInstanceBase *instanceBase;\n\n  instanceBase = &node->node->base;\n\n  if (!instanceBase->object)\n    instanceBase->object = ak_getObjectByUrl(&instanceBase->url);\n\n  return instanceBase->object;\n}\n\nAK_EXPORT\nAkGeometry *\nak_instanceObjectGeom(AkNode * node) {\n  AkInstanceBase *instanceBase;\n\n  instanceBase = &node->geometry->base;\n\n  if (!instanceBase->object)\n    instanceBase->object = ak_getObjectByUrl(&instanceBase->url);\n\n  return instanceBase->object;\n}\n\nAK_EXPORT\nAkGeometry *\nak_instanceObjectGeomId(AkDoc * __restrict doc,\n                        const char * id) {\n  AkNode         *node;\n  AkInstanceBase *instanceBase;\n\n  node = ak_getObjectById(doc, id);\n  if (!node)\n    return NULL;\n\n  instanceBase = &node->geometry->base;\n\n  if (!instanceBase->object)\n    instanceBase->object = ak_getObjectByUrl(&instanceBase->url);\n\n  return instanceBase->object;\n}\n\nAK_EXPORT\nAkNode*\nak_instanceMoveToSubNode(AkNode * __restrict node,\n                         AkInstanceBase     *inst) {\n  AkHeap *heap;\n  AkNode *subNode;\n  size_t  off;\n\n  heap    = ak_heap_getheap(node);\n  subNode = ak_heap_calloc(heap, node, sizeof(*node));\n  subNode->visible = true;\n\n  switch (inst->type) {\n    case AK_INSTANCE_GEOMETRY:\n      off = offsetof(AkNode, geometry);\n      break;\n    case AK_INSTANCE_LIGHT:\n      off = offsetof(AkNode, light);\n      break;\n    case AK_INSTANCE_CAMERA:\n      off = offsetof(AkNode, camera);\n      break;\n    case AK_INSTANCE_NODE:\n      off = offsetof(AkNode, node);\n      break;\n    default:\n      ak_free(subNode);\n      return NULL;\n      break;\n  }\n\n  if (inst->prev)\n    inst->prev->next = inst->next;\n\n  if (inst->next)\n    inst->next->prev = inst->prev;\n\n  if (*(void **)((char *)node + off) == inst)\n    *(void **)((char *)node + off) = NULL;\n\n  *(void **)((char *)subNode + off) = inst;\n\n  inst->node = subNode;\n  ak_heap_setpm(inst, subNode);\n\n  ak_addSubNode(node, subNode, false);\n\n  return subNode;\n}\n\nAK_EXPORT\nAkNode*\nak_instanceMoveToSubNodeIfNeeded(AkNode * __restrict node,\n                                 AkInstanceBase     *inst) {\n  void           *instObj,  *parentObject;\n  AkCoordSys     *coordSys, *instCoordSys;\n  AkInstanceBase *insti;\n  AkInstanceBase *instArray[] = {(AkInstanceBase *)node->geometry,\n                                 (AkInstanceBase *)node->node,\n                                 node->camera,\n                                 node->light};\n  int             i, instArrayLen;\n\n  instObj = ak_instanceObject(inst);\n  if (!instObj)\n    goto ret;\n\n  /* check if node coordsys is equal to instance */\n  coordSys     = ak_getCoordSys(node);\n  instCoordSys = ak_getCoordSys(instObj);\n\n  if (!ak_hasCoordSys(instObj))\n    goto ret;\n\n  parentObject = node->parent;\n  if (!parentObject)\n    parentObject = ak_mem_parent(node);\n\n  if ((ak_hasCoordSys(node)\n       && coordSys != instCoordSys)\n      || (!node->parent && ak_typeid(parentObject) == AKT_SCENE))\n    goto move;\n\n  /* check all instances coord sys in this node */\n  instArrayLen = AK_ARRAY_LEN(instArray);\n  for (i = 0; i < instArrayLen; i++) {\n    insti = instArray[i];\n    while (insti) {\n      if (insti != inst) {\n        void *instObji;\n        instObji = ak_instanceObject(insti);\n        if (!ak_hasCoordSys(instObji)\n            || ak_getCoordSys(instObji) != instCoordSys) {\n          goto move;\n        }\n      }\n      insti = insti->next;\n    }\n  }\n\nret:\n  return NULL;\nmove:\n  return ak_instanceMoveToSubNode(node, inst);\n}\n"
  },
  {
    "path": "src/instance/list.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../../include/ak/util.h\"\n#include <stdio.h>\n#include <assert.h>\n\nAK_EXPORT\nvoid\nak_instanceListAdd(AkInstanceList *list,\n                   AkInstanceBase *inst) {\n  AkHeap             *heap;\n  AkInstanceListItem *ili;\n\n  heap = ak_heap_getheap(list);\n  ili  = ak_heap_alloc(heap, list, sizeof(*ili));\n\n  ili->instance = inst;\n  ili->index    = ++list->lastindex;\n\n  list->count++;\n\n  ili->prev     = list->last;\n  ili->next     = NULL;\n\n  if (!list->first)\n    list->first = ili;\n\n  if (list->last)\n    list->last->next = ili;\n\n  list->last = ili;\n}\n\nAK_EXPORT\nvoid\nak_instanceListDel(AkInstanceList *list,\n                   AkInstanceListItem *item) {\n  if (list->first == item)\n    list->first = item->next;\n\n  if (list->last == item)\n    list->last = item->prev;\n\n  if (item->prev)\n    item->prev->next = item->next;\n\n  if (item->next)\n    item->next->prev = item->prev;\n\n  list->count--;\n\n  ak_free(item);\n}\n\nAK_EXPORT\nvoid\nak_instanceListEmpty(AkInstanceList *list) {\n  AkInstanceListItem *item, *tofree;\n  item = list->first;\n\n  while (item) {\n    tofree = item;\n    item = item->next;\n    ak_free(tofree);\n  }\n\n  list->first     = NULL;\n  list->last      = NULL;\n  list->count     = 0;\n  list->lastindex = 0;\n}\n\nchar*\nak_instanceName(AkInstanceListItem *item) {\n  AkHeap         *heap;\n  AkInstanceBase *inst;\n  char           *name, *objId;\n  void           *obj;\n  uint32_t        indexDigit;\n  size_t          idlen;\n\n  inst = item->instance;\n  assert(inst);\n\n  heap = ak_heap_getheap(item);\n\n  /* instance name */\n  if (inst->name)\n    return ak_heap_strdup(heap, item, inst->name);\n\n  /* we can use node.name or node.id for instance name */\n  if (inst->node) {\n    char *nodeid;\n\n    if (inst->node->name)\n      return ak_heap_strdup(heap,\n                            item,\n                            inst->node->name);\n\n    nodeid = ak_getId(inst->node);\n    if (nodeid)\n      return ak_heap_strdup(heap, item, nodeid);\n  }\n\n  obj = ak_instanceObject(inst);\n  if (!obj)\n    return NULL;\n\n  objId      = ak_getId(obj);\n  idlen      = strlen(objId);\n\n  indexDigit = ak_digitsize(item->index);\n  name       = ak_heap_alloc(heap,\n                             item,\n                             idlen + indexDigit + 2);\n\n  memcpy(name, objId, idlen);\n  sprintf(name + idlen, \"-%zu\", item->index);\n  name[idlen + indexDigit + 1] = '\\0';\n\n  return name;\n}\n"
  },
  {
    "path": "src/io/3mf/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n\nadd_subdirectory(imp)\n"
  },
  {
    "path": "src/io/3mf/README.md",
    "content": "# AssetKit: 3mf Status\n\n- [ ] 3mf XML\n"
  },
  {
    "path": "src/io/3mf/imp/3mf.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"3mf.h\"\n#include \"../../../miniz/miniz.h\"\n\nAK_HIDE\nAkResult\nimp_3mf(AkDoc ** __restrict dest, const char * __restrict filepath) {\n//  AkHeap          *heap;\n//  AkDoc           *doc;\n//  const xml_doc_t *xdoc;\n//  xml_t           *xml, *assetEl;\n//  AkAssetInf      *inf;\n//  xml_attr_t      *versionAttr;\n//  void            *xmlString, *zipped;\n//  AkLibraries     *libs;\n//  FListItem       *freeUsrData;\n//  _3MFState        dstVal, *dst;\n//  size_t           xmlSize, zipSize;\n//  AkResult         ret;\n//\n//  if ((ret = ak_readfile(filepath, NULL, &zipped, &zipSize)) != AK_OK)\n//    return ret;\n//\n//  /* if we know uncompressed fixed size, otherwise use chunked uncompress and merge it */\n//  xmlSize   = zipSize * 4;\n//  xmlString = malloc(xmlSize); /* TODO: enough size? */\n//\n//  /* TODO: optimize memory */\n//  /* unzip */\n//  if (mz_uncompress(xmlString, &xmlSize, zipped, zipSize) != MZ_OK) {\n//    goto err;\n//  }\n//\n//  if (mz_zip_reader_validate_mem(xmlString, xmlSize)) {\n//    mz_zip_archive zip;\n//    mz_zip_zero_struct(&zip);\n//    if (!mz_zip_reader_init_mem(&zip, xmlString, xmlSize, 0)) {\n//      free(xmlString);\n//      return AK_ERR_IO;\n//    }\n//\n//    if (mz_zip_reader_is_file_a_directory(&zip, \"3D/3dmodel.model\", 0)) {\n//      mz_zip_reader_end(&zip);\n//      free(xmlString);\n//      return AK_ERR_IO;\n//    }\n//\n//    xmlSize = mz_zip_reader_get_file_stat(&zip, \"3D/3dmodel.model\")->m_uncomp_size;\n//    xmlString = realloc(xmlString, xmlSize);\n//    if (!mz_zip_reader_extract_file_to_mem(&zip,\n//                                           \"3D/3dmodel.model\",\n//                                           xmlString,\n//                                           xmlSize,\n//                                           0)) {\n//      mz_zip_reader_end(&zip);\n//      free(xmlString);\n//      return AK_ERR_IO;\n//    }\n//\n//    mz_zip_reader_end(&zip);\n//  }\n//\n//  xdoc = xml_parse(xmlString, XML_PREFIXES | XML_READONLY);\n//  if (!xdoc || !(xml = xdoc->root)) {\n//    if (xdoc) { free((void *)xdoc); }\n//    ak_releasefile(xmlString, xmlSize);\n//    return AK_ERR;\n//  }\n//\n//  heap = ak_heap_new(NULL, NULL, NULL);\n//  doc  = ak_heap_calloc(heap, NULL, sizeof(*doc));\n//\n//  doc->inf            = ak_heap_calloc(heap, doc, sizeof(*doc->inf));\n//  doc->inf->name      = filepath;\n//  doc->inf->dir       = ak_path_dir(heap, doc, filepath);\n//  doc->inf->flipImage = true;\n//  doc->inf->ftype     = AK_FILE_TYPE_COLLADA;\n//  doc->coordSys       = AK_YUP; /* Default */\n//\n//  /* for fixing skin and morph vertices */\n//  doc->reserved = rb_newtree_ptr();\n//  ((RBTree *)doc->reserved)->onFreeNode = ak_daeFreeDupl;\n//\n//  if (doc->inf->dir)\n//    doc->inf->dirlen = strlen(doc->inf->dir);\n//\n//  ak_heap_setdata(heap, doc);\n//  ak_id_newheap(heap);\n//\n//  memset(&dstVal, 0, sizeof(dstVal));\n//\n//  dstVal.doc          = doc;\n//  dstVal.heap         = heap;\n//  dstVal.tempmem      = ak_heap_alloc(heap, doc, sizeof(void*));\n//  dstVal.meshInfo     = rb_newtree_ptr();\n//  dstVal.inputmap     = rb_newtree_ptr();\n//  dstVal.texmap       = rb_newtree_ptr();\n//  dstVal.instanceMap  = rb_newtree_ptr();\n//\n//  dstVal.ctlrSkinMap  = rb_newtree_ptr();\n//  dstVal.ctlrMorphMap = rb_newtree_ptr();\n//\n//  dst                 = &dstVal;\n//\n//  dstVal.texmap->userData = dst;\n//\n//  /* get version info */\n//  /* because it is current and most used version */\n//  dst->version = AK_COLLADA_VERSION_141;\n//  if ((versionAttr = xmla(xml, _s_dae_version))) {\n//    ak_enumpair *v;\n//\n//    for (v = daeVersions; v->key; v++) {\n//      if (!strncmp(v->key, versionAttr->val, versionAttr->valsize)) {\n//        dst->version = v->val;\n//        break;\n//      }\n//    }\n//  }\n//\n//  libs    = &doc->lib;\n//  assetEl = NULL;\n//  xml     = xml->val;\n//\n//  /* with default Asset Parameters */\n//  assetEl = xml_elem(xml->parent, _s_dae_asset);\n//  if ((inf = dae_asset(dst, assetEl, doc, &doc->inf->base))) {\n//    doc->coordSys = inf->coordSys;\n//    doc->unit     = inf->unit;\n//  }\n//\n//  while (xml) {\n//    if (xml_tag_eq(xml, _s_dae_lib_cameras)) {\n//      dae_lib(dst, xml, _s_dae_camera, dae_cam, &libs->cameras);\n//    } else if (xml_tag_eq(xml, _s_dae_lib_lights)) {\n//      dae_lib(dst, xml, _s_dae_light, dae_light, &libs->lights);\n//    } else if (xml_tag_eq(xml, _s_dae_lib_geometries)) {\n//      dae_lib(dst, xml, _s_dae_geometry, dae_geom, &libs->geometries);\n//    } else if (xml_tag_eq(xml, _s_dae_lib_effects)) {\n//      dae_lib(dst, xml, _s_dae_effect, dae_effect, &libs->effects);\n//    } else if (xml_tag_eq(xml, _s_dae_lib_images)) {\n//      dae_lib(dst, xml, _s_dae_image, dae_image, &libs->libimages);\n//    } else if (xml_tag_eq(xml, _s_dae_lib_materials)) {\n//      dae_lib(dst, xml, _s_dae_material, dae_material, &libs->materials);\n//    } else if (xml_tag_eq(xml, _s_dae_lib_controllers)) {\n//      dae_lib(dst, xml, _s_dae_controller, dae_ctlr, &libs->controllers);\n//    } else if (xml_tag_eq(xml, _s_dae_lib_visual_scenes)) {\n//      dae_lib(dst, xml, _s_dae_visual_scene, dae_vscene, &libs->visualScenes);\n//    } else if (xml_tag_eq(xml, _s_dae_lib_nodes)) {\n//      dae_lib(dst, xml, _s_dae_node, dae_node2, &libs->nodes);\n//    } else if (xml_tag_eq(xml, _s_dae_lib_animations)) {\n//      dae_lib(dst, xml, _s_dae_animation, dae_anim, &libs->animations);\n//    } else if (xml_tag_eq(xml, _s_dae_scene)) {\n//      dae_scene(dst, xml);\n//    }\n//    xml = xml->next;\n//  }\n//\n//  *dest = doc;\n//\n//  /* post-parse operations */\n//  dae_postscript(dst);\n//\n//  /* cleanup up details */\n//  freeUsrData = dst->linkedUserData;\n//  while (freeUsrData) {\n//    void *tofree;\n//\n//    if ((tofree = ak_userData(freeUsrData->data)))\n//      ak_free(tofree);\n//\n//    ak_heap_ext_rm(heap, ak__alignof(freeUsrData->data), AK_HEAP_NODE_FLAGS_USR);\n//    freeUsrData = freeUsrData->next;\n//  }\n//\n//  ak_free(dstVal.tempmem);\n//\n//  flist_sp_destroy(&dst->linkedUserData);\n//\n//  rb_destroy(dstVal.meshInfo);\n//  rb_destroy(dstVal.inputmap);\n//  rb_destroy(dstVal.texmap);\n//  rb_destroy(dstVal.instanceMap);\n//\n//  flist_sp_destroy(&dstVal.vertMap);\n//\n//  rb_destroy(dstVal.ctlrSkinMap);\n//  rb_destroy(dstVal.ctlrMorphMap);\n//\n//  if (xdoc)\n//    free((void *)xdoc);\n//\n//  if (xmlString)\n//    ak_releasefile(xmlString, xmlSize);\n//\n//  /* TODO: memory leak, free this RBTree*/\n//  /* rb_destroy(doc->reserved); */\n//\n//  return AK_OK;\n//\n//err:\n  return AK_EBADF;\n}\n"
  },
  {
    "path": "src/io/3mf/imp/3mf.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _3mf_h\n#define _3mf_h\n\n#include \"common.h\"\n\nAK_HIDE\nAkResult\nimp_3mf(AkDoc ** __restrict dest, const char * __restrict filepath);\n\n#endif /* _3mf_h */\n"
  },
  {
    "path": "src/io/3mf/imp/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/3mf/imp/common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _3mf_common_h\n#define _3mf_common_h\n\n#include \"../../../../include/ak/assetkit.h\"\n#include \"../../../../include/ak/url.h\"\n#include \"../../../common.h\"\n#include \"../../../utils.h\"\n#include \"../../../xml.h\"\n\n#include <ds/forward-list-sep.h>\n#include <string.h>\n#include <xml/xml.h>\n#include <xml/attrib.h>\n\ntypedef AK_ALIGN(16) struct _3MFState {\n  AkHeap          *heap;\n  void            *tempmem;\n  AkDoc           *doc;\n  bool             stop;\n} _3MFState;\n\n#endif /* _3mf_common_h */\n"
  },
  {
    "path": "src/io/CMakeLists.txt",
    "content": "add_subdirectory(common)\nadd_subdirectory(dae)\nadd_subdirectory(gltf)\nadd_subdirectory(obj)\nadd_subdirectory(stl)\nadd_subdirectory(ply)\nadd_subdirectory(3mf)\n"
  },
  {
    "path": "src/io/common/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/common/postscript.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"postscript.h\"\n#include \"../../mesh/index.h\"\n\nAK_HIDE\nvoid\nio_postscript(AkDoc * __restrict doc) {\n  AkLibrary  *geomLib;\n  AkGeometry *geom;\n\n  geomLib = doc->lib.geometries;\n  while (geomLib) {\n    geom = (void *)geomLib->chld;\n    while (geom) {\n      AkObject *primitive;\n\n      primitive = geom->gdata;\n      switch ((AkGeometryType)primitive->type) {\n        case AK_GEOMETRY_MESH: {\n          AkMesh           *mesh;\n          AkMeshEditHelper *edith;\n\n          mesh = ak_objGet(primitive);\n          \n          ak_meshBeginEdit(mesh);\n\n          edith                 = mesh->edith;\n          edith->skipFixIndices = true; /* to do it once per mesh */\n\n          if (ak_opt_get(AK_OPT_TRIANGULATE))\n            ak_meshTriangulate(mesh);\n\n          if (ak_opt_get(AK_OPT_GEN_NORMALS_IF_NEEDED))\n            if (ak_meshNeedsNormals(mesh))\n              ak_meshGenNormals(mesh);\n\n          edith->skipFixIndices = false;\n          ak_meshFixIndices(mesh);\n\n          ak_meshEndEdit(mesh);\n\n          if (ak_opt_get(AK_OPT_COMPUTE_BBOX))\n            ak_bbox_mesh(mesh);\n          \n        }\n        default:\n          break;\n      }\n      geom = (void *)geom->base.next;\n    }\n\n    geomLib = geomLib->next;\n  }\n}\n"
  },
  {
    "path": "src/io/common/postscript.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ply_postscript_h\n#define ply_postscript_h\n\n#include \"../../../include/ak/assetkit.h\"\n\nAK_HIDE\nvoid\nio_postscript(AkDoc * __restrict doc);\n\n#endif /* ply_postscript_h */\n"
  },
  {
    "path": "src/io/common/util.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"util.h\"\n\nAK_HIDE\nAkMesh*\nak_allocMesh(AkHeap      * __restrict heap,\n             AkLibrary   * __restrict memp,\n             AkGeometry ** __restrict geomLink) {\n  AkGeometry *geom;\n  AkObject   *meshObj;\n  AkMesh     *mesh;\n\n  /* create geometries */\n  geom              = ak_heap_calloc(heap, memp, sizeof(*geom));\n  geom->materialMap = ak_map_new(ak_cmp_str);\n\n  /* destroy heap with this object */\n  ak_setAttachedHeap(geom, geom->materialMap->heap);\n\n  meshObj     = ak_objAlloc(heap, geom, sizeof(AkMesh), AK_GEOMETRY_MESH, true);\n  geom->gdata = meshObj;\n  mesh        = ak_objGet(meshObj);\n  mesh->geom  = geom;\n  \n  if (geomLink)\n    *geomLink = geom;\n\n  return mesh;\n}\n\nAK_HIDE\nAkInput*\nio_addInput(AkHeap          * __restrict heap,\n            AkDataContext   * __restrict dctx,\n            AkMeshPrimitive * __restrict prim,\n            AkInputSemantic              sem,\n            const char      * __restrict semRaw,\n            AkComponentSize              compSize,\n            AkTypeId                     type,\n            uint32_t                     offset) {\n  AkDoc      *doc;\n  AkBuffer   *buff;\n  AkAccessor *acc;\n  AkInput    *inp;\n  AkTypeDesc *typeDesc;\n  int         nComponents;\n\n  doc         = ak_heap_data(heap);\n  typeDesc    = ak_typeDesc(type);\n  nComponents = (int)compSize;\n\n  buff         = ak_heap_calloc(heap, doc, sizeof(*buff));\n  buff->data   = ak_heap_alloc(heap, buff, dctx->usedsize);\n  buff->length = dctx->usedsize;\n  ak_data_join(dctx, buff->data, 0, 0);\n  \n  flist_sp_insert(&doc->lib.buffers, buff);\n  \n  acc                    = ak_heap_calloc(heap, doc, sizeof(*acc));\n  acc->buffer            = buff;\n  acc->byteLength        = buff->length;\n  acc->byteStride        = typeDesc->size * nComponents;\n  acc->componentSize     = compSize;\n  acc->componentType     = type;\n  acc->bytesPerComponent = typeDesc->size;\n  acc->componentCount    = nComponents;\n  acc->fillByteSize      = typeDesc->size * nComponents;\n  acc->count             = (uint32_t)dctx->itemcount;\n\n  inp                    = ak_heap_calloc(heap, prim, sizeof(*inp));\n  inp->accessor          = acc;\n  inp->semantic          = sem;\n  inp->semanticRaw       = ak_heap_strdup(heap, inp, semRaw);\n  inp->offset            = offset;\n\n  ak_retain(acc);\n\n  inp->next   = prim->input;\n  prim->input = inp;\n  prim->inputCount++;\n  \n  return inp;\n}\n\nAK_HIDE\nAkAccessor*\nio_acc(AkHeap          * __restrict heap,\n       AkDoc           * __restrict doc,\n       AkComponentSize              compSize,\n       AkTypeId                     type,\n       uint32_t                     count,\n       AkBuffer        * __restrict buff) {\n  AkAccessor *acc;\n  AkTypeDesc *typeDesc;\n  int         nComponents;\n\n  typeDesc    = ak_typeDesc(type);\n  nComponents = (int)compSize;\n  \n  acc                    = ak_heap_calloc(heap, doc, sizeof(*acc));\n  acc->buffer            = buff;\n  acc->byteLength        = buff->length;\n  acc->byteStride        = typeDesc->size * nComponents;\n  acc->componentSize     = compSize;\n  acc->componentType     = type;\n  acc->bytesPerComponent = typeDesc->size;\n  acc->componentCount    = nComponents;\n  acc->fillByteSize      = typeDesc->size * nComponents;\n  acc->count             = count;\n\n  return acc;\n}\n\nAK_HIDE\nAkInput*\nio_input(AkHeap          * __restrict heap,\n         AkMeshPrimitive * __restrict prim,\n         AkAccessor      * __restrict acc,\n         AkInputSemantic              sem,\n         const char      * __restrict semRaw,\n         uint32_t                     offset) {\n  AkInput *inp;\n\n  inp                 = ak_heap_calloc(heap, prim, sizeof(*inp));\n  inp->accessor       = acc;\n  inp->semantic       = sem;\n  inp->semanticRaw    = ak_heap_strdup(heap, inp, semRaw);\n  inp->offset         = offset;\n\n  inp->next   = prim->input;\n  prim->input = inp;\n  prim->inputCount++;\n  \n  return inp;\n}\n\n"
  },
  {
    "path": "src/io/common/util.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef io_common_util_h\n#define io_common_util_h\n\n#include \"../../../include/ak/assetkit.h\"\n#include \"../../common.h\"\n#include \"../../utils.h\"\n#include \"../../tree.h\"\n#include \"../../json.h\"\n#include \"../../data.h\"\n\n#include <string.h>\n#include <stdlib.h>\n\nAK_HIDE\nAkMesh*\nak_allocMesh(AkHeap      * __restrict heap,\n             AkLibrary   * __restrict memp,\n             AkGeometry ** __restrict geomLink);\n\nAK_HIDE\nAkInput*\nio_addInput(AkHeap          * __restrict heap,\n            AkDataContext   * __restrict dctx,\n            AkMeshPrimitive * __restrict prim,\n            AkInputSemantic              sem,\n            const char      * __restrict semRaw,\n            AkComponentSize              compSize,\n            AkTypeId                     type,\n            uint32_t                     offset);\n\nAK_HIDE\nAkAccessor*\nio_acc(AkHeap          * __restrict heap,\n       AkDoc           * __restrict doc,\n       AkComponentSize              compSize,\n       AkTypeId                     type,\n       uint32_t                     count,\n       AkBuffer        * __restrict buff);\n\nAK_HIDE\nAkInput*\nio_input(AkHeap          * __restrict heap,\n         AkMeshPrimitive * __restrict prim,\n         AkAccessor      * __restrict acc,\n         AkInputSemantic              sem,\n         const char      * __restrict semRaw,\n         uint32_t                     offset);\n\n#endif /* io_common_util_h */\n"
  },
  {
    "path": "src/io/dae/1.4/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/dae/1.4/dae14.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../../../include/ak/assetkit.h\"\n#include \"../common.h\"\n#include \"dae14.h\"\n\nAK_HIDE\nvoid\ndae14_loadjobs_add(DAEState   * __restrict  dst,\n                   void       *  __restrict parent,\n                   void       * __restrict  value,\n                   AkDae14LoadJobType       type) {\n  AkDae14LoadJob *job, *last;\n\n  if (!dst)\n    return;\n\n  job = ak_heap_calloc(dst->heap,\n                       NULL,\n                       sizeof(*job));\n  job->parent = parent;\n  job->type   = type;\n  job->value  = value;\n\n  last = dst->jobs14;\n  if (last)\n    last->prev = job;\n\n  job->next   = dst->jobs14;\n  dst->jobs14 = job;\n}\n\nAK_HIDE\nvoid\ndae14_loadjobs_finish(DAEState * __restrict dst) {\n  AkDae14LoadJob *job;\n\n  job = dst->jobs14;\n  while (job) {\n    switch (job->type) {\n      case AK_DAE14_LOADJOB_SURFACE: {\n        AkNewParam     *surfaceParam;\n        AkDae14Surface *surface;\n        AkContext       ctx;\n\n        memset(&ctx, 0, sizeof(ctx));\n        ctx.doc = dst->doc;\n\n        surfaceParam = ak_sid_resolve(&ctx, job->value, NULL);\n        if (surfaceParam) {\n          AkSampler      *sampler;\n          AkInstanceBase *instanceImage;\n\n          surface = surfaceParam->val->value;\n\n          /* surface may already migrated to 1.5+ */\n          if (!surface->instanceImage) {\n            AkImage *image;\n\n            sampler = job->parent;\n\n            /* convert initFrom to instance_image */\n            instanceImage = ak_heap_calloc(dst->heap,\n                                           sampler,\n                                           sizeof(*instanceImage));\n            if (surface->extra) {\n              instanceImage->extra = surface->extra;\n              surface->extra       = NULL;\n              ak_mem_setp(instanceImage->extra,\n                          instanceImage);\n            }\n\n            rb_insert(dst->instanceMap, sampler, instanceImage);\n            ak_url_init_with_id(dst->heap->allocator,\n                                instanceImage,\n                                (char *)surface->initFrom->image,\n                                &instanceImage->url);\n\n            /* TODO: */\n//            sampler->instanceImage = instanceImage;\n            surface->instanceImage = instanceImage;\n\n            /* convert other params to update/new image */\n            image = ak_instanceObject(instanceImage);\n            if (image) {\n              if (surface->initFrom) {\n                image->initFrom->face       = surface->initFrom->face;\n                image->initFrom->mipIndex   = surface->initFrom->mip;\n                image->initFrom->arrayIndex = surface->initFrom->slice;\n              }\n\n              image->renderable = surface->initAsTarget;\n            }\n          }\n        }\n\n        break;\n      }\n    }\n\n    job = job->next;\n  }\n\n  /* cleanup */\n  job = dst->jobs14;\n  while (job) {\n    AkDae14LoadJob *tofree;\n    \n    tofree = job;\n    switch (job->type) {\n      case AK_DAE14_LOADJOB_SURFACE: {\n        AkNewParam     *surfaceParam;\n        AkDae14Surface *surface;\n        AkContext       ctx;\n\n        memset(&ctx, 0, sizeof(ctx));\n        ctx.doc = dst->doc;\n\n        surfaceParam = ak_sid_resolve(&ctx, job->value, NULL);\n        if (surfaceParam) {\n          void *parentOfParam;\n\n          parentOfParam = ak_mem_parent(surfaceParam);\n\n          surface = surfaceParam->val->value;\n          ak_free(surface);\n\n          if (surfaceParam->prev)\n            surfaceParam->prev->next = surfaceParam->next;\n\n          if (surfaceParam->next)\n            surfaceParam->next->prev = surfaceParam->prev;\n\n          switch (ak_typeid(parentOfParam)) {\n            case AKT_EFFECT: {\n              AkEffect *effect;\n              effect = parentOfParam;\n              if (effect->newparam == surfaceParam)\n                effect->newparam = surfaceParam->next;\n              break;\n            }\n            case AKT_PROFILE: {\n              AkProfile *profile;\n              profile = parentOfParam;\n              if (profile->newparam == surfaceParam)\n                profile->newparam = surfaceParam->next;\n              break;\n            }\n\n            default: break;\n          }\n\n          ak_free(surfaceParam);\n        }\n\n        break;\n      }\n    }\n\n    job = job->next;\n\n    ak_free(tofree->value);\n    ak_free(tofree);\n  }\n\n  dst->jobs14 = NULL;\n}\n"
  },
  {
    "path": "src/io/dae/1.4/dae14.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae14_h\n#define dae14_h\n\n#include \"../common.h\"\n\ntypedef enum AkDae14LoadJobType {\n  AK_DAE14_LOADJOB_SURFACE = 1\n} AkDae14LoadJobType;\n\ntypedef struct AkDae14LoadJob {\n  struct AkDae14LoadJob *prev;\n  struct AkDae14LoadJob *next;\n  void                  *parent;\n  void                  *value;\n  AkDae14LoadJobType     type;\n} AkDae14LoadJob;\n\ntypedef struct AkDae14SurfaceFrom {\n  const char *image;\n  AkUInt      mip;\n  AkUInt      slice;\n  AkFace      face;\n} AkDae14SurfaceFrom;\n\ntypedef struct AkDae14Surface {\n  AkInstanceBase *instanceImage;\n  AkTree         *extra;\n  const char     *format;\n  AkImageFormat   formatHint;\n  AkImageSize     size;\n  float           viewportRatio[2];\n  int             mipLevels;\n  bool            mipmapGenerate;\n\n  /* initializers */\n  bool                  initAsNull;\n  bool                  initAsTarget;\n  AkDae14SurfaceFrom   *initFrom;\n} AkDae14Surface;\n\nAK_HIDE\nvoid\ndae14_loadjobs_add(DAEState   * __restrict  dst,\n                   void       *  __restrict parent,\n                   void       * __restrict  value,\n                   AkDae14LoadJobType       type);\n\nAK_HIDE\nvoid\ndae14_loadjobs_finish(DAEState * __restrict dst);\n\n#endif /* dae14_h */\n"
  },
  {
    "path": "src/io/dae/1.4/image.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"image.h\"\n#include \"../core/asset.h\"\n#include \"../core/enum.h\"\n\nAK_HIDE\nvoid\ndae14_fxMigrateImg(DAEState * __restrict dst,\n                   xml_t    * __restrict xml,\n                   void     * __restrict memp) {\n  AkHeap     *heap;\n  AkDoc      *doc;\n  AkImage    *img;\n  AkInitFrom *initFrom;\n  const char *format;\n  AkLibrary  *lib;\n\n  heap = dst->heap;\n  doc  = dst->doc;\n\n  if (memp)\n    lib = memp;\n  else\n    lib = ak_libImageFirstOrCreat(doc);\n\n  img           = ak_heap_calloc(heap, lib, sizeof(*img));\n  initFrom      = ak_heap_calloc(heap, img, sizeof(*img->initFrom));\n  img->initFrom = initFrom;\n\n  xmla_setid(xml, heap, img);\n  \n  img->name = xmla_strdup_by(xml, heap, _s_dae_name, img);\n\n  format = xmla_strdup_by(xml, heap, _s_dae_format, img->initFrom);\n  initFrom->mipsGenerate = false; /* 1.4's default, 1.5's is true */\n  initFrom->depth        = xmla_u32(xmla(xml, _s_dae_depth), 0);\n  \n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, img, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_data)) {\n      AkHexData *hex;\n      hex = ak_heap_calloc(heap, initFrom, sizeof(*hex));\n      \n      hex->format = format;\n      ak_heap_setpm((void *)format, hex);\n      \n      if (hex->format) {\n        hex->hexval        = xml_strdup(xml, heap, hex);\n        img->initFrom->hex = hex;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_init_from)) {\n      img->initFrom->ref = xml_strdup(xml, heap, initFrom);\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      img->extra = tree_fromxml(heap, img, xml);\n    }\n    xml = xml->next;\n  }\n\n  if (!memp)\n    ak_libInsertInto(lib, img, -1, offsetof(AkImage, next));\n}\n"
  },
  {
    "path": "src/io/dae/1.4/image.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_14_image_h\n#define ak_14_image_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ndae14_fxMigrateImg(DAEState * __restrict dst,\n                   xml_t    * __restrict xml,\n                   void     * __restrict memp);\n\n#endif /* ak_14_image_h */\n"
  },
  {
    "path": "src/io/dae/1.4/surface.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"surface.h\"\n#include \"../core/asset.h\"\n#include \"../core/enum.h\"\n\nAK_HIDE\nAkDae14Surface*\ndae14_surface(DAEState * __restrict dst,\n              xml_t    * __restrict xml,\n              void     * __restrict memp) {\n  AkHeap         *heap;\n  AkDae14Surface *surf;\n  xml_attr_t     *att;\n  const xml_t    *sval;\n\n  heap = dst->heap;\n  surf = ak_heap_calloc(heap, memp, sizeof(*surf));\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_init_from)) {\n      AkDae14SurfaceFrom *initFrom;\n      initFrom = ak_heap_calloc(heap, surf, sizeof(*heap));\n      initFrom->mip   = xmla_u32(xmla(xml, _s_dae_mip),   0);\n      initFrom->slice = xmla_u32(xmla(xml, _s_dae_slice), 0);\n      \n      if ((att = xmla(xml, _s_dae_face))) {\n        initFrom->face = dae_face(att);\n      } else {\n        initFrom->face = AK_FACE_POSITIVE_Y;\n      }\n\n      initFrom->image = xml_strdup(xml, heap, initFrom);\n      surf->initFrom  = initFrom;\n    } else if (xml_tag_eq(xml, _s_dae_init_as_target)) {\n      surf->initAsTarget = true; /* becuse the element exists */\n    } else if (xml_tag_eq(xml, _s_dae_format)) {\n      surf->format = xml_strdup(xml, heap, surf);\n    } else if (xml_tag_eq(xml, _s_dae_format_hint)) {\n      AkImageFormat *format;\n      xml_t         *xfmt;\n      \n      format = ak_heap_calloc(heap, memp, sizeof(*format));\n      \n      xfmt = xml->val;\n      while (xfmt) {\n        if (xml_tag_eq(xfmt, _s_dae_channels) && (sval = xmls(xfmt))) {\n          format->channel = dae_enumChannel(sval->val, sval->valsize);\n        } else if (xml_tag_eq(xfmt, _s_dae_range) && (sval = xmls(xfmt))) {\n          format->range = dae_range(sval->val, sval->valsize);\n        } else if (xml_tag_eq(xfmt, _s_dae_precision) && (sval = xmls(xfmt))) {\n          format->precision = dae_precision(sval->val, sval->valsize);\n        } else if (xml_tag_eq(xfmt, _s_dae_option)) {\n          format->space = xml_strdup(xml, heap, format);\n        } else if (xml_tag_eq(xfmt, _s_dae_exact)) {\n          format->exact = xml_strdup(xml, heap, format);\n        }\n        xfmt = xfmt->next;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_size) && (sval = xmls(xml))) {\n      AkUInt size[3];\n      xml_strtoui_fast(sval, size, 3);\n      \n      surf->size.width  = size[0];\n      surf->size.height = size[1];\n      surf->size.depth  = size[2];\n    } else if (xml_tag_eq(xml, _s_dae_viewport_ratio) && (sval = xmls(xml))) {\n      xml_strtof_fast(sval, surf->viewportRatio, 2);\n    } else if (xml_tag_eq(xml, _s_dae_mip_levels) && (sval = xmls(xml))) {\n      surf->mipLevels = (int)strtol(sval->val, NULL, 10);\n    } else if (xml_tag_eq(xml, _s_dae_mipmap_generate) && (sval = xmls(xml))) {\n      surf->mipmapGenerate = (bool)strtol(sval->val, NULL, 10);\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      surf->extra = tree_fromxml(heap, surf, xml);\n    }\n    xml = xml->next;\n  }\n\n  return surf;\n}\n"
  },
  {
    "path": "src/io/dae/1.4/surface.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_surface_h\n#define ak_surface_h\n\n#include \"../common.h\"\n#include \"dae14.h\"\n\nAK_HIDE\nAkDae14Surface*\ndae14_surface(DAEState * __restrict dst,\n              xml_t    * __restrict xml,\n              void     * __restrict memp);\n\n#endif /* ak_surface_h */\n"
  },
  {
    "path": "src/io/dae/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n\nadd_subdirectory(core)\nadd_subdirectory(fx)\nadd_subdirectory(fixup)\nadd_subdirectory(bugfix)\nadd_subdirectory(1.4)\nadd_subdirectory(brep)"
  },
  {
    "path": "src/io/dae/README.md",
    "content": "# AssetKit: COLLADA Status\n\n- [x] Single Interface for glTF and COLLADA\n- [x] COLLADA 1.4 / 1.4.1\n- [x] COLLADA 1.5\n- [x] Object-based Asset support\n  - Resolving Asset support for an element\n- [x] Fix / Convert UP axis to any other\n- [x] ID resolving\n- [x] SID resolving\n- [x] Bugfix some DAE files\n- [x] Convert angles to Radians \n- [x] Convert Multi-Index indices to Single-Index indices by keeping primitive indices\n- [x] Options to Generate Mesh Normals\n- [x] Option to Triangulate Polygons\n- [x] Libraries support\n- [x] Geometries\n  - [x] Meshes (Triangles, Polygons, Lines)\n  - [x] Brep\n    - Curves, Nurbs, Solids... \n- [x] Nodes\n  - [x] Instances (instance Geometry, Light, Camera...)\n  - [x] Simplified Controller mechanism into **node->morpher** and **node->skinner**\n  - [x] Fix camera node transform\n  - [x] **bind_material** and bind vertex input support to bind material dynamically \n- [x] Scenes\n  - Active Scene\n  - Visual Scenes\n- [x] Cameras\n- [x] Lights\n- [x] Materials\n  - [x] Images\n  - [x] Samplers\n  - [x] Textures\n  - [x] Common Profile\n      - [x] Phong, Blinn, Lambert and Constant\n  - ⚠️ Other profiles were supported, but currently they are removed. But multi profile is still supported. We can support other profiles in the future.\n  - [x] Transparency (A_ONE, RGB_ONE, A_ZERO, RGB_ZERO)\n- [x] Skin\n- [x] Morph\n- [x] Animations\n- [x] Extra\n- [x] Load external DAE files and cache them\n- [ ] Parse MathML formulas\n- [ ] Physics\n- [ ] Kinematics\n- [ ] ZAE\n- [ ] More to work on...\n\n### Trademarks\n\nglTF and COLLADA and their logos are trademarks of Khronos Group.\n"
  },
  {
    "path": "src/io/dae/brep/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/dae/brep/brep.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"brep.h\"\n#include \"../core/source.h\"\n#include \"../core/vert.h\"\n#include \"curve.h\"\n#include \"surface.h\"\n#include \"topology.h\"\n\nAK_HIDE\nAkObject*\ndae_brep(DAEState   * __restrict dst,\n         xml_t      * __restrict xml,\n         AkGeometry * __restrict geom) {\n  AkObject     *obj;\n  AkBoundryRep *brep;\n  AkHeap       *heap;\n  AkSource     *source;\n\n  heap   = dst->heap;\n  xml    = xml->val;\n\n  obj    = ak_objAlloc(heap, geom, sizeof(*brep), AK_GEOMETRY_BREP, true);\n  brep   = ak_objGet(obj);\n\n  brep->geom = geom;\n\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_curves)) {\n      brep->curves = dae_curves(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_surface_curves)) {\n      brep->surfaceCurves = dae_curves(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_surfaces)) {\n      brep->surfaces = dae_surfaces(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_source)) {\n      if ((source = dae_source(dst, xml, NULL, 0))) {\n        source->next  = brep->source;\n        brep->source = source;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_vertices)) {\n      brep->vertices = dae_vert(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_edges)) {\n      brep->edges = dae_edges(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_wires)) {\n      brep->wires = dae_wires(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_faces)) {\n      brep->faces = dae_faces(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_pcurves)) {\n      brep->pcurves = dae_pcurves(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_shells)) {\n      brep->shells = dae_shells(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_solids)) {\n      brep->solids = dae_solids(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      brep->extra = tree_fromxml(heap, brep, xml);\n    }\n    xml = xml->next;\n  }\n\n  return obj;\n}\n"
  },
  {
    "path": "src/io/dae/brep/brep.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_brep_h\n#define dae_brep_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkObject*\ndae_brep(DAEState   * __restrict dst,\n         xml_t      * __restrict xml,\n         AkGeometry * __restrict geom);\n\n#endif /* dae_brep_h */\n"
  },
  {
    "path": "src/io/dae/brep/curve.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"curve.h\"\n#include \"nurb.h\"\n#include \"../../../array.h\"\n\nAK_HIDE\nAkCurve*\ndae_curve(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp) {\n  AkHeap      *heap;\n  AkObject    *obj;\n  AkCurve     *curve;\n  const xml_t *sval;\n\n  heap  = dst->heap;\n  curve = ak_heap_calloc(heap, memp, sizeof(*curve));\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_line)) {\n      AkLine *line;\n      xml_t  *xline;\n      \n      xline = xml->val;\n      obj   = ak_objAlloc(heap, curve, sizeof(*line), AK_CURVE_LINE, true);\n      line  = ak_objGet(obj);\n      \n      while (xline) {\n        if (xml_tag_eq(xline, _s_dae_origin) && (sval = xmls(xline))) {\n          xml_strtof_fast(sval, line->origin, 3);\n        } else if (xml_tag_eq(xline, _s_dae_direction) && (sval = xmls(xline))) {\n          xml_strtof_fast(sval, line->direction, 3);\n        } else if (xml_tag_eq(xline, _s_dae_extra)) {\n          line->extra = tree_fromxml(heap, obj, xml);\n        }\n        \n        xline = xline->next;\n      }\n\n      curve->curve = obj;\n    } else if (xml_tag_eq(xml, _s_dae_circle)) {\n      AkCircle *circ;\n      xml_t    *xcirc;\n      \n      xcirc = xml->val;\n      obj     = ak_objAlloc(heap, curve, sizeof(*circ), AK_CURVE_CIRCLE, true);\n      circ  = ak_objGet(obj);\n      \n      while (xcirc) {\n        if (xml_tag_eq(xcirc, _s_dae_radius) && xcirc->val) {\n          circ->radius = xml_float(xcirc->val, 0.0f);\n        } else if (xml_tag_eq(xcirc, _s_dae_extra)) {\n          circ->extra = tree_fromxml(heap, obj, xml);\n        }\n        xcirc = xcirc->next;\n      }\n\n      curve->curve = obj;\n    } else if (xml_tag_eq(xml, _s_dae_ellipse)) {\n      AkEllipse *ell;\n      xml_t     *xell;\n\n      xell = xml->val;\n      obj  = ak_objAlloc(heap, curve, sizeof(*ell), AK_CURVE_ELLIPSE, true);\n      ell  = ak_objGet(obj);\n      \n      while (xell) {\n        if (xml_tag_eq(xell, _s_dae_radius) && (sval = xmls(xell))) {\n          xml_strtof_fast(sval, (AkFloat *)&ell->radius, 2);\n        } else if (xml_tag_eq(xell, _s_dae_extra)) {\n          ell->extra = tree_fromxml(heap, obj, xml);\n        }\n        xell = xell->next;\n      }\n\n      curve->curve = obj;\n    } else if (xml_tag_eq(xml, _s_dae_parabola)) {\n      AkParabola *par;\n      xml_t      *xpar;\n\n      xpar = xml->val;\n      obj  = ak_objAlloc(heap, curve, sizeof(*par), AK_CURVE_PARABOLA, true);\n      par  = ak_objGet(obj);\n\n      while (xpar) {\n        if (xml_tag_eq(xpar, _s_dae_focal) && xpar->val) {\n          par->focal = xml_float(xpar->val, 0.0f);\n        } else if (xml_tag_eq(xpar, _s_dae_extra)) {\n          par->extra = tree_fromxml(heap, obj, xml);\n        }\n        xpar = xpar->next;\n      }\n\n      curve->curve = obj;\n    } else if (xml_tag_eq(xml, _s_dae_hyperbola)) {\n      AkHyperbola *hpar;\n      xml_t       *xhpar;\n      \n      xhpar = xml->val;\n      obj   = ak_objAlloc(heap,\n                          curve,\n                          sizeof(*hpar),\n                          AK_CURVE_HYPERBOLA,\n                          true);\n      hpar  = ak_objGet(obj);\n      \n      while (xhpar) {\n        if (xml_tag_eq(xhpar, _s_dae_radius) && (sval = xmls(xhpar))) {\n          xml_strtof_fast(sval, (AkFloat *)&hpar->radius, 2);\n        } else if (xml_tag_eq(xhpar, _s_dae_extra)) {\n          hpar->extra = tree_fromxml(heap, obj, xml);\n        }\n        xhpar = xhpar->next;\n      }\n      \n      curve->curve = obj;\n    } else if (xml_tag_eq(xml, _s_dae_nurbs)) {\n      curve->curve = dae_nurbs(dst, xml, curve);\n    } else if (xml_tag_eq(xml, _s_dae_orient) && (sval = xmls(xml))) {\n      AkFloatArrayL *orient;\n      AkResult       ret;\n      \n      ret = xml_strtof_arrayL(heap, curve, sval, &orient);\n      if (ret == AK_OK) {\n        orient->next  = curve->orient;\n        curve->orient = orient;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_origin) && (sval = xmls(xml))) {\n      xml_strtof_fast(sval, curve->origin, 3);\n    }\n    xml = xml->next;\n  }\n\n  return curve;\n}\n\nAK_HIDE\nAkCurves*\ndae_curves(DAEState * __restrict dst,\n           xml_t    * __restrict xml,\n           void     * __restrict memp) {\n  AkHeap   *heap;\n  AkCurves *curves;\n  AkCurve  *curve;\n\n  heap   = dst->heap;\n  curves = ak_heap_calloc(heap, memp, sizeof(*curves));\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_curve)) {\n      if ((curve = dae_curve(dst, xml, curves))) {\n        curve->next   = curves->curve;\n        curves->curve = curve;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      curves->extra = tree_fromxml(heap, curves, xml);\n    }\n    xml = xml->next;\n  }\n  \n  return curves;\n}\n"
  },
  {
    "path": "src/io/dae/brep/curve.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_brep_curve_h\n#define dae_brep_curve_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkCurve*\ndae_curve(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp);\n\nAK_HIDE\nAkCurves*\ndae_curves(DAEState * __restrict dst,\n           xml_t    * __restrict xml,\n           void     * __restrict memp);\n\n#endif /* dae_brep_curve_h */\n"
  },
  {
    "path": "src/io/dae/brep/nurb.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"nurb.h\"\n#include \"../core/vert.h\"\n#include \"../core/source.h\"\n#include \"../core/enum.h\"\n\nAK_HIDE\nAkObject*\ndae_nurbs(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp) {\n  AkHeap   *heap;\n  AkObject *obj;\n  AkNurbs  *nurbs;\n  AkSource *source;\n\n  heap  = dst->heap;\n  obj   = ak_objAlloc(heap, memp, sizeof(*nurbs), 0, true);\n  nurbs = ak_objGet(obj);\n\n  nurbs->degree = xmla_u32(xmla(xml, _s_dae_degree), 0);\n  nurbs->closed = xmla_u32(xmla(xml, _s_dae_closed), 0);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_source)) {\n      if ((source = dae_source(dst, xml, NULL, 0))) {\n        source->next  = nurbs->source;\n        nurbs->source = source;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_control_vertices)) {\n      nurbs->cverts = dae_vert(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      nurbs->extra = tree_fromxml(heap, nurbs, xml);\n    }\n    xml = xml->next;\n  }\n  \n  return obj;\n}\n\nAK_HIDE\nAkObject*\ndae_nurbs_surface(DAEState * __restrict dst,\n                  xml_t    * __restrict xml,\n                  void     * __restrict memp) {\n  AkHeap         *heap;\n  AkObject       *obj;\n  AkNurbsSurface *nurbsSurface;\n  AkSource       *source;\n\n  heap  = dst->heap;\n  obj   = ak_objAlloc(heap, memp, sizeof(*nurbsSurface), 0, true);\n  nurbsSurface = ak_objGet(obj);\n\n  nurbsSurface->degree_u = xmla_u32(xmla(xml, _s_dae_degree_u), 0);\n  nurbsSurface->degree_v = xmla_u32(xmla(xml, _s_dae_degree_v), 0);\n  nurbsSurface->closed_u = xmla_u32(xmla(xml, _s_dae_closed_u), 0);\n  nurbsSurface->closed_v = xmla_u32(xmla(xml, _s_dae_closed_v), 0);\n  \n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_source)) {\n      if ((source = dae_source(dst, xml, NULL, 0))) {\n        source->next         = nurbsSurface->source;\n        nurbsSurface->source = source;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_control_vertices)) {\n      nurbsSurface->cverts = dae_vert(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      nurbsSurface->extra = tree_fromxml(heap, nurbsSurface, xml);\n    }\n    xml = xml->next;\n  }\n  \n  return obj;\n}\n"
  },
  {
    "path": "src/io/dae/brep/nurb.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_brep_nurbs_h\n#define dae_brep_nurbs_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkObject*\ndae_nurbs(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp);\n\nAK_HIDE\nAkObject*\ndae_nurbs_surface(DAEState * __restrict dst,\n                  xml_t    * __restrict xml,\n                  void     * __restrict memp);\n\n#endif /* dae_brep_nurbs_h */\n"
  },
  {
    "path": "src/io/dae/brep/surface.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"surface.h\"\n#include \"nurb.h\"\n#include \"curve.h\"\n#include \"../../../array.h\"\n\nAK_HIDE\nAkSurface*\ndae_surface(DAEState * __restrict dst,\n            xml_t    * __restrict xml,\n            void     * __restrict memp) {\n  AkHeap      *heap;\n  AkSurface   *surf;\n  AkObject    *obj;\n  const xml_t *sval;\n\n  heap = dst->heap;\n  surf = ak_heap_calloc(heap, memp, sizeof(*surf));\n\n  sid_set(xml, heap, surf);\n\n  surf->name = xmla_strdup_by(xml, heap, _s_dae_name, surf);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_cone)) {\n      AkCone *cone;\n      xml_t  *xcone;\n      \n      obj  = ak_objAlloc(heap, surf, sizeof(*cone), AK_SURFACE_CONE, true);\n      cone = ak_objGet(obj);\n      \n      xcone = xml->val;\n      while (xcone) {\n        if (xml_tag_eq(xcone, _s_dae_radius) && xcone->val) {\n          cone->radius = xml_float(xcone->val, 0.0f);\n        } else if (xml_tag_eq(xcone, _s_dae_angle) && xcone->val) {\n          cone->angle = xml_float(xcone->val, 0.0f);\n        } else if (xml_tag_eq(xcone, _s_dae_extra)) {\n          cone->extra = tree_fromxml(heap, obj, xcone);\n        }\n        xcone = xcone->next;\n      }\n\n      surf->surface = obj;\n    } else if (xml_tag_eq(xml, _s_dae_plane)) {\n      AkPlane *plane;\n      xml_t   *xplane;\n      \n      obj   = ak_objAlloc(heap, surf, sizeof(*plane), AK_SURFACE_PLANE, true);\n      plane = ak_objGet(obj);\n      \n      xplane = xml->val;\n      while (xplane) {\n        if (xml_tag_eq(xplane, _s_dae_equation) && (sval = xmls(xplane))) {\n          xml_strtof_fast(sval, (AkFloat *)&plane->equation, 4);\n        } else if (xml_tag_eq(xplane, _s_dae_extra)) {\n          plane->extra = tree_fromxml(heap, obj, xplane);\n        }\n        xplane = xplane->next;\n      }\n      \n      surf->surface = obj;\n    } else if (xml_tag_eq(xml, _s_dae_cylinder)) {\n      AkCylinder *clyn;\n      xml_t      *xclyn;\n      \n      obj  = ak_objAlloc(heap, surf, sizeof(*clyn), AK_SURFACE_CYLINDER, true);\n      clyn = ak_objGet(obj);\n      \n      xclyn = xml->val;\n      while (xclyn) {\n        if (xml_tag_eq(xclyn, _s_dae_radius) && (sval = xmls(xclyn))) {\n          xml_strtof_fast(sval, (AkFloat *)&clyn->radius, 2);\n        } else if (xml_tag_eq(xclyn, _s_dae_extra)) {\n          clyn->extra = tree_fromxml(heap, obj, xclyn);\n        }\n        xclyn = xclyn->next;\n      }\n      \n      surf->surface = obj;\n    } else if (xml_tag_eq(xml, _s_dae_nurbs_surface)) {\n      surf->surface = dae_nurbs_surface(dst, xml, surf);\n    } else if (xml_tag_eq(xml, _s_dae_sphere)) {\n      AkSphere *sphere;\n      xml_t    *xsphere;\n      \n      obj = ak_objAlloc(heap, surf, sizeof(*sphere), AK_SURFACE_SPHERE, true);\n      sphere = ak_objGet(obj);\n      \n      xsphere = xml->val;\n      while (xsphere) {\n        if (xml_tag_eq(xsphere, _s_dae_radius) && xsphere->val) {\n          sphere->radius = xml_float(xsphere->val, 0.0f);\n        } else if (xml_tag_eq(xsphere, _s_dae_extra)) {\n          sphere->extra = tree_fromxml(heap, obj, xsphere);\n        }\n        xsphere = xsphere->next;\n      }\n\n      surf->surface = obj;\n    } else if (xml_tag_eq(xml, _s_dae_torus)) {\n      AkTorus *torus;\n      xml_t   *xtorus;\n\n      obj   = ak_objAlloc(heap, surf, sizeof(*xtorus), AK_SURFACE_TORUS, true);\n      torus = ak_objGet(obj);\n\n      xtorus = xml->val;\n      while (xtorus) {\n        if (xml_tag_eq(xtorus, _s_dae_radius) && (sval = xmls(xtorus))) {\n          xml_strtof_fast(sval, (AkFloat *)&torus->radius, 2);\n        } else if (xml_tag_eq(xtorus, _s_dae_extra)) {\n          torus->extra = tree_fromxml(heap, obj, xtorus);\n        }\n        xtorus = xtorus->next;\n      }\n\n      surf->surface = obj;\n    } else if (xml_tag_eq(xml, _s_dae_swept_surface)) {\n      AkObject       *obj;\n      AkSweptSurface *sweptSurface;\n      xml_t          *xswept;\n      \n      obj = ak_objAlloc(heap,\n                        surf,\n                        sizeof(*sweptSurface),\n                        AK_SURFACE_SWEPT_SURFACE,\n                        true);\n      \n      sweptSurface = ak_objGet(obj);\n      \n      xswept = xml->val;\n      while (xswept) {\n        if (xml_tag_eq(xswept, _s_dae_curve)) {\n          sweptSurface->curve = dae_curve(dst, xswept, obj);\n        } else if (xml_tag_eq(xswept, _s_dae_direction) && (sval = xmls(xml))) {\n          xml_strtof_fast(sval, (AkFloat *)&sweptSurface->direction, 3);\n        } else if (xml_tag_eq(xswept, _s_dae_origin) && (sval = xmls(xml))) {\n          xml_strtof_fast(sval, (AkFloat *)&sweptSurface->origin, 3);\n        } else if (xml_tag_eq(xswept, _s_dae_axis) && (sval = xmls(xml))) {\n          xml_strtof_fast(sval, (AkFloat *)&sweptSurface->axis, 3);\n        } else if (xml_tag_eq(xswept, _s_dae_extra)) {\n           sweptSurface->extra = tree_fromxml(heap, obj, xswept);\n        }\n        xswept = xswept->next;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_orient) && (sval =xmls(xml))) {\n      AkFloatArrayL *orient;\n      AkResult       ret;\n      \n      ret = xml_strtof_arrayL(heap, surf, sval, &orient);\n      if (ret == AK_OK) {\n        orient->next = surf->orient;\n        surf->orient = orient;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_origin) && (sval = xmls(xml))) {\n      xml_strtof_fast(sval, surf->origin, 3);\n    }\n    xml = xml->next;\n  }\n\n  return surf;\n}\n\nAK_HIDE\nAkSurfaces*\ndae_surfaces(DAEState * __restrict dst,\n             xml_t    * __restrict xml,\n             void     * __restrict memp) {\n  AkHeap     *heap;\n  AkSurfaces *surfaces;\n  AkSurface  *surface;\n\n  heap     = dst->heap;\n  surfaces = ak_heap_calloc(heap, memp, sizeof(*surfaces));\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_surface)) {\n      if ((surface = dae_surface(dst, xml, memp))) {\n        surface->next     = surfaces->surface;\n        surfaces->surface = surface;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      surfaces->extra = tree_fromxml(heap, surfaces, xml);\n    }\n    xml = xml->next;\n  }\n\n  return surfaces;\n}\n"
  },
  {
    "path": "src/io/dae/brep/surface.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_brep_surface_h\n#define dae_brep_surface_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkSurface*\ndae_surface(DAEState * __restrict dst,\n            xml_t    * __restrict xml,\n            void     * __restrict memp);\n\nAK_HIDE\nAkSurfaces*\ndae_surfaces(DAEState * __restrict dst,\n             xml_t    * __restrict xml,\n             void     * __restrict memp);\n\n#endif /* dae_brep_surface_h */\n"
  },
  {
    "path": "src/io/dae/brep/topology.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"topology.h\"\n#include \"../core/enum.h\"\n#include \"../../../array.h\"\n\nAK_HIDE AkEdges*\ndae_edges(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp) {\n  AkHeap      *heap;\n  AkEdges     *edges;\n  const xml_t *sval;\n\n  heap  = dst->heap;\n  edges = ak_heap_calloc(heap, memp, sizeof(*edges));\n\n  xmla_setid(xml, heap, edges);\n\n  edges->name  = xmla_strdup_by(xml, heap, _s_dae_name, edges);\n  edges->count = xmla_u32(xmla(xml, _s_dae_count), 0);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_input)) {\n      AkInput *inp;\n      \n      inp              = ak_heap_calloc(heap, edges, sizeof(*inp));\n      inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp);\n      \n      if (!inp->semanticRaw) {\n        ak_free(inp);\n      } else {\n        AkEnum inputSemantic;\n        \n        inputSemantic = dae_semantic(inp->semanticRaw);\n        \n        if (inputSemantic < 0)\n          inputSemantic = AK_INPUT_OTHER;\n        \n        inp->semantic = inputSemantic;\n        inp->offset   = xmla_u32(xmla(xml, _s_dae_offset), 0);\n        inp->set      = xmla_u32(xmla(xml, _s_dae_set),    0);\n        \n        if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) {\n          AkURL *url;\n          \n          inp->semantic = dae_semantic(inp->semanticRaw);\n          \n          inp->next     = edges->input;\n          edges->input  = inp;\n          edges->inputCount++;\n    \n          url = url_from(xml, _s_dae_source, edges);\n          rb_insert(dst->inputmap, inp, url);\n        } else {\n          ak_free(inp);\n        }\n      }\n    } else if (xml_tag_eq(xml, _s_dae_p) && (sval = xmls(xml))) {\n      AkUIntArray *prims;\n      AkResult     ret;\n      \n      ret = xml_strtoui_array(heap, edges, sval, &prims);\n      if (ret == AK_OK)\n        edges->primitives = prims;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      edges->extra = tree_fromxml(heap, edges, xml);\n    }\n    xml = xml->next;\n  }\n\n  return edges;\n}\n\nAK_HIDE AkWires*\ndae_wires(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp) {\n  AkHeap      *heap;\n  AkWires     *wires;\n  const xml_t *sval;\n\n  heap  = dst->heap;\n  wires = ak_heap_calloc(heap, memp, sizeof(*wires));\n\n  xmla_setid(xml, heap, wires);\n\n  wires->name  = xmla_strdup_by(xml, heap, _s_dae_name, wires);\n  wires->count = xmla_u32(xmla(xml, _s_dae_count), 0);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_input)) {\n      AkInput *inp;\n      \n      inp              = ak_heap_calloc(heap, wires, sizeof(*inp));\n      inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp);\n      \n      if (!inp->semanticRaw) {\n        ak_free(inp);\n      } else {\n        AkEnum inputSemantic;\n        \n        inputSemantic = dae_semantic(inp->semanticRaw);\n        \n        if (inputSemantic < 0)\n          inputSemantic = AK_INPUT_OTHER;\n        \n        inp->semantic = inputSemantic;\n        inp->offset   = xmla_u32(xmla(xml, _s_dae_offset), 0);\n        inp->set      = xmla_u32(xmla(xml, _s_dae_set),    0);\n        \n        if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) {\n          AkURL *url;\n          \n          inp->semantic = dae_semantic(inp->semanticRaw);\n          \n          inp->next     = wires->input;\n          wires->input  = inp;\n          wires->inputCount++;\n    \n          url = url_from(xml, _s_dae_source, wires);\n          rb_insert(dst->inputmap, inp, url);\n        } else {\n          ak_free(inp);\n        }\n      }\n    } else if (xml_tag_eq(xml, _s_dae_vcount) && (sval = xmls(xml))) {\n      AkUIntArray *vcount;\n      AkResult     ret;\n      \n      ret = xml_strtoui_array(heap, wires, sval, &vcount);\n      if (ret == AK_OK)\n        wires->vcount = vcount;\n    } else if (xml_tag_eq(xml, _s_dae_p) && (sval = xmls(xml))) {\n      AkUIntArray *prims;\n      AkResult     ret;\n      \n      ret = xml_strtoui_array(heap, wires, sval, &prims);\n      if (ret == AK_OK)\n        wires->primitives = prims;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      wires->extra = tree_fromxml(heap, wires, xml);\n    }\n    xml = xml->next;\n  }\n\n  return wires;\n}\n\nAK_HIDE AkFaces*\ndae_faces(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp) {\n  AkHeap      *heap;\n  AkFaces     *faces;\n  const xml_t *sval;\n\n  heap  = dst->heap;\n  faces = ak_heap_calloc(heap, memp, sizeof(*faces));\n\n  xmla_setid(xml, heap, faces);\n\n  faces->name  = xmla_strdup_by(xml, heap, _s_dae_name, faces);\n  faces->count = xmla_u32(xmla(xml, _s_dae_count), 0);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_input)) {\n      AkInput *inp;\n      \n      inp              = ak_heap_calloc(heap, faces, sizeof(*inp));\n      inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp);\n      \n      if (!inp->semanticRaw) {\n        ak_free(inp);\n      } else {\n        AkEnum inputSemantic;\n        \n        inputSemantic = dae_semantic(inp->semanticRaw);\n        \n        if (inputSemantic < 0)\n          inputSemantic = AK_INPUT_OTHER;\n        \n        inp->semantic = inputSemantic;\n        inp->offset   = xmla_u32(xmla(xml, _s_dae_offset), 0);\n        inp->set      = xmla_u32(xmla(xml, _s_dae_set),    0);\n        \n        if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) {\n          AkURL *url;\n          \n          inp->semantic = dae_semantic(inp->semanticRaw);\n          \n          inp->next     = faces->input;\n          faces->input  = inp;\n          faces->inputCount++;\n    \n          url = url_from(xml, _s_dae_source, faces);\n          rb_insert(dst->inputmap, inp, url);\n        } else {\n          ak_free(inp);\n        }\n      }\n    } else if (xml_tag_eq(xml, _s_dae_vcount) && (sval = xmls(xml))) {\n      AkUIntArray *vcount;\n      AkResult     ret;\n      \n      ret = xml_strtoui_array(heap, faces, sval, &vcount);\n      if (ret == AK_OK)\n        faces->vcount = vcount;\n    } else if (xml_tag_eq(xml, _s_dae_p) && (sval = xmls(xml))) {\n      AkUIntArray *prims;\n      AkResult     ret;\n      \n      ret = xml_strtoui_array(heap, faces, sval, &prims);\n      if (ret == AK_OK)\n        faces->primitives = prims;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      faces->extra = tree_fromxml(heap, faces, xml);\n    }\n    xml = xml->next;\n  }\n\n  return faces;\n}\n\nAK_HIDE AkPCurves*\ndae_pcurves(DAEState * __restrict dst,\n            xml_t    * __restrict xml,\n            void     * __restrict memp) {\n  AkHeap      *heap;\n  AkPCurves   *pcurves;\n  const xml_t *sval;\n\n  heap  = dst->heap;\n  pcurves = ak_heap_calloc(heap, memp, sizeof(*pcurves));\n\n  xmla_setid(xml, heap, pcurves);\n\n  pcurves->name  = xmla_strdup_by(xml, heap, _s_dae_name, pcurves);\n  pcurves->count = xmla_u32(xmla(xml, _s_dae_count), 0);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_input)) {\n      AkInput *inp;\n      \n      inp              = ak_heap_calloc(heap, pcurves, sizeof(*inp));\n      inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp);\n      \n      if (!inp->semanticRaw) {\n        ak_free(inp);\n      } else {\n        AkEnum inputSemantic;\n        \n        inputSemantic = dae_semantic(inp->semanticRaw);\n        \n        if (inputSemantic < 0)\n          inputSemantic = AK_INPUT_OTHER;\n        \n        inp->semantic = inputSemantic;\n        inp->offset   = xmla_u32(xmla(xml, _s_dae_offset), 0);\n        inp->set      = xmla_u32(xmla(xml, _s_dae_set),    0);\n        \n        if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) {\n          AkURL *url;\n          \n          inp->semantic = dae_semantic(inp->semanticRaw);\n          \n          inp->next     = pcurves->input;\n          pcurves->input  = inp;\n          pcurves->inputCount++;\n    \n          url = url_from(xml, _s_dae_source, pcurves);\n          rb_insert(dst->inputmap, inp, url);\n        } else {\n          ak_free(inp);\n        }\n      }\n    } else if (xml_tag_eq(xml, _s_dae_vcount) && (sval = xmls(xml))) {\n      AkUIntArray *vcount;\n      AkResult     ret;\n      \n      ret = xml_strtoui_array(heap, pcurves, sval, &vcount);\n      if (ret == AK_OK)\n        pcurves->vcount = vcount;\n    } else if (xml_tag_eq(xml, _s_dae_p) && (sval = xmls(xml))) {\n      AkUIntArray *prims;\n      AkResult     ret;\n      \n      ret = xml_strtoui_array(heap, pcurves, sval, &prims);\n      if (ret == AK_OK)\n        pcurves->primitives = prims;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      pcurves->extra = tree_fromxml(heap, pcurves, xml);\n    }\n    xml = xml->next;\n  }\n\n  return pcurves;\n}\n\nAK_HIDE AkShells*\ndae_shells(DAEState * __restrict dst,\n           xml_t    * __restrict xml,\n           void     * __restrict memp) {\n  AkHeap      *heap;\n  AkShells    *shells;\n  const xml_t *sval;\n\n  heap  = dst->heap;\n  shells = ak_heap_calloc(heap, memp, sizeof(*shells));\n\n  xmla_setid(xml, heap, shells);\n\n  shells->name  = xmla_strdup_by(xml, heap, _s_dae_name, shells);\n  shells->count = xmla_u32(xmla(xml, _s_dae_count), 0);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_input)) {\n      AkInput *inp;\n      \n      inp              = ak_heap_calloc(heap, shells, sizeof(*inp));\n      inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp);\n      \n      if (!inp->semanticRaw) {\n        ak_free(inp);\n      } else {\n        AkEnum inputSemantic;\n        \n        inputSemantic = dae_semantic(inp->semanticRaw);\n        \n        if (inputSemantic < 0)\n          inputSemantic = AK_INPUT_OTHER;\n        \n        inp->semantic = inputSemantic;\n        inp->offset   = xmla_u32(xmla(xml, _s_dae_offset), 0);\n        inp->set      = xmla_u32(xmla(xml, _s_dae_set),    0);\n        \n        if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) {\n          AkURL *url;\n          \n          inp->semantic = dae_semantic(inp->semanticRaw);\n          \n          inp->next     = shells->input;\n          shells->input  = inp;\n          shells->inputCount++;\n    \n          url = url_from(xml, _s_dae_source, shells);\n          rb_insert(dst->inputmap, inp, url);\n        } else {\n          ak_free(inp);\n        }\n      }\n    } else if (xml_tag_eq(xml, _s_dae_vcount) && (sval = xmls(xml))) {\n      AkUIntArray *vcount;\n      AkResult     ret;\n      \n      ret = xml_strtoui_array(heap, shells, sval, &vcount);\n      if (ret == AK_OK)\n        shells->vcount = vcount;\n    } else if (xml_tag_eq(xml, _s_dae_p) && (sval = xmls(xml))) {\n      AkUIntArray *prims;\n      AkResult     ret;\n      \n      ret = xml_strtoui_array(heap, shells, sval, &prims);\n      if (ret == AK_OK)\n        shells->primitives = prims;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      shells->extra = tree_fromxml(heap, shells, xml);\n    }\n    xml = xml->next;\n  }\n\n  return shells;\n}\n\nAK_HIDE AkSolids*\ndae_solids(DAEState * __restrict dst,\n           xml_t    * __restrict xml,\n           void     * __restrict memp){\n  AkHeap      *heap;\n  AkSolids    *solids;\n  const xml_t *sval;\n\n  heap  = dst->heap;\n  solids = ak_heap_calloc(heap, memp, sizeof(*solids));\n\n  xmla_setid(xml, heap, solids);\n\n  solids->name  = xmla_strdup_by(xml, heap, _s_dae_name, solids);\n  solids->count = xmla_u32(xmla(xml, _s_dae_count), 0);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_input)) {\n      AkInput *inp;\n      \n      inp              = ak_heap_calloc(heap, solids, sizeof(*inp));\n      inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp);\n      \n      if (!inp->semanticRaw) {\n        ak_free(inp);\n      } else {\n        AkEnum inputSemantic;\n        \n        inputSemantic = dae_semantic(inp->semanticRaw);\n        \n        if (inputSemantic < 0)\n          inputSemantic = AK_INPUT_OTHER;\n        \n        inp->semantic = inputSemantic;\n        inp->offset   = xmla_u32(xmla(xml, _s_dae_offset), 0);\n        inp->set      = xmla_u32(xmla(xml, _s_dae_set),    0);\n        \n        if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) {\n          AkURL *url;\n          \n          inp->semantic = dae_semantic(inp->semanticRaw);\n          \n          inp->next     = solids->input;\n          solids->input  = inp;\n          solids->inputCount++;\n    \n          url = url_from(xml, _s_dae_source, solids);\n          rb_insert(dst->inputmap, inp, url);\n        } else {\n          ak_free(inp);\n        }\n      }\n    } else if (xml_tag_eq(xml, _s_dae_vcount) && (sval = xmls(xml))) {\n      AkUIntArray *vcount;\n      AkResult     ret;\n      \n      ret = xml_strtoui_array(heap, solids, sval, &vcount);\n      if (ret == AK_OK)\n        solids->vcount = vcount;\n    } else if (xml_tag_eq(xml, _s_dae_p) && (sval = xmls(xml))) {\n      AkUIntArray *prims;\n      AkResult     ret;\n      \n      ret = xml_strtoui_array(heap, solids, sval, &prims);\n      if (ret == AK_OK)\n        solids->primitives = prims;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      solids->extra = tree_fromxml(heap, solids, xml);\n    }\n    xml = xml->next;\n  }\n\n  return solids;\n}\n"
  },
  {
    "path": "src/io/dae/brep/topology.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_brep_topology_h\n#define dae_brep_topology_h\n\n#include \"../common.h\"\n\nAK_HIDE AkEdges*\ndae_edges(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp);\n\nAK_HIDE AkWires*\ndae_wires(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp);\n\nAK_HIDE AkFaces*\ndae_faces(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp);\n\nAK_HIDE AkPCurves*\ndae_pcurves(DAEState * __restrict dst,\n            xml_t    * __restrict xml,\n            void     * __restrict memp);\n\nAK_HIDE AkShells*\ndae_shells(DAEState * __restrict dst,\n           xml_t    * __restrict xml,\n           void     * __restrict memp);\n\nAK_HIDE AkSolids*\ndae_solids(DAEState * __restrict dst,\n           xml_t    * __restrict xml,\n           void     * __restrict memp);\n\n#endif /* dae_brep_topology_h */\n"
  },
  {
    "path": "src/io/dae/bugfix/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/dae/bugfix/scenekit.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"scenekit.h\"\n\n#include <ctype.h>\n#include <string.h>\n\nstatic\nbool\ndae_strcase_contains(const char * __restrict str,\n                     const char * __restrict needle) {\n  const char *s, *n, *match;\n\n  if (!str || !needle || !needle[0])\n    return false;\n\n  for (; *str; str++) {\n    s     = str;\n    n     = needle;\n    match = str;\n\n    while (*s && *n\n           && tolower((unsigned char)*s) == tolower((unsigned char)*n)) {\n      s++;\n      n++;\n    }\n\n    if (!*n)\n      return true;\n\n    str = match;\n  }\n\n  return false;\n}\n\nstatic\nbool\ndae_scenekit_authored(AkDoc * __restrict doc) {\n  AkContributor *contr;\n\n  if (!doc || !doc->inf)\n    return false;\n\n  for (contr = doc->inf->base.contributor; contr; contr = contr->next) {\n    if (dae_strcase_contains(contr->authoringTool, \"scenekit\"))\n      return true;\n  }\n\n  return false;\n}\n\nstatic\nbool\ndae_colordesc_has_texture(DAEState    * __restrict dst,\n                          AkColorDesc * __restrict color) {\n  if (!color)\n    return false;\n\n  return color->texture || (dst->texmap && rb_find(dst->texmap, color));\n}\n\nstatic\nbool\ndae_scenekit_is_red_fill(AkMaterial          * __restrict material,\n                         AkTechniqueFxCommon * __restrict common) {\n  AkColor *color;\n\n  if (!material\n      || !material->name\n      || strcmp(material->name, _s_dae_material) != 0\n      || !common\n      || !common->diffuse\n      || !common->diffuse->color)\n    return false;\n\n  color = common->diffuse->color;\n\n  return color->rgba.R > 0.45f\n         && color->rgba.R < 0.90f\n         && color->rgba.G < 0.40f\n         && color->rgba.B < 0.50f\n         && color->rgba.R > color->rgba.G + 0.20f\n         && color->rgba.R > color->rgba.B + 0.15f\n         && color->rgba.A > 0.95f;\n}\n\nstatic\nAkTechniqueFxCommon*\ndae_scenekit_primitive_common(DAEState            * __restrict dst,\n                              AkInstanceGeometry  * __restrict instGeom,\n                              AkMeshPrimitive     * __restrict prim,\n                              AkInstanceMaterial ** __restrict instMat) {\n  AkEffect *effect;\n\n  AK__UNUSED(dst);\n\n  *instMat = NULL;\n\n  if (!instGeom->bindMaterial)\n    return NULL;\n\n  effect = ak_effectForBindMaterial(instGeom->bindMaterial, prim, instMat);\n  if (!effect)\n    return NULL;\n\n  return ak_getProfileTechniqueCommon(effect);\n}\n\nstatic\nbool\ndae_scenekit_primitive_has_texture(DAEState           * __restrict dst,\n                                   AkInstanceGeometry * __restrict instGeom,\n                                   AkMeshPrimitive    * __restrict prim) {\n  AkTechniqueFxCommon *common;\n  AkInstanceMaterial  *instMat;\n\n  common = dae_scenekit_primitive_common(dst, instGeom, prim, &instMat);\n\n  return common && dae_colordesc_has_texture(dst, common->diffuse);\n}\n\nstatic\nbool\ndae_scenekit_is_textured_twin(DAEState           * __restrict dst,\n                              AkInstanceGeometry * __restrict instGeom,\n                              AkMeshPrimitive    * __restrict prim,\n                              AkMeshPrimitive    * __restrict other) {\n  return other\n         && other != prim\n         && other->type == AK_PRIMITIVE_TRIANGLES\n         && other->nPolygons == prim->nPolygons\n         && dae_scenekit_primitive_has_texture(dst, instGeom, other);\n}\n\nstatic\nbool\ndae_scenekit_should_drop_primitive(DAEState           * __restrict dst,\n                                   AkInstanceGeometry * __restrict instGeom,\n                                   AkMeshPrimitive    * __restrict prim,\n                                   AkMeshPrimitive    * __restrict prev,\n                                   AkMeshPrimitive    * __restrict next) {\n  AkTechniqueFxCommon *common;\n  AkInstanceMaterial  *instMat;\n  AkMaterial          *material;\n\n  if (prim->type != AK_PRIMITIVE_TRIANGLES || prim->nPolygons == 0)\n    return false;\n\n  common = dae_scenekit_primitive_common(dst, instGeom, prim, &instMat);\n  if (!common || dae_colordesc_has_texture(dst, common->diffuse))\n    return false;\n\n  material = instMat ? ak_instanceObject(&instMat->base) : NULL;\n\n  return dae_scenekit_is_red_fill(material, common)\n         && (dae_scenekit_is_textured_twin(dst, instGeom, prim, prev)\n             || dae_scenekit_is_textured_twin(dst, instGeom, prim, next));\n}\n\nstatic\nvoid\ndae_scenekit_unmap_material(AkGeometry      * __restrict geom,\n                            AkMeshPrimitive * __restrict prim) {\n  AkMapItem *head, *item;\n\n  if (!geom || !geom->materialMap || !prim->bindmaterial)\n    return;\n\n  head = ak_map_findm(geom->materialMap, (void *)prim->bindmaterial);\n  if (!head)\n    return;\n\n  item = head->data;\n  while (item) {\n    if (item->data == prim) {\n      if (item->prev)\n        item->prev->next = item->next;\n      else\n        head->data = item->next;\n\n      if (item->next)\n        item->next->prev = item->prev;\n\n      return;\n    }\n\n    item = item->next;\n  }\n}\n\nstatic\nvoid\ndae_scenekit_fix_mesh(DAEState           * __restrict dst,\n                      AkInstanceGeometry * __restrict instGeom,\n                      AkGeometry         * __restrict geom,\n                      AkMesh             * __restrict mesh) {\n  AkMeshPrimitive *prim, *prev, *next;\n\n  prev = NULL;\n  prim = mesh->primitive;\n\n  while (prim) {\n    next = prim->next;\n\n    if (dae_scenekit_should_drop_primitive(dst, instGeom, prim, prev, next)) {\n      if (prev)\n        prev->next = next;\n      else\n        mesh->primitive = next;\n\n      dae_scenekit_unmap_material(geom, prim);\n\n      prim->next = NULL;\n      if (mesh->primitiveCount > 0)\n        mesh->primitiveCount--;\n\n    } else {\n      prev = prim;\n    }\n\n    prim = next;\n  }\n}\n\nstatic\nvoid\ndae_scenekit_fix_node(DAEState * __restrict dst, AkNode * __restrict node) {\n  AkInstanceGeometry *instGeom;\n  AkGeometry         *geom;\n  AkObject           *geomData;\n\n  for (; node; node = node->next) {\n    for (instGeom = node->geometry; instGeom;\n         instGeom = (AkInstanceGeometry *)instGeom->base.next) {\n      geom = ak_instanceObject(&instGeom->base);\n      if (!geom || ak_typeid(geom) != AKT_GEOMETRY)\n        continue;\n\n      geomData = geom->gdata;\n      if (!geomData || (AkGeometryType)geomData->type != AK_GEOMETRY_MESH)\n        continue;\n\n      dae_scenekit_fix_mesh(dst, instGeom, geom, ak_objGet(geomData));\n    }\n\n    if (node->chld)\n      dae_scenekit_fix_node(dst, node->chld);\n  }\n}\n\nAK_HIDE\nvoid\ndae_bugfix_scenekit_backfaces(DAEState * __restrict dst) {\n  AkVisualScene *vscn;\n\n  if (!dst\n      || !dst->doc\n      || !ak_opt_get(AK_OPT_BUGFIXES)\n      || !dae_scenekit_authored(dst->doc)\n      || !dst->doc->lib.visualScenes)\n    return;\n\n  for (vscn = (void *)dst->doc->lib.visualScenes->chld;\n       vscn;\n       vscn = (void *)vscn->base.next) {\n    dae_scenekit_fix_node(dst, vscn->node);\n  }\n}\n"
  },
  {
    "path": "src/io/dae/bugfix/scenekit.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_bugfix_scenekit_h\n#define dae_bugfix_scenekit_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ndae_bugfix_scenekit_backfaces(DAEState * __restrict dst);\n\n#endif /* dae_bugfix_scenekit_h */\n"
  },
  {
    "path": "src/io/dae/bugfix/transp.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"transp.h\"\n#include <string.h>\n#include <ctype.h>\n\nAK_HIDE\nvoid\ndae_bugfix_transp(AkTransparent * __restrict transp) {\n  AkContributor *contr;\n  char          *tool;\n\n  if (!(contr = ak_getAssetInfo(transp, offsetof(AkAssetInf, contributor)))\n      || !(tool = (char *)contr->authoringTool))\n    return;\n\n  tool = ak_tolower(strdup(tool));\n\n  /* fix old SketchUp transparency bug */\n  if (strstr(tool, _s_dae_sketchup)) {\n    int major, minor, patch;\n    if(sscanf(tool, \"%*[^0123456789]%d%*[. ]%d%*[. ]%d\",\n              &major, &minor, &patch)) {\n\n      /* don't flip >= 7.1.1 */\n      if (major <= 7 && minor < 2 && patch < 1) {\n        transp->amount = 1.0f - transp->amount;\n      }\n    }\n  } /* _s_dae_sketchup */\n  \n  free(tool);\n}\n"
  },
  {
    "path": "src/io/dae/bugfix/transp.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_bugfix_transp_h\n#define dae_bugfix_transp_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ndae_bugfix_transp(AkTransparent * __restrict transp);\n\n#endif /* dae_bugfix_transp_h */\n"
  },
  {
    "path": "src/io/dae/bugfix/url.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_bugfix_url_h\n#define dae_bugfix_url_h\n\n#include <string.h>\n#ifdef _MSC_VER\n#  include <malloc.h>\n#  define dae_alloca _alloca\n#else\n#  include <alloca.h>\n#  define dae_alloca alloca\n#endif\n\n/*\n * Industry bug: many DAE exporters (Blender, Maya, some glTF→DAE\n * converters) emit `<... source=\"some_id\"/>` instead of the spec-correct\n * `<... source=\"#some_id\"/>` for internal references. Without the\n * fragment marker AssetKit's url machinery routes these strings through\n * the external-resource fetch path, which never resolves and (worse)\n * NULL-derefs in ak_path_fragment.\n *\n * Policy: AssetKit is lenient toward asset-side mistakes that have an\n * unambiguous fix. When the URL has no '#' AND no path/URL separator\n * (`/`, `\\`, scheme `://`), treat it as a bare internal id and prepend\n * the missing '#'.\n *\n * Implementation: macro instead of an inline function so alloca() lives\n * in the caller's stack frame — the normalized buffer must outlive\n * ak_url_init, which strdups the input internally (its '#' branch). No\n * extra heap allocation for the common malformed case; well-formed\n * input is a no-op.\n */\n#define DAE_URL_INIT_FIXED(memp_, urlstring_, url_)                      \\\n  do {                                                                   \\\n    char *_dbu_str = (urlstring_);                                       \\\n    if (_dbu_str && _dbu_str[0] != '#'                                   \\\n        && !strchr(_dbu_str, '/')                                        \\\n        && !strchr(_dbu_str, '\\\\')                                       \\\n        && !strstr(_dbu_str, \"://\")) {                                   \\\n      size_t  _dbu_len = strlen(_dbu_str);                               \\\n      char   *_dbu_norm = dae_alloca(_dbu_len + 2);                      \\\n      _dbu_norm[0] = '#';                                                \\\n      memcpy(_dbu_norm + 1, _dbu_str, _dbu_len + 1);                     \\\n      ak_free(_dbu_str);                                                 \\\n      _dbu_str = _dbu_norm;                                              \\\n    }                                                                    \\\n    ak_url_init((memp_), _dbu_str, (url_));                              \\\n  } while (0)\n\n#endif /* dae_bugfix_url_h */\n"
  },
  {
    "path": "src/io/dae/common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_common_h\n#define dae_common_h\n\n#include \"../../../include/ak/assetkit.h\"\n#include \"../../../include/ak/url.h\"\n#include \"../../common.h\"\n#include \"../../utils.h\"\n#include \"../../tree.h\"\n#include \"../../xml.h\"\n#include \"strpool.h\"\n\n#include <ds/forward-list-sep.h>\n#include <string.h>\n#include <xml/xml.h>\n#include <xml/attrib.h>\n\n#include \"bugfix/url.h\"\n\n#ifndef AK_INPUT_SEMANTIC_VERTEX \n#  define AK_INPUT_SEMANTIC_VERTEX 100001\n#endif\n\ntypedef enum AkCOLLADAVersion {\n  AK_COLLADA_VERSION_150 = 150,\n  AK_COLLADA_VERSION_141 = 141,\n  AK_COLLADA_VERSION_140 = 140\n} AkCOLLADAVersion;\n\ntypedef enum AkControllerType {\n  AK_CONTROLLER_MORPH = 1,\n  AK_CONTROLLER_SKIN  = 2\n} AkControllerType;\n\ntypedef struct AkURLQueue {\n  AkURL *url;\n  struct AkURLQueue *next;\n} AkURLQueue;\n\ntypedef struct AkDAEVerticesMapItem {\n  AkInput         *inp;\n  AkMeshPrimitive *prim;\n} AkDAEVerticesMapItem;\n\ntypedef AK_ALIGN(16) struct DAEState {\n  AkHeap          *heap;\n  void            *tempmem;\n  void            *jobs14;\n  AkDoc           *doc;\n  AkURLQueue      *urlQueue;\n  FListItem       *accessors;\n  FListItem       *instCtlrs;\n  FListItem       *inputs;\n  FListItem       *toRadiansSampelers;\n  FListItem       *linkedUserData;\n  RBTree          *meshInfo;\n  RBTree          *inputmap;\n  RBTree          *texmap;\n  RBTree          *instanceMap;\n  FListItem       *vertMap;\n  /* maps base AkGeometry* → AkMorph*. Populated by dae_fixup_ctlr's\n     MORPH case so that the postscript orphan-attach pass can wrap\n     <instance_geometry> uses of the base mesh in an AkInstanceMorph\n     (DAE exporters — especially glTF→DAE — frequently emit a morph\n     controller without ever wrapping the geometry in\n     <instance_controller>, leaving the morph dangling otherwise). */\n  RBTree          *meshTargets;\n  AkSource        *sources;\n  AkCOLLADAVersion version;\n  bool             stop;\n} DAEState;\n\ntypedef struct AkDaeMeshInfo {\n  AkInput *pos;\n  size_t   nVertex;\n} AkDaeMeshInfo;\n\ntypedef struct AkDAETextureRef {\n  const char          *texture;\n  const char          *texcoord;\n  AkTextureColorSpace  colorSpace;\n  AkTextureChannels    channels;\n} AkDAETextureRef;\n\ntypedef struct AkNewParam {\n  /* const char * sid; */\n  struct AkNewParam *prev;\n  struct AkNewParam *next;\n  const char        *semantic;\n  AkValue           *val;\n} AkNewParam;\n\ntypedef struct AkController {\n  /* const char * id; */\n  AkOneWayIterBase     base;\n  const char          *name;\n  void                *data;\n  AkTree              *extra;\n  AkControllerType     type;\n} AkController;\n\ntypedef struct AkInstanceController {\n  AkInstanceBase    base;\n  AkURL             geometry;\n  struct AkNode   **joints;\n  AkBindMaterial   *bindMaterial;\n  struct FListItem *reserved;\n} AkInstanceController;\n\ntypedef struct AkAccessorDAE {\n  struct AkDataParam *param;\n  AkURL               source;\n  size_t              offset;\n  uint32_t            stride;\n  uint32_t            bound;\n} AkAccessorDAE;\n\ntypedef struct AkSkinJointsDAE {\n  AkInput *joints;\n  AkInput *invBindMatrix;\n} AkSkinJointsDAE;\n\ntypedef struct AkSkinWeightsDAE {\n  AkInput     *joints;\n  AkInput     *weights;\n  AkUIntArray *v;\n} AkSkinWeightsDAE;\n\ntypedef struct AkSkinDAE {\n  AkURL            baseGeom;\n  AkSource        *source;\n  AkTree          *extra;\n\n  AkSkinJointsDAE  joints;\n  AkSkinWeightsDAE weights;\n  uint32_t         inputCount;\n} AkSkinDAE;\n\ntypedef struct AkMorphDAE {\n  AkURL     baseGeom;\n  AkSource *source;\n  AkTree   *extra;\n  AkInput  *input;\n} AkMorphDAE;\n\nAK_INLINE\nvoid\nsid_seta(xml_t  * __restrict xml,\n         AkHeap * __restrict heap,\n         void   * __restrict memnode,\n         void   * __restrict memptr) {\n  const char *sid;\n\n  if (!(sid = xmla_strdup_by(xml, heap, _s_dae_sid, memnode)))\n    return;\n\n  ak_sid_seta(memnode, memptr, sid);\n}\n\nAK_INLINE\nvoid\nsid_set(xml_t  * __restrict xml,\n        AkHeap * __restrict heap,\n        void   * __restrict memnode) {\n  const char *sid;\n  \n  if (!(sid = xmla_strdup_by(xml, heap, _s_dae_sid, memnode)))\n    return;\n  \n  ak_sid_set(memnode, sid);\n}\n\nAK_INLINE\nvoid\nurl_set(DAEState   * __restrict dst,\n        xml_t      * __restrict xml,\n        const char * __restrict name,\n        void       * __restrict memp,\n        AkURL      * __restrict url) {\n  AkURLQueue *urlQueue;\n  xml_attr_t *att;\n\n  if (!(att = xmla(xml, name)) || !att->val) {\n    url->reserved = NULL;\n    url->url      = NULL;\n    return;\n  }\n\n  /* DAE_URL_INIT_FIXED normalizes bare ids (\"foo\" → \"#foo\") emitted by\n     non-conforming exporters via stack alloca; well-formed input is a\n     no-op. See bugfix/url.h. */\n  DAE_URL_INIT_FIXED(memp, xmla_strdup(att, dst->heap, memp), url);\n\n  urlQueue       = dst->heap->allocator->malloc(sizeof(*urlQueue));\n  urlQueue->next = dst->urlQueue;\n  urlQueue->url  = url;\n  dst->urlQueue  = urlQueue;\n}\n\n\nAK_INLINE\nvoid\ndae_vertmap_add(DAEState     * __restrict dst,\n                AkInput         * __restrict inp,\n                AkMeshPrimitive * __restrict prim) {\n  AkDAEVerticesMapItem *item;\n\n  if (!inp || !prim) { return; }\n\n  item       = ak_heap_calloc(dst->heap, dst->tempmem, sizeof(*item));\n  item->inp  = inp;\n  item->prim = prim;\n\n  flist_sp_insert(&dst->vertMap, item);\n}\n\nAK_INLINE\nAkURL*\nurl_from(xml_t      * __restrict xml,\n         const char * __restrict name,\n         void       * __restrict memp) {\n  AkHeap     *heap;\n  AkURL      *url;\n  xml_attr_t *att;\n\n  if (!(att = xmla(xml, name)) || ! att->val)\n    return NULL;\n\n  heap = ak_heap_getheap(memp);\n  url  = ak_heap_calloc(heap, memp, sizeof(*url));\n\n  /* see url_set: normalize bare-id industry bug before init. */\n  DAE_URL_INIT_FIXED(memp, xmla_strdup(att, heap, memp), url);\n\n  return url;\n}\n\nAK_EXPORT\nAkGeometry*\nak_baseGeometry(AkURL * __restrict baseurl);\n\n#endif /* dae_common_h */\n"
  },
  {
    "path": "src/io/dae/core/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/dae/core/anim.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"anim.h\"\n#include \"asset.h\"\n#include \"source.h\"\n#include \"enum.h\"\n\nAK_HIDE\nvoid*\ndae_anim(DAEState * __restrict dst,\n         xml_t    * __restrict xml,\n         void     * __restrict memp) {\n  AkHeap      *heap;\n  AkAnimation *anim;\n\n  heap = dst->heap;\n  anim = ak_heap_calloc(heap, memp, sizeof(*anim));\n\n  xmla_setid(xml, heap, anim);\n  \n  anim->name = xmla_strdup_by(xml, heap, _s_dae_name, anim);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, anim, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_source)) {\n      AkSource *source;\n      \n      /* store interpolation in char */\n      if ((source = dae_source(dst, xml, dae_animInterp, AKT_UBYTE))) {\n        source->next = anim->source;\n        anim->source = source;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_sampler)) {\n      AkAnimSampler *samp;\n      if ((samp = dae_animSampler(dst, xml, anim))) {\n        samp->base.next = (void *)anim->sampler;\n        anim->sampler   = samp;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_channel)) {\n      AkChannel *ch;\n      if ((ch = dae_channel(dst, xml, anim))) {\n        ch->next      = anim->channel;\n        anim->channel = ch;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_animation)) {\n      AkAnimation *subAnim;\n      if ((subAnim = dae_anim(dst, xml, anim))) {\n        subAnim->base.next = (AkOneWayIterBase *)anim->animation;\n        anim->animation    = subAnim;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      anim->extra = tree_fromxml(heap, anim, xml);\n    }\n    xml = xml->next;\n  }\n\n  return anim;\n}\n\nAK_HIDE\nAkAnimSampler*\ndae_animSampler(DAEState * __restrict dst,\n                xml_t    * __restrict xml,\n                void     * __restrict memp) {\n  AkHeap        *heap;\n  AkAnimSampler *samp;\n  AkInput       *inp;\n  xml_attr_t    *att;\n\n  heap = dst->heap;\n  samp = ak_heap_calloc(heap, memp, sizeof(*samp));\n\n  xmla_setid(xml, heap, samp);\n\n  if ((att = xmla(xml, _s_dae_pre_behavior)))\n    samp->pre = dae_animBehavior(att);\n\n  if ((att = xmla(xml, _s_dae_post_behavior)))\n    samp->post = dae_animBehavior(att);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_input)) {\n      inp              = ak_heap_calloc(heap, samp, sizeof(*inp));\n      inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp);\n      \n      if (!inp->semanticRaw) {\n        ak_free(inp);\n      } else {\n        AkURL *url;\n        AkEnum  inputSemantic;\n        \n        inputSemantic = dae_semantic(inp->semanticRaw);\n        \n        if (inputSemantic < 0)\n          inputSemantic = AK_INPUT_OTHER;\n        \n        inp->semantic = inputSemantic;\n        inp->offset   = xmla_u32(xmla(xml, _s_dae_offset), 0);\n        \n        inp->semantic = dae_semantic(inp->semanticRaw);\n        \n        url           = url_from(xml, _s_dae_source, memp);\n        rb_insert(dst->inputmap, inp, url);\n        \n        /* check if there are angles, because they are in degress,\n         will be converted to radians, we will wait to load whole dae file\n         because all sources may not be loaded at this time\n         */\n        if (inp->semantic == AK_INPUT_OUTPUT)\n          flist_sp_insert(&dst->toRadiansSampelers, samp);\n        \n        switch (inp->semantic) {\n          case AK_INPUT_INPUT:\n            samp->inputInput = inp;\n            break;\n          case AK_INPUT_OUTPUT:\n            samp->outputInput = inp;\n            break;\n          case AK_INPUT_IN_TANGENT:\n            samp->inTangentInput = inp;\n            break;\n          case AK_INPUT_OUT_TANGENT:\n            samp->outTangentInput = inp;\n            break;\n          case AK_INPUT_INTERPOLATION:\n            samp->interpInput = inp;\n            break;\n          default:\n            break;\n        }\n        \n        inp->next   = samp->input;\n        samp->input = inp;\n      }\n    }\n    xml = xml->next;\n  }\n\n  return samp;\n}\n\nAK_HIDE\nAkChannel*\ndae_channel(DAEState * __restrict dst,\n            void     * __restrict xml,\n            void     * __restrict memp) {\n  AkChannel *ch;\n\n  ch = ak_heap_calloc(dst->heap, memp, sizeof(*ch));\n\n  url_set(dst, xml, _s_dae_source, ch,  &ch->source);\n  ch->target = xmla_strdup_by(xml, dst->heap, _s_dae_target, ch);\n\n  return ch;\n}\n"
  },
  {
    "path": "src/io/dae/core/anim.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_anim_h\n#define dae_anim_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid*\ndae_anim(DAEState * __restrict dst,\n         xml_t    * __restrict xml,\n         void     * __restrict memp);\n\nAK_HIDE\nAkAnimSampler*\ndae_animSampler(DAEState * __restrict dst,\n                xml_t    * __restrict xml,\n                void     * __restrict memp);\n\nAK_HIDE\nAkChannel*\ndae_channel(DAEState * __restrict dst,\n            void     * __restrict xml,\n            void     * __restrict memp);\n\n#endif /* dae_anim_h */\n"
  },
  {
    "path": "src/io/dae/core/asset.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"asset.h\"\n\nAK_HIDE\nAkAssetInf*\ndae_asset(DAEState   * __restrict dst,\n          xml_t      * __restrict xml,\n          void       * __restrict memp,\n          AkAssetInf * __restrict inf) {\n  AkHeap        *heap;\n  xml_attr_t    *attr;\n  AkContributor *cont;\n  xml_t         *xcont;\n  char          *val;\n\n  heap = dst->heap;\n\n  if (!inf)\n    inf = ak_heap_alloc(heap, memp, sizeof(*inf));\n\n  /* DAE default definitions */\n  \n  /* CoordSys is Y_UP */\n  inf->coordSys    = AK_YUP;\n\n  /* Unit is 1 meter */\n  inf->unit        = ak_heap_calloc(heap, inf, sizeof(*inf->unit));\n  inf->unit->dist  = 1.0;\n  inf->unit->name  = ak_heap_strdup(heap, inf->unit, _s_dae_meter);\n\n  if (xml) {\n    xml = xml->val;\n    while (xml) {\n      if (xml_tag_eq(xml, _s_dae_contributor)) {\n        cont  = ak_heap_calloc(heap, inf, sizeof(*cont));\n        xcont = xml->val;\n        while (xcont) {\n          if (xml_tag_eq(xcont, _s_dae_author))\n            cont->author = xml_strdup(xcont, heap, inf);\n          else if (xml_tag_eq(xcont, _s_dae_author_email))\n            cont->authorEmail = xml_strdup(xcont, heap, inf);\n          else if (xml_tag_eq(xcont, _s_dae_author_website))\n            cont->authorWebsite = xml_strdup(xcont, heap, inf);\n          else if (xml_tag_eq(xcont, _s_dae_authoring_tool))\n            cont->authoringTool = xml_strdup(xcont, heap, inf);\n          else if (xml_tag_eq(xcont, _s_dae_comments))\n            cont->comments = xml_strdup(xcont, heap, inf);\n          else if (xml_tag_eq(xcont, _s_dae_copyright))\n            cont->copyright = xml_strdup(xcont, heap, inf);\n          else if (xml_tag_eq(xcont, _s_dae_source_data))\n            cont->sourceData = xml_strdup(xcont, heap, inf);\n          xcont = xcont->next;\n        }\n        \n        inf->contributor = cont;\n      } else if (xml_tag_eq(xml, _s_dae_created)) {\n        if ((val = xml_strdup(xml, heap, inf))) {\n          memset(&xml[xml->valsize], '\\0', xml->valsize);\n          inf->created = ak_parse_date(val, NULL);\n          ak_free(val);\n        }\n      } else if (xml_tag_eq(xml, _s_dae_modified)) {\n        if ((val = xml_strdup(xml, heap, inf))) {\n          memset(&xml[xml->valsize], '\\0', xml->valsize);\n          inf->modified = ak_parse_date(val, NULL);\n          ak_free(val);\n        }\n      } else if (xml_tag_eq(xml, _s_dae_keywords)) {\n        inf->keywords = xml_strdup(xml, heap, inf);\n      } else if (xml_tag_eq(xml, _s_dae_revision)) {\n        inf->revision = xml_strdup(xml, heap, inf);\n      } else if (xml_tag_eq(xml, _s_dae_subject)) {\n        inf->subject = xml_strdup(xml, heap, inf);\n      } else if (xml_tag_eq(xml, _s_dae_title)) {\n        inf->title = xml_strdup(xml, heap, inf);\n      } else if (xml_tag_eq(xml, _s_dae_unit)) {\n        if ((attr = xmla(xml, _s_dae_name)))\n          inf->unit->name = xmla_strdup(attr, heap, inf->unit);\n        \n        if ((attr = xmla(xml, _s_dae_meter))) {\n          /* memset((char *)attr->val +attr->valsize, '\\0', 1); */\n          inf->unit->dist = xmla_double(attr, 0.0);\n        }\n      } else if (xml_tag_eq(xml, _s_dae_up_axis)) {\n        if ((val = xml_strdup(xml, heap, inf))) {\n          if (strcasecmp(val, _s_dae_z_up) == 0)\n            inf->coordSys = AK_ZUP;\n          else if (strcasecmp(val, _s_dae_x_up) == 0)\n            inf->coordSys = AK_XUP;\n          else\n            inf->coordSys = AK_YUP;\n          ak_free(val);\n        }\n      } else if (xml_tag_eq(xml, _s_dae_extra)) {\n        inf->extra = tree_fromxml(heap, inf, xml);\n      }\n      \n      xml = xml->next;\n    }\n  } /* else -> default */\n\n  *(AkAssetInf **)ak_heap_ext_add(heap,\n                                  ak__alignof(memp),\n                                  AK_HEAP_NODE_FLAGS_INF) = inf;\n\n  return inf;\n}\n"
  },
  {
    "path": "src/io/dae/core/asset.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_asset_h\n#define dae_asset_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkAssetInf*\ndae_asset(DAEState   * __restrict dst,\n          xml_t      * __restrict xml,\n          void       * __restrict memp,\n          AkAssetInf * __restrict pinf);\n\n#endif /* dae_asset_h */\n"
  },
  {
    "path": "src/io/dae/core/cam.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"cam.h\"\n#include \"asset.h\"\n#include \"techn.h\"\n\nAK_HIDE\nvoid*\ndae_cam(DAEState * __restrict dst,\n        xml_t    * __restrict xml,\n        void     * __restrict memp) {\n  AkHeap      *heap;\n  AkCamera    *cam;\n  AkTechnique *tq;\n\n  heap      = dst->heap;\n  cam       = ak_heap_calloc(heap, memp, sizeof(*cam));\n  cam->name = xmla_strdup_by(xml, heap, _s_dae_name, cam);\n\n  xmla_setid(xml, heap, cam);\n  \n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, cam, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_optics)) {\n      AkOptics *optics;\n      xml_t    *xoptics;\n\n      optics  = ak_heap_calloc(heap, cam, sizeof(*optics));\n      xoptics = xml->val;\n\n      while (xoptics) {\n        if (xml_tag_eq(xoptics, _s_dae_techniquec)) {\n          xml_t *xtech, *xtechv;\n\n          xtech = xoptics->val;\n\n          while (xtech) {\n            if (xml_tag_eq(xtech, _s_dae_perspective)) {\n              AkPerspective *persp;\n\n              persp  = ak_heap_calloc(heap, optics, sizeof(*persp));\n              xtechv = xtech->val;\n\n              while (xtechv) {\n                if (xml_tag_eq(xtechv, _s_dae_xfov)) {\n                  sid_seta(xtechv, heap, persp, &persp->xfov);\n                  persp->xfov = glm_rad(xml_float(xtechv, 0.0f));\n                } else if (xml_tag_eq(xtechv, _s_dae_yfov)) {\n                  sid_seta(xtechv, heap, persp, &persp->yfov);\n                  persp->yfov = glm_rad(xml_float(xtechv, 0.0f));\n                } else if (xml_tag_eq(xtechv, _s_dae_aspect_ratio)) {\n                  sid_seta(xtechv, heap, persp, &persp->aspectRatio);\n                  persp->aspectRatio = xml_float(xtechv, 0.0f);\n                } else if (xml_tag_eq(xtechv, _s_dae_znear)) {\n                  sid_seta(xtechv, heap, persp, &persp->znear);\n                  persp->znear = xml_float(xtechv, 0.0f);\n                } else if (xml_tag_eq(xtechv, _s_dae_zfar)) {\n                  sid_seta(xtechv, heap, persp, &persp->zfar);\n                  persp->zfar = xml_float(xtechv, 0.0f);\n                }\n                xtechv = xtechv->next;\n              }\n\n              persp->base.type = AK_PROJECTION_PERSPECTIVE;\n              if (!persp->aspectRatio && persp->yfov && persp->xfov) {\n                persp->aspectRatio = persp->xfov / persp->yfov;\n              } else if (!persp->yfov && persp->aspectRatio && persp->xfov) {\n                persp->yfov = persp->xfov / persp->aspectRatio;\n              } else if (!persp->xfov && persp->aspectRatio && persp->yfov) {\n                persp->xfov = persp->yfov * persp->aspectRatio;\n              }\n\n              optics->tcommon = &persp->base;\n            } else if (xml_tag_eq(xtech, _s_dae_orthographic)) {\n              AkOrthographic *ortho;\n\n              ortho = ak_heap_calloc(heap, optics, sizeof(*ortho));\n              xtechv = xtech->val;\n\n              while (xtechv) {\n                if (xml_tag_eq(xtechv, _s_dae_xmag)) {\n                  sid_seta(xtechv, heap, ortho, &ortho->xmag);\n                  ortho->xmag = xml_float(xtechv, 0.0f);\n                } else if (xml_tag_eq(xtechv, _s_dae_ymag)) {\n                  sid_seta(xtechv, heap, ortho, &ortho->ymag);\n                  ortho->ymag = xml_float(xtechv, 0.0f);\n                } else if (xml_tag_eq(xtechv, _s_dae_aspect_ratio)) {\n                  sid_seta(xtechv, heap, ortho, &ortho->aspectRatio);\n                  ortho->aspectRatio = xml_float(xtechv, 0.0f);\n                } else if (xml_tag_eq(xtechv, _s_dae_znear)) {\n                  sid_seta(xtechv, heap, ortho, &ortho->znear);\n                  ortho->znear = xml_float(xtechv, 0.0f);\n                } else if (xml_tag_eq(xtechv, _s_dae_zfar)) {\n                  sid_seta(xtechv, heap, ortho, &ortho->zfar);\n                  ortho->zfar = xml_float(xtechv, 0.0f);\n                }\n                xtechv = xtechv->next;\n              }\n\n              ortho->base.type = AK_PROJECTION_ORTHOGRAPHIC;\n              if (!ortho->aspectRatio && ortho->ymag && ortho->xmag) {\n                ortho->aspectRatio = ortho->xmag / ortho->ymag;\n              } else if (!ortho->ymag && ortho->aspectRatio && ortho->xmag) {\n                ortho->ymag = ortho->xmag / ortho->aspectRatio;\n              } else if (!ortho->xmag && ortho->aspectRatio && ortho->ymag) {\n                ortho->xmag = ortho->ymag * ortho->aspectRatio;\n              }\n              \n              optics->tcommon = &ortho->base;\n            }\n            xtech = xtech->next;\n          }\n        } else if (xml_tag_eq(xoptics, _s_dae_technique)) {\n          tq                = dae_techn(xoptics, heap, optics);\n          tq->next          = optics->technique;\n          optics->technique = tq;\n        }\n        xoptics = xoptics->next;\n      }\n\n      cam->optics = optics;\n    } else if (xml_tag_eq(xml, _s_dae_imager)) {\n      AkImager *imager;\n      xml_t    *ximager;\n\n      imager  = ak_heap_calloc(heap, cam, sizeof(*imager));\n      ximager = xml->val;\n\n      while (ximager) {\n        if (xml_tag_eq(ximager, _s_dae_technique)) {\n          tq                = dae_techn(ximager, heap, imager);\n          tq->next          = imager->technique;\n          imager->technique = tq;\n        } else if (xml_tag_eq(ximager, _s_dae_extra)) {\n          imager->extra = tree_fromxml(heap, imager, xml);\n        }\n        ximager = ximager->next;\n      }\n\n      cam->imager = imager;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      cam->extra = tree_fromxml(heap, cam, xml);\n    }\n\n    xml = xml->next;\n  }\n\n  return cam;\n}\n"
  },
  {
    "path": "src/io/dae/core/cam.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_camera_h\n#define dae_camera_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid*\ndae_cam(DAEState * __restrict dst,\n        xml_t    * __restrict xml,\n        void     * __restrict memp);\n\n#endif /* dae_camera_h */\n"
  },
  {
    "path": "src/io/dae/core/color.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"color.h\"\n\nAK_HIDE\nvoid\ndae_color(xml_t   * __restrict xml,\n          void    *            memparent,\n          bool                 read_sid,\n          bool                 stack,\n          AkColor *            dest) {\n  AkHeap       *heap;\n  void         *memp;\n  unsigned long c;\n\n  heap = ak_heap_getheap(memparent);\n  memp = stack ? memparent : dest;\n\n  if (read_sid)\n    sid_set(xml, heap, memp);\n  \n  c = xml_strtof_fast(xml->val, dest->vec, 4);\n  \n  if (c > 4)\n    c = 4;\n\n  if (c > 0) {\n    do {\n      dest->vec[4 - c--] = 1.0f;\n    } while (c > 0);\n  }\n  \n  glm_vec4_clamp(dest->vec, 0.0f, 1.0f);\n\n  /* BUGIFX: assume alpha=0 is an export BUG */\n  if (dest->vec[3] < 0.125) {\n    dest->vec[3] = 1.0f - dest->vec[3];\n  }\n\n  ak_sRGB_linear(dest);\n}\n"
  },
  {
    "path": "src/io/dae/core/color.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_color_h\n#define dae_color_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ndae_color(xml_t   * __restrict xml,\n          void    *            memparent,\n          bool                 read_sid,\n          bool                 stack,\n          AkColor *            dest);\n\n#endif /* dae_color_h */\n"
  },
  {
    "path": "src/io/dae/core/ctlr.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ctlr.h\"\n#include \"skin.h\"\n#include \"morph.h\"\n#include \"../core/asset.h\"\n\nAK_HIDE\nvoid*\ndae_ctlr(DAEState * __restrict dst,\n         xml_t    * __restrict xml,\n         void     * __restrict memp) {\n  AkHeap       *heap;\n  AkController *ctlr;\n\n  heap       = dst->heap;\n  ctlr       = ak_heap_calloc(heap, memp, sizeof(*ctlr));\n  ctlr->name = xmla_strdup_by(xml, heap, _s_dae_name, ctlr);\n  \n  xmla_setid(xml, heap, ctlr);\n  ak_setypeid(ctlr, AKT_CONTROLLER);\n  \n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, ctlr, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_skin)) {\n      ctlr->data = dae_skin(dst, xml, ctlr);\n      ctlr->type = AK_CONTROLLER_SKIN;\n    } else if (xml_tag_eq(xml, _s_dae_morph)) {\n      ctlr->data = dae_morph(dst, xml, ctlr);\n      ctlr->type = AK_CONTROLLER_MORPH;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      ctlr->extra = tree_fromxml(heap, ctlr, xml);\n    }\n    xml = xml->next;\n  }\n\n  return ctlr;\n}\n"
  },
  {
    "path": "src/io/dae/core/ctlr.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_controller_h\n#define dae_controller_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid*\ndae_ctlr(DAEState * __restrict dst,\n         xml_t    * __restrict xml,\n         void     * __restrict memp);\n\n#endif /* dae_controller_h */\n"
  },
  {
    "path": "src/io/dae/core/enum.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"enum.h\"\n#include \"../../../common.h\"\n#include \"../common.h\"\n#include <string.h>\n\nAK_HIDE AkEnum\ndae_semantic(const char * name) {\n  AkEnum val;\n  long   glenums_len, i;\n\n  if (!name)\n    return AK_INPUT_OTHER;\n  \n  dae_enum glenums[] = {\n    {\"BINORMAL\",        AK_INPUT_BINORMAL},\n    {\"COLOR\",           AK_INPUT_COLOR},\n    {\"CONTINUITY\",      AK_INPUT_CONTINUITY},\n    {\"IMAGE\",           AK_INPUT_IMAGE},\n    {\"INPUT\",           AK_INPUT_INPUT},\n    {\"IN_TANGENT\",      AK_INPUT_IN_TANGENT},\n    {\"INTERPOLATION\",   AK_INPUT_INTERPOLATION},\n    {\"INV_BIND_MATRIX\", AK_INPUT_INV_BIND_MATRIX},\n    {\"JOINT\",           AK_INPUT_JOINT},\n    {\"LINEAR_STEPS\",    AK_INPUT_LINEAR_STEPS},\n    {\"MORPH_TARGET\",    AK_INPUT_MORPH_TARGET},\n    {\"MORPH_WEIGHT\",    AK_INPUT_MORPH_WEIGHT},\n    {\"NORMAL\",          AK_INPUT_NORMAL},\n    {\"OUTPUT\",          AK_INPUT_OUTPUT},\n    {\"OUT_TANGENT\",     AK_INPUT_OUT_TANGENT},\n    {\"POSITION\",        AK_INPUT_POSITION},\n    {\"TANGENT\",         AK_INPUT_TANGENT},\n    {\"TEXBINORMAL\",     AK_INPUT_TEXBINORMAL},\n    {\"TEXCOORD\",        AK_INPUT_TEXCOORD},\n    {\"TEXTANGENT\",      AK_INPUT_TEXTANGENT},\n    {\"UV\",              AK_INPUT_UV},\n    {\"VERTEX\",          AK_INPUT_SEMANTIC_VERTEX},\n    {\"WEIGHT\",          AK_INPUT_WEIGHT},\n  };\n\n  /* COLLADA 1.5: ALWAYS is the default */\n  val         = AK_INPUT_OTHER;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (strcasecmp(name, glenums[i].name) == 0) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_morphMethod(const xml_attr_t * __restrict xatt) {\n  AkEnum val;\n  long   glenums_len, i;\n\n  if (!xatt)\n    return AK_MORPH_METHOD_NORMALIZED;\n  \n  dae_enum glenums[] = {\n    {\"NORMALIZED\", AK_MORPH_METHOD_NORMALIZED},\n    {\"RELATIVE\",   AK_MORPH_METHOD_RELATIVE},\n  };\n\n  val         = AK_MORPH_METHOD_NORMALIZED;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (strncasecmp(xatt->val, glenums[i].name, xatt->valsize) == 0) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_nodeType(const xml_attr_t * __restrict xatt) {\n  AkEnum val;\n  long   glenums_len, i;\n  \n  if (!xatt)\n    return AK_NODE_TYPE_NODE;\n\n  dae_enum glenums[] = {\n    {\"NODE\",  AK_NODE_TYPE_NODE},\n    {\"JOINT\", AK_NODE_TYPE_JOINT},\n  };\n\n  val         = AK_NODE_TYPE_NODE;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (strncasecmp(xatt->val, glenums[i].name, xatt->valsize) == 0) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_animBehavior(const xml_attr_t * __restrict xatt) {\n  AkEnum val;\n  long   glenums_len, i;\n\n  if (!xatt)\n    return AK_SAMPLER_BEHAVIOR_UNDEFINED;\n  \n  dae_enum glenums[] = {\n    {\"UNDEFINED\",      AK_SAMPLER_BEHAVIOR_UNDEFINED},\n    {\"CONSTANT\",       AK_SAMPLER_BEHAVIOR_CONSTANT},\n    {\"GRADIENT\",       AK_SAMPLER_BEHAVIOR_GRADIENT},\n    {\"CYCLE\",          AK_SAMPLER_BEHAVIOR_CYCLE},\n    {\"OSCILLATE\",      AK_SAMPLER_BEHAVIOR_OSCILLATE},\n    {\"CYCLE_RELATIVE\", AK_SAMPLER_BEHAVIOR_CYCLE_RELATIVE}\n  };\n\n  val         = AK_SAMPLER_BEHAVIOR_UNDEFINED;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (strncasecmp(xatt->val, glenums[i].name, xatt->valsize) == 0) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_animInterp(const char *name, size_t len) {\n  AkEnum val;\n  long   glenums_len, i;\n\n  if (!name)\n    return AK_INTERPOLATION_LINEAR;\n  \n  dae_enum glenums[] = {\n    {\"LINEAR\",   AK_INTERPOLATION_LINEAR},\n    {\"BEZIER\",   AK_INTERPOLATION_BEZIER},\n    {\"CARDINAL\", AK_INTERPOLATION_CARDINAL},\n    {\"HERMITE\",  AK_INTERPOLATION_HERMITE},\n    {\"BSPLINE\",  AK_INTERPOLATION_BSPLINE},\n    {\"STEP\",     AK_INTERPOLATION_STEP}\n  };\n\n  val         = AK_INTERPOLATION_LINEAR;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (strncasecmp(name, glenums[i].name, len) == 0) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_wrap(const xml_t * __restrict xml) {\n  AkEnum val;\n  long   glenums_len, i;\n\n  if (!xml)\n    return 0;\n  \n  dae_enum glenums[] = {\n    {\"WRAP\",        AK_WRAP_MODE_WRAP},\n    {\"CLAMP\",       AK_WRAP_MODE_CLAMP},\n    {\"BORDER\",      AK_WRAP_MODE_BORDER},\n    {\"MIRROR\",      AK_WRAP_MODE_MIRROR},\n    {\"MIRROR_ONCE\", AK_WRAP_MODE_MIRROR_ONCE}\n  };\n\n  val         = 0;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (xml_val_eq(xml, glenums[i].name)) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_minfilter(const xml_t * __restrict xml) {\n  AkEnum val;\n  long   glenums_len, i;\n\n  if (!xml)\n    return 0;\n  \n  dae_enum glenums[] = {\n    {\"NEAREST\",     AK_MINFILTER_NEAREST},\n    {\"LINEAR\",      AK_MINFILTER_LINEAR},\n    {\"ANISOTROPIC\", AK_MINFILTER_ANISOTROPIC}\n  };\n\n  val         = 0;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (xml_val_eq(xml, glenums[i].name)) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_mipfilter(const xml_t * __restrict xml) {\n  AkEnum val;\n  long   glenums_len, i;\n\n  if (!xml)\n    return 0;\n  \n  dae_enum glenums[] = {\n    {\"NONE\",    AK_MIPFILTER_NONE},\n    {\"NEAREST\", AK_MIPFILTER_NEAREST},\n    {\"LINEAR\",  AK_MIPFILTER_LINEAR}\n  };\n\n  val         = 0;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (xml_val_eq(xml, glenums[i].name)) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_magfilter(const xml_t * __restrict xml) {\n  AkEnum val;\n  long   glenums_len, i;\n\n  if (!xml)\n    return 0;\n  \n  dae_enum glenums[] = {\n    {\"NEAREST\", AK_MAGFILTER_NEAREST},\n    {\"LINEAR\",  AK_MAGFILTER_LINEAR}\n  };\n\n  val         = 0;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (xml_val_eq(xml, glenums[i].name)) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_face(const xml_attr_t * __restrict xatt) {\n  AkEnum val;\n  long   glenums_len, i;\n\n  if (!xatt)\n    return 0;\n  \n  dae_enum glenums[] = {\n    {\"POSITIVE_X\", AK_FACE_POSITIVE_X},\n    {\"NEGATIVE_X\", AK_FACE_NEGATIVE_X},\n    {\"POSITIVE_Y\", AK_FACE_POSITIVE_Y},\n    {\"NEGATIVE_Y\", AK_FACE_NEGATIVE_Y},\n    {\"POSITIVE_Z\", AK_FACE_POSITIVE_Z},\n    {\"NEGATIVE_Z\", AK_FACE_NEGATIVE_Z}\n  };\n\n  val         = 0;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (strncasecmp(xatt->val, glenums[i].name, xatt->valsize) == 0) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_opaque(const xml_attr_t * __restrict xatt) {\n  AkEnum val;\n  long  glenums_len, i;\n\n  if (!xatt)\n    return AK_OPAQUE_A_ONE;\n  \n  dae_enum glenums[] = {\n    {\"A_ONE\",    AK_OPAQUE_A_ONE},\n    {\"RGB_ZERO\", AK_OPAQUE_RGB_ZERO},\n    {\"A_ZERO\",   AK_OPAQUE_A_ZERO},\n    {\"RGB_ONE\",  AK_OPAQUE_RGB_ONE}\n  };\n\n  val         = AK_OPAQUE_A_ONE;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (strncasecmp(xatt->val, glenums[i].name, xatt->valsize) == 0) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_enumChannel(const char *name, size_t len) {\n  AkEnum val;\n  long   glenums_len, i;\n\n  if (!name)\n    return 0;\n  \n  dae_enum glenums[] = {\n    {\"RGB\",  AK_CHANNEL_FORMAT_RGB},\n    {\"RGBA\", AK_CHANNEL_FORMAT_RGBA},\n    {\"RGBE\", AK_CHANNEL_FORMAT_RGBE},\n    {\"L\",    AK_CHANNEL_FORMAT_L},\n    {\"LA\",   AK_CHANNEL_FORMAT_LA},\n    {\"D\",    AK_CHANNEL_FORMAT_D},\n\n    /* 1.4 */\n    {\"XYZ\",  AK_CHANNEL_FORMAT_XYZ},\n    {\"XYZW\", AK_CHANNEL_FORMAT_XYZW}\n  };\n\n  val         = 0;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (strncasecmp(name, glenums[i].name, len) == 0) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_range(const char *name, size_t len) {\n  AkEnum val;\n  long   glenums_len, i;\n\n  if (!name)\n    return 0;\n  \n  dae_enum glenums[] = {\n    {\"SNORM\", AK_RANGE_FORMAT_SNORM},\n    {\"UNORM\", AK_RANGE_FORMAT_UNORM},\n    {\"SINT\",  AK_RANGE_FORMAT_SINT},\n    {\"UINT\",  AK_RANGE_FORMAT_UINT},\n    {\"FLOAT\", AK_RANGE_FORMAT_FLOAT}\n  };\n\n  val         = 0;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (strncasecmp(name, glenums[i].name, len) == 0) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ndae_precision(const char *name, size_t len) {\n  AkEnum val;\n  long   glenums_len, i;\n\n  if (!name)\n    return 0;\n\n  dae_enum glenums[] = {\n    {\"DEFAULT\", AK_PRECISION_FORMAT_DEFAULT},\n    {\"LOW\",     AK_PRECISION_FORMAT_LOW},\n    {\"MID\",     AK_PRECISION_FORMAT_MID},\n    {\"HIGH\",    AK_PRECISION_FORMAT_HIGHT},\n    {\"MAX\",     AK_PRECISION_FORMAT_MAX}\n  };\n\n  val         = 0;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (strncasecmp(name, glenums[i].name, len) == 0) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n"
  },
  {
    "path": "src/io/dae/core/enum.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_enums_h\n#define dae_enums_h\n\n#include \"../../../xml.h\"\n\nAK_HIDE AkEnum\ndae_semantic(const char * name);\n\nAK_HIDE AkEnum\ndae_morphMethod(const xml_attr_t * __restrict xatt);\n\nAK_HIDE AkEnum\ndae_nodeType(const xml_attr_t * __restrict xatt);\n\nAK_HIDE AkEnum\ndae_animBehavior(const xml_attr_t * __restrict xatt);\n\nAK_HIDE AkEnum\ndae_animInterp(const char * name, size_t len);\n\nAK_HIDE AkEnum\ndae_wrap(const xml_t * __restrict xml);\n\nAK_HIDE AkEnum\ndae_minfilter(const xml_t * __restrict xml);\n\nAK_HIDE AkEnum\ndae_mipfilter(const xml_t * __restrict xml);\n\nAK_HIDE AkEnum\ndae_magfilter(const xml_t * __restrict xml);\n\nAK_HIDE AkEnum\ndae_face(const xml_attr_t * __restrict xatt);\n\nAK_HIDE AkEnum\ndae_opaque(const xml_attr_t * __restrict xatt);\n\nAK_HIDE AkEnum\ndae_enumChannel(const char *name, size_t len);\n\nAK_HIDE AkEnum\ndae_range(const char *name, size_t len);\n\nAK_HIDE AkEnum\ndae_precision(const char *name, size_t len);\n\n#endif /* dae_enums_h */\n"
  },
  {
    "path": "src/io/dae/core/geom.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"geom.h\"\n#include \"mesh.h\"\n#include \"spline.h\"\n#include \"../core/asset.h\"\n#include \"../brep/brep.h\"\n\nAK_HIDE\nvoid*\ndae_geom(DAEState * __restrict dst,\n         xml_t    * __restrict xml,\n         void     * __restrict memp) {\n  AkGeometry *geom;\n  AkHeap     *heap;\n\n  heap              = dst->heap;\n  geom              = ak_heap_calloc(heap, memp, sizeof(*geom));\n  geom->name        = xmla_strdup_by(xml, heap, _s_dae_name, geom);\n  geom->materialMap = ak_map_new(ak_cmp_str);\n  \n  xmla_setid(xml, heap, geom);\n  \n  /* destroy heap with this object */\n  ak_setAttachedHeap(geom, geom->materialMap->heap);\n  ak_setypeid(geom, AKT_GEOMETRY);\n  \n  xml = xml->val;\n\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, geom, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_mesh)\n               || xml_tag_eq(xml, _s_dae_convex_mesh)) {\n      geom->gdata = dae_mesh(dst, xml, geom);\n    } else if (xml_tag_eq(xml, _s_dae_spline)) {\n      geom->gdata = dae_spline(dst, xml, geom);\n    } else if (xml_tag_eq(xml, _s_dae_brep)) {\n      geom->gdata = dae_brep(dst, xml, geom);\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      geom->extra = tree_fromxml(heap, geom, xml);\n    }\n    \n    xml = xml->next;\n  }\n  \n  return geom;\n}\n"
  },
  {
    "path": "src/io/dae/core/geom.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_geometry_h\n#define dae_geometry_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid*\ndae_geom(DAEState * __restrict dst,\n         xml_t    * __restrict xml,\n         void     * __restrict memp);\n\n#endif /* dae_geometry_h */\n"
  },
  {
    "path": "src/io/dae/core/light.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"light.h\"\n#include \"asset.h\"\n#include \"techn.h\"\n#include \"color.h\"\n\n#define AK_DEFAULT_LIGHT_DIR {0.0f, 0.0f, -1.0f}\n\nAK_HIDE\nvoid*\ndae_light(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp) {\n  AkLight     *light;\n  AkHeap      *heap;\n  AkTechnique *tq;\n\n  heap        = dst->heap;\n  light       = ak_heap_calloc(heap, memp, sizeof(*light));\n  light->name = xmla_strdup_by(xml, heap, _s_dae_name, light);\n  \n  xmla_setid(xml, heap, light);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, light, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_techniquec)) {\n      xml_t          *xtech, *xtechv, *xcolor;\n      AkAmbientLight *lightb;\n      AkCoordSys     *optCoordSys;\n      \n      xtech = xml->val;\n  \n      if (xml_tag_eq(xtech, _s_dae_ambient)) {\n        lightb       = ak_heap_calloc(heap, light, sizeof(AkAmbientLight));\n        lightb->type = AK_LIGHT_TYPE_AMBIENT;\n      } else if (xml_tag_eq(xtech, _s_dae_directional)) {\n        lightb       = ak_heap_calloc(heap, light, sizeof(AkDirectionalLight));\n        lightb->type = AK_LIGHT_TYPE_DIRECTIONAL;\n      } else if (xml_tag_eq(xtech, _s_dae_point)) {\n        AkPointLight *point;\n        \n        point            = ak_heap_calloc(heap, light, sizeof(*point));\n        point->base.type = AK_LIGHT_TYPE_POINT;\n        lightb           = &point->base;\n        \n        /* default values */\n        point->constAttn = 1.0f;\n        \n        xtechv = xtech->val;\n        while (xtechv) {\n          if (xml_tag_eq(xtechv, _s_dae_color)) {\n            sid_seta(xtechv, heap, point, &point->base.color);\n          } else if (xml_tag_eq(xtechv, _s_dae_const_attn)) {\n            sid_seta(xtechv, heap, point, &point->constAttn);\n            point->constAttn = xml_float(xtechv, 1.0f);\n          } else if (xml_tag_eq(xtechv, _s_dae_linear_attn)) {\n            sid_seta(xtechv, heap, point, &point->linearAttn);\n            point->linearAttn = xml_float(xtechv, 0.0f);\n          } else if (xml_tag_eq(xtechv, _s_dae_quad_attn)) {\n            sid_seta(xtechv, heap, point, &point->quadAttn);\n            point->quadAttn = xml_float(xtechv, 0.0f);\n          }\n          xtechv = xtechv->next;\n        }\n      } else if (xml_tag_eq(xtech, _s_dae_spot)) {\n        AkSpotLight *spot;\n\n        spot            = ak_heap_calloc(heap, light, sizeof(*spot));\n        spot->base.type = AK_LIGHT_TYPE_SPOT;\n        lightb          = &spot->base;\n\n        /* default values */\n        spot->constAttn    = 1.0f;\n        spot->falloffAngle = glm_rad(180.0f);\n        \n        xtechv = xtech->val;\n        while (xtechv) {\n          if (xml_tag_eq(xtechv, _s_dae_color)) {\n            sid_seta(xtechv, heap, spot, &spot->base.color);\n          } else if (xml_tag_eq(xtechv, _s_dae_const_attn)) {\n            sid_seta(xtechv, heap, spot, &spot->constAttn);\n            spot->constAttn = xml_float(xtechv, 0.0f);\n          } else if (xml_tag_eq(xtechv, _s_dae_linear_attn)) {\n            sid_seta(xtechv, heap, spot, &spot->linearAttn);\n            spot->linearAttn = xml_float(xtechv, 0.0f);\n          } else if (xml_tag_eq(xtechv, _s_dae_quad_attn)) {\n            sid_seta(xtechv, heap, spot, &spot->quadAttn);\n            spot->quadAttn = xml_float(xtechv, 0.0f);\n          } else if (xml_tag_eq(xtechv, _s_dae_falloff_angle)) {\n            sid_seta(xtechv, heap, spot, &spot->falloffAngle);\n            spot->falloffAngle = xml_float(xtechv, 0.0f);\n            glm_make_rad(&spot->falloffAngle);\n          } else if (xml_tag_eq(xtechv, _s_dae_falloff_exp)) {\n            sid_seta(xtechv, heap, spot, &spot->falloffExp);\n            spot->falloffExp = xml_float(xtechv, 0.0f);\n          }\n          xtechv = xtechv->next;\n        }\n      } else {\n        goto nxt;\n      }\n\n      if ((xcolor = xml_elem(xtech, _s_dae_color))) {\n        dae_color(xcolor, lightb, true, true, &lightb->color);\n      } else {\n        glm_vec4_one(lightb->color.vec);\n      }\n      \n      if ((light->tcommon = lightb)) {\n        /* fix coord sys  */\n        optCoordSys = (void *)ak_opt_get(AK_OPT_COORD);\n        if (ak_opt_get(AK_OPT_COORD_CONVERT_TYPE) == AK_COORD_CVT_ALL\n            && optCoordSys != dst->doc->coordSys) {\n          /* convert default cone direction to new coord sys */\n          ak_coordCvtVectorTo(dst->doc->coordSys,\n                              (vec3)AK_DEFAULT_LIGHT_DIR,\n                              optCoordSys,\n                              lightb->direction);\n        } else {\n          glm_vec3_copy((vec3)AK_DEFAULT_LIGHT_DIR,\n                        lightb->direction);\n        }\n      }\n    } else if (xml_tag_eq(xml, _s_dae_technique)) {\n      tq               = dae_techn(xml, heap, light);\n      tq->next         = light->technique;\n      light->technique = tq;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      light->extra = tree_fromxml(heap, light, xml);\n    }\n\n  nxt:\n    xml = xml->next;\n  }\n  \n  return light;\n}\n"
  },
  {
    "path": "src/io/dae/core/light.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_light_h\n#define dae_light_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid*\ndae_light(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp);\n\n#endif /* dae_light_h */\n"
  },
  {
    "path": "src/io/dae/core/line.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"line.h\"\n#include \"enum.h\"\n#include \"../../../array.h\"\n\nAK_HIDE\nAkLines*\ndae_lines(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp,\n          AkLineMode            mode) {\n  AkLines *lines;\n  AkHeap  *heap;\n  uint32_t indexoff;\n  \n  heap  = dst->heap;\n  lines = ak_heap_calloc(heap, memp, sizeof(*lines));\n  \n  lines->mode              = mode;\n  lines->base.type         = AK_PRIMITIVE_LINES;\n\n  lines->base.name         = xmla_strdup_by(xml, heap, _s_dae_name, lines);\n  lines->base.bindmaterial = xmla_strdup_by(xml, heap, _s_dae_material, lines);\n  lines->base.nPolygons    = xmla_u32(xmla(xml, _s_dae_count), 0);\n  \n  indexoff = 0;\n  xml      = xml->val;\n  \n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_input)) {\n      AkInput *inp;\n      \n      inp              = ak_heap_calloc(heap, lines, sizeof(*inp));\n      inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp);\n      \n      if (!inp->semanticRaw) {\n        ak_free(inp);\n      } else {\n        AkURL *url;\n        AkEnum inputSemantic;\n\n        inputSemantic = dae_semantic(inp->semanticRaw);\n\n        if (inputSemantic < 0)\n          inputSemantic = AK_INPUT_OTHER;\n\n        inp->semantic = inputSemantic;\n        inp->offset   = xmla_u32(xmla(xml, _s_dae_offset), 0);\n        inp->set      = xmla_u32(xmla(xml, _s_dae_set),    0);\n\n        url = url_from(xml, _s_dae_source, memp);\n        rb_insert(dst->inputmap, inp, url);\n\n        if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) {\n          inp->semantic     = dae_semantic(inp->semanticRaw);\n          inp->next         = lines->base.input;\n          lines->base.input = inp;\n          lines->base.inputCount++;\n\n          if (inp->offset > indexoff)\n            indexoff = inp->offset;\n        } else {\n          dae_vertmap_add(dst, inp, &lines->base);\n\n          /* don't store VERTEX because it will be duplicated to all prims */\n          // lines->base.reserved1 = inp->offset;\n          // lines->base.reserved2 = inp->set;\n          // ak_free(inp);\n        }\n      }\n    } else if (xml_tag_eq(xml, _s_dae_p) && xml->val) {\n      AkUIntArray *uintArray;\n      AkResult     ret;\n\n      ret = xml_strtoui_array(heap, lines, xml->val, &uintArray);\n      if (ret == AK_OK)\n        lines->base.indices = uintArray;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      lines->base.extra = tree_fromxml(heap, lines, xml);\n    }\n    xml = xml->next;\n  }\n  \n  lines->base.indexStride = indexoff + 1;\n  \n  return lines;\n}\n"
  },
  {
    "path": "src/io/dae/core/line.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_lines_h\n#define dae_lines_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkLines*\ndae_lines(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp,\n          AkLineMode            mode);\n\n#endif /* dae_lines_h */\n"
  },
  {
    "path": "src/io/dae/core/mesh.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"mesh.h\"\n#include \"source.h\"\n#include \"vert.h\"\n#include \"triangle.h\"\n#include \"poly.h\"\n#include \"line.h\"\n\nAK_INLINE\nvoid\ndae_mesh_appendPrim(AkMesh           * __restrict mesh,\n                    AkMeshPrimitive ** __restrict lastPrim,\n                    AkMeshPrimitive  * __restrict prim) {\n  prim->next = NULL;\n  prim->mesh = mesh;\n\n  if (*lastPrim) (*lastPrim)->next = prim;\n  else           mesh->primitive   = prim;\n\n  *lastPrim = prim;\n  mesh->primitiveCount++;\n}\n\nAK_HIDE\nAkObject*\ndae_mesh(DAEState   * __restrict dst,\n         xml_t      * __restrict xml,\n         AkGeometry * __restrict geom) {\n  AkObject        *obj;\n  AkMesh          *mesh;\n  AkMeshPrimitive *lastPrim;\n  AkHeap          *heap;\n  uint32_t         m;\n\n  heap     = dst->heap;\n  xml      = xml->val;\n  lastPrim = NULL;\n\n  obj  = ak_objAlloc(heap, geom, sizeof(*mesh), AK_GEOMETRY_MESH, true);\n  mesh = ak_objGet(obj);\n\n  mesh->geom         = geom;\n  mesh->convexHullOf = xmla_strdup_by(xml, heap, _s_dae_convex_hull_of, obj);\n\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_source)) {\n      (void)dae_source(dst, xml, NULL, 0);\n    } else if (xml_tag_eq(xml, _s_dae_vertices)) {\n      (void)dae_vert(dst, xml, dst->tempmem);\n    } else if ((xml_tag_eq(xml, _s_dae_triangles) & (m = AK_TRIANGLES))\n            || (xml_tag_eq(xml, _s_dae_trifans)   & (m = AK_TRIANGLE_FAN))\n            || (xml_tag_eq(xml, _s_dae_tristrips) & (m = AK_TRIANGLE_STRIP))) {\n      AkTriangles *tri;\n      \n      if ((tri = dae_triangles(dst, xml, obj, m))) {\n        dae_mesh_appendPrim(mesh, &lastPrim, &tri->base);\n\n        if (tri->base.bindmaterial)\n          ak_meshSetMaterial(&tri->base, tri->base.bindmaterial);\n      }\n    } else if ((xml_tag_eq(xml, _s_dae_polygons) & (m = AK_POLY_POLYGONS))\n            || (xml_tag_eq(xml, _s_dae_polylist) & (m = AK_POLY_POLYLIST))) {\n      AkPolygon *poly;\n\n      if ((poly = dae_poly(dst, xml, obj, m))) {\n        dae_mesh_appendPrim(mesh, &lastPrim, &poly->base);\n\n        if (poly->base.bindmaterial)\n          ak_meshSetMaterial(&poly->base, poly->base.bindmaterial);\n      }\n      \n    } else if (xml_tag_eq(xml, _s_dae_lines)      & (m = AK_LINES)\n           || (xml_tag_eq(xml, _s_dae_linestrips) & (m = AK_LINE_STRIP))) {\n      AkLines *lines;\n      \n      if ((lines = dae_lines(dst, xml, obj, m))) {\n        dae_mesh_appendPrim(mesh, &lastPrim, &lines->base);\n\n        if (lines->base.bindmaterial)\n          ak_meshSetMaterial(&lines->base, lines->base.bindmaterial);\n      }\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      mesh->extra = tree_fromxml(heap, obj, xml);\n    }\n\n    xml = xml->next;\n  }\n\n  return obj;\n}\n"
  },
  {
    "path": "src/io/dae/core/mesh.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_mesh_h\n#define dae_mesh_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkObject*\ndae_mesh(DAEState   * __restrict dst,\n         xml_t      * __restrict xml,\n         AkGeometry * __restrict geom);\n\n#endif /* dae_mesh_h */\n"
  },
  {
    "path": "src/io/dae/core/morph.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"morph.h\"\n#include \"source.h\"\n#include \"enum.h\"\n#include \"../../../array.h\"\n\nAK_HIDE\nAkMorph*\ndae_morph(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp) {\n  AkHeap     *heap;\n  AkMorph    *morph;\n  AkMorphDAE *morphdae;\n  xml_attr_t *att;\n\n  heap     = dst->heap;\n  morph    = ak_heap_calloc(heap, memp, sizeof(*morph));\n  morphdae = ak_heap_calloc(heap, morph, sizeof(*morphdae));\n\n  ak_heap_setUserData(heap, morph, morphdae);\n  flist_sp_insert(&dst->linkedUserData, morph);\n\n  url_set(dst, xml, _s_dae_source, memp, &morphdae->baseGeom);\n\n  if ((att = xmla(xml, _s_dae_method)))\n    morph->method = dae_morphMethod(att);\n  else\n    morph->method = AK_MORPH_METHOD_NORMALIZED;\n  \n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_source)) {\n      AkSource *source;\n      if ((source = dae_source(dst, xml, NULL, 0))) {\n        source->next     = morphdae->source;\n        morphdae->source = source;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_targets)) {\n      AkInput   *inp;\n      xml_t     *xtarg;\n      \n      xtarg = xml->val;\n      \n      while (xtarg) {\n        if (xml_tag_eq(xtarg, _s_dae_input)) {\n          inp              = ak_heap_calloc(heap, morphdae, sizeof(*inp));\n          inp->semanticRaw = xmla_strdup_by(xtarg, heap, _s_dae_semantic, inp);\n          \n          if (!inp->semanticRaw) {\n            ak_free(inp);\n          } else {\n            AkURL *url;\n            AkEnum inputSemantic;\n            \n            inputSemantic = dae_semantic(inp->semanticRaw);\n            \n            if (inputSemantic < 0)\n              inputSemantic = AK_INPUT_OTHER;\n            \n            inp->semantic = inputSemantic;\n            inp->offset   = xmla_u32(xmla(xtarg, _s_dae_offset), 0);\n            \n            inp->semantic = dae_semantic(inp->semanticRaw);\n            \n            url           = url_from(xtarg, _s_dae_source, memp);\n            rb_insert(dst->inputmap, inp, url);\n\n            inp->next       = morphdae->input;\n            morphdae->input = inp;\n          }\n        }\n        xtarg = xtarg->next;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      morphdae->extra = tree_fromxml(heap, morphdae, xml);\n    }\n    xml = xml->next;\n  }\n\n  return morph;\n}\n"
  },
  {
    "path": "src/io/dae/core/morph.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_morph_h\n#define dae_morph_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkMorph*\ndae_morph(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp);\n\n#endif /* dae_morph_h */\n"
  },
  {
    "path": "src/io/dae/core/node.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"node.h\"\n#include \"enum.h\"\n\n#include \"../../../array.h\"\n#include \"../core/asset.h\"\n#include \"../fx/mat.h\"\n#include \"../fixup/node.h\"\n\n#include <cglm/cglm.h>\n\nAK_HIDE\nvoid*\ndae_node2(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp) {\n  return dae_node(dst, xml, memp, NULL);\n}\n\nAK_HIDE\nAkNode*\ndae_node(DAEState      * __restrict dst,\n         xml_t         * __restrict xml,\n         void          *            memp,\n         AkVisualScene *            scene) {\n  AkHeap      *heap;\n  AkNode      *node;\n  const xml_t *sval;\n  xml_attr_t  *att;\n  AkObject    *last_trans;\n\n  heap = dst->heap;\n  node = ak_heap_calloc(heap, memp, sizeof(*node));\n  ak_setypeid(node, AKT_NODE);\n  node->visible = true;\n\n  xmla_setid(xml, heap, node);\n  sid_set(xml, heap, node);\n  \n  node->name     = xmla_strdup_by(xml, heap, _s_dae_name, node);\n  node->nodeType = dae_nodeType(xmla(xml, _s_dae_type));\n  if (node->nodeType < 1)\n    node->nodeType = AK_NODE_TYPE_NODE;\n\n  if ((att = xmla(xml, _s_dae_layer))) {\n    AkStringArray *strArray;\n    AkResult       ret;\n    char          *layer;\n\n    layer = xmla_strdup(att, heap, node);\n    ret   = layer\n            ? ak_strtostr_array(heap, node, layer, ' ', &strArray)\n            : AK_ERR;\n    if (ret == AK_OK)\n      node->layer = strArray;\n  }\n\n  last_trans = NULL;\n  xml        = xml->val;\n\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, node, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_lookat) && (sval = xmls(xml))) {\n      AkObject *obj;\n      AkLookAt *looakAt;\n      \n      obj     = ak_objAlloc(heap, node, sizeof(*looakAt), AKT_LOOKAT, true);\n      looakAt = ak_objGet(obj);\n      \n      sid_set(xml, heap, obj);\n      xml_strtof_fast(sval, (float *)looakAt->val, 9);\n      \n      if (!node->transform)\n        node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform));\n      \n      AK_APPEND_FLINK(node->transform->item, last_trans, obj);\n    } else if (xml_tag_eq(xml, _s_dae_matrix) && (sval = xmls(xml))) {\n      mat4      transform;\n      AkObject *obj;\n      AkMatrix *matrix;\n\n      obj    = ak_objAlloc(heap, node, sizeof(*matrix), AKT_MATRIX, true);\n      matrix = ak_objGet(obj);\n\n      sid_set(xml, heap, obj);\n      xml_strtof_fast(sval, transform[0], 16);\n      glm_mat4_transpose_to(transform, matrix->val);\n  \n      if (!node->transform)\n        node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform));\n\n      AK_APPEND_FLINK(node->transform->item, last_trans, obj);\n    } else if (xml_tag_eq(xml, _s_dae_rotate) && (sval = xmls(xml))) {\n      AkObject *obj;\n      AkRotate *rotate;\n      \n      obj    = ak_objAlloc(heap, node, sizeof(*rotate), AKT_ROTATE, true);\n      rotate = ak_objGet(obj);\n      \n      sid_set(xml, heap, obj);\n      xml_strtof_fast(sval, (AkFloat *)rotate->val, 4);\n      glm_make_rad(&rotate->val[3]);\n      \n      if (!node->transform)\n        node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform));\n      \n      AK_APPEND_FLINK(node->transform->item, last_trans, obj);\n    } else if (xml_tag_eq(xml, _s_dae_scale) && (sval = xmls(xml))) {\n      AkObject *obj;\n      AkScale  *scale;\n      \n      obj   = ak_objAlloc(heap, node, sizeof(*scale), AKT_SCALE, true);\n      scale = ak_objGet(obj);\n      \n      sid_set(xml, heap, obj);\n      xml_strtof_fast(sval, (AkFloat *)scale->val, 3);\n      \n      if (!node->transform)\n        node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform));\n      \n      AK_APPEND_FLINK(node->transform->item, last_trans, obj);\n    } else if (xml_tag_eq(xml, _s_dae_skew) && (sval = xmls(xml))) {\n      AkObject *obj;\n      AkSkew   *skew;\n      AkFloat   tmp[7];\n      \n      obj  = ak_objAlloc(heap, node, sizeof(*skew), AKT_SKEW, true);\n      skew = ak_objGet(obj);\n      \n      sid_set(xml, heap, obj);\n      xml_strtof_fast(sval, (AkFloat *)tmp, 4);\n      \n      /* COLLADA uses degree here, convert it to radians */\n      skew->angle = glm_rad(tmp[0]);\n      glm_vec3_copy(&tmp[1], skew->rotateAxis);\n      glm_vec3_copy(&tmp[4], skew->aroundAxis);\n      \n      if (!node->transform)\n        node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform));\n      \n      AK_APPEND_FLINK(node->transform->item, last_trans, obj);\n    } else if (xml_tag_eq(xml, _s_dae_translate) && (sval = xmls(xml))) {\n      AkObject    *obj;\n      AkTranslate *transl;\n      \n      obj    = ak_objAlloc(heap, node, sizeof(*transl), AKT_TRANSLATE, true);\n      transl = ak_objGet(obj);\n      \n      sid_set(xml, heap, obj);\n      xml_strtof_fast(sval, (AkFloat *)transl->val, 4);\n      \n      if (!node->transform)\n        node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform));\n      \n      AK_APPEND_FLINK(node->transform->item, last_trans, obj);\n    } else if (xml_tag_eq(xml, _s_dae_instance_camera)) {\n      AkInstanceBase *instcam;\n\n      instcam       = ak_heap_calloc(heap, node, sizeof(*instcam));\n      instcam->type = AK_INSTANCE_CAMERA;\n      instcam->name = xmla_strdup_by(xml, heap, _s_dae_name, instcam);\n      url_set(dst, xml, _s_dae_url, instcam, &instcam->url);\n      \n      instcam->node = node;\n      \n      instcam->next = node->camera;\n      node->camera  = instcam;\n\n      instcam->prev = node->camera;\n      if (node->camera)\n        node->camera->prev = instcam;\n      \n      if (scene) {\n        if (!scene->firstCamNode)\n          scene->firstCamNode = node;\n        \n        if (instcam)\n          ak_instanceListAdd(scene->cameras, instcam);\n      }\n    } else if (xml_tag_eq(xml, _s_dae_instance_controller)) {\n      AkInstanceController *instctl;\n      xml_t                *xinstctl;\n\n      instctl            = ak_heap_calloc(heap, node, sizeof(*instctl));\n      instctl->base.type = AK_INSTANCE_CONTROLLER;\n      instctl->base.name = xmla_strdup_by(xml, heap, _s_dae_name, instctl);\n      url_set(dst, xml, _s_dae_url, instctl, &instctl->base.url);\n      \n      xinstctl           = xml->val;\n      while (xinstctl) {\n        if (xml_tag_eq(xinstctl, _s_dae_skeleton)) {\n          char *skel;\n          if ((skel = xml_strdup(xinstctl, heap, instctl)))\n            flist_sp_insert(&instctl->reserved, skel);\n        } else if (xml_tag_eq(xinstctl, _s_dae_bind_material)) {\n          instctl->bindMaterial = dae_bindMaterial(dst, xinstctl, instctl);\n        } else if (xml_tag_eq(xinstctl, _s_dae_extra)) {\n          instctl->base.extra = tree_fromxml(heap, instctl, xinstctl);\n        }\n        xinstctl = xinstctl->next;\n      }\n\n      instctl->base.node = node;\n      flist_sp_insert(&dst->instCtlrs, instctl);;\n    } else if (xml_tag_eq(xml, _s_dae_instance_geometry)) {\n      AkInstanceGeometry *instgeo;\n      xml_t              *xinstgeo;\n\n      instgeo            = ak_heap_calloc(heap, node, sizeof(*instgeo));\n      instgeo->base.type = AK_INSTANCE_GEOMETRY;\n      instgeo->base.name = xmla_strdup_by(xml, heap, _s_dae_name, instgeo);\n      url_set(dst, xml, _s_dae_url, instgeo, &instgeo->base.url);\n\n      xinstgeo           = xml->val;\n      while (xinstgeo) {\n        if (xml_tag_eq(xinstgeo, _s_dae_bind_material)) {\n          instgeo->bindMaterial = dae_bindMaterial(dst, xinstgeo, instgeo);\n        } else if (xml_tag_eq(xinstgeo, _s_dae_extra)) {\n          instgeo->base.extra = tree_fromxml(heap, instgeo, xinstgeo);\n        }\n        xinstgeo = xinstgeo->next;\n      }\n\n      instgeo->base.node = node;\n\n      instgeo->base.next = (void *)node->geometry;\n      node->geometry     = instgeo;\n    } else if (xml_tag_eq(xml, _s_dae_instance_light)) {\n      AkInstanceBase *instlight;\n      \n      instlight       = ak_heap_calloc(heap, node, sizeof(*instlight));\n      instlight->type = AK_INSTANCE_LIGHT;\n      instlight->name = xmla_strdup_by(xml, heap, _s_dae_name, instlight);\n      url_set(dst, xml, _s_dae_url, instlight, &instlight->url);\n      \n      instlight->node = node;\n      \n      instlight->next = node->light;\n      node->light     = instlight;\n      \n      instlight->prev = node->light;\n      if (node->light)\n        node->light->prev = instlight;\n      \n      if (scene && instlight) {\n        AkLight *lightObject;\n        lightObject = ak_instanceObject(instlight);\n        if (lightObject)\n          ak_instanceListAdd(scene->lights, instlight);\n      }\n    } else if (xml_tag_eq(xml, _s_dae_instance_node)) {\n      AkInstanceNode *instnode;\n      \n      instnode            = ak_heap_calloc(heap, node, sizeof(*instnode));\n      instnode->base.type = AK_INSTANCE_NODE;\n      instnode->base.name = xmla_strdup_by(xml, heap, _s_dae_name,  instnode);\n      instnode->proxy     = xmla_strdup_by(xml, heap, _s_dae_proxy, instnode);\n      url_set(dst, xml, _s_dae_url, instnode, &instnode->base.url);\n      \n      if (node->node)\n        instnode->base.next = &node->node->base;\n\n      instnode->base.node = node;\n      node->node          = instnode;\n      \n      if (node->node) {\n        instnode->base.prev   = &node->node->base;\n        node->node->base.prev = &instnode->base;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_node)) {\n      AkNode *subNode;\n      \n      if ((subNode = dae_node(dst, xml, node, scene))) {\n        if (node->chld) {\n          node->chld->prev = subNode;\n        }\n\n        subNode->next = node->chld;\n        node->chld    = subNode;\n      }\n\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      node->extra = tree_fromxml(heap, node, xml);\n    }\n    xml = xml->next;\n  }\n  \n  if (ak_isKindOf(memp, node))\n    node->parent = memp;\n\n  dae_nodeFixup(heap, node);\n\n  return node;\n}\n"
  },
  {
    "path": "src/io/dae/core/node.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_node_h\n#define dae_node_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid*\ndae_node2(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp);\n\nAK_HIDE\nAkNode*\ndae_node(DAEState      * __restrict dst,\n         xml_t         * __restrict xml,\n         void          *            memp,\n         AkVisualScene *            scene);\n\n#endif /* dae_node_h */\n"
  },
  {
    "path": "src/io/dae/core/param.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"param.h\"\n#include \"value.h\"\n\nAK_HIDE\nAkNewParam*\ndae_newparam(DAEState * __restrict dst,\n             xml_t    * __restrict xml,\n             void     * __restrict memp) {\n  AkNewParam   *newparam;\n\n  newparam = ak_heap_calloc(dst->heap, memp, sizeof(*newparam));\n  ak_setypeid(newparam, AKT_NEWPARAM);\n  \n  sid_set(xml, dst->heap, newparam);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_semantic)) {\n      \n    } else {\n      /* load once */\n      if (!newparam->val) {\n        newparam->val = dae_value(dst, xml, newparam);\n      }\n    }\n    xml = xml->next;\n  }\n\n  return newparam;\n}\n\nAK_HIDE\nAkParam*\ndae_param(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp) {\n  AkParam *param;\n\n  param = ak_heap_calloc(dst->heap, memp, sizeof(*param));\n  ak_setypeid(param, AKT_PARAM);\n\n  param->ref = xmla_strdup_by(xml, dst->heap, _s_dae_ref, param);\n\n  return param;\n}\n"
  },
  {
    "path": "src/io/dae/core/param.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_param_h\n#define dae_param_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkNewParam*\ndae_newparam(DAEState * __restrict dst,\n             xml_t    * __restrict xml,\n             void     * __restrict memp);\n\nAK_HIDE\nAkParam*\ndae_param(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp);\n\n#endif /* dae_param_h */\n"
  },
  {
    "path": "src/io/dae/core/poly.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"poly.h\"\n#include \"enum.h\"\n#include \"../../../array.h\"\n#include \"../../../data.h\"\n\nAK_HIDE\nAkPolygon*\ndae_poly(DAEState * __restrict dst,\n         xml_t    * __restrict xml,\n         void     * __restrict memp,\n         AkPolygonMode         mode) {\n  AkPolygon *poly;\n  FListItem *polyi;\n  AkHeap    *heap;\n  uint32_t   indexoff, polygonsCount, st;\n  size_t     indicesCount;\n  \n  heap = dst->heap;\n  poly = ak_heap_calloc(heap, memp, sizeof(*poly));\n  \n  poly->haveHoles         = false;\n  poly->base.type         = AK_PRIMITIVE_POLYGONS;\n\n  poly->base.name         = xmla_strdup_by(xml, heap, _s_dae_name, poly);\n  poly->base.bindmaterial = xmla_strdup_by(xml, heap, _s_dae_material, poly);\n  poly->base.nPolygons    = xmla_u32(xmla(xml, _s_dae_count), 0);\n\n  polyi         = NULL;\n  indexoff      = 0;\n  polygonsCount = 0;\n  indicesCount  = 0;\n  \n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_input)) {\n      AkInput *inp;\n\n      inp              = ak_heap_calloc(heap, poly, sizeof(*inp));\n      inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp);\n\n      if (!inp->semanticRaw) {\n        ak_free(inp);\n      } else {\n        AkURL *url;\n        AkEnum inputSemantic;\n\n        inputSemantic = dae_semantic(inp->semanticRaw);\n\n        if (inputSemantic < 0)\n          inputSemantic = AK_INPUT_OTHER;\n\n        inp->semantic = inputSemantic;\n        inp->offset   = xmla_u32(xmla(xml, _s_dae_offset), 0);\n        inp->set      = xmla_u32(xmla(xml, _s_dae_set),    0);\n\n        url = url_from(xml, _s_dae_source, memp);\n        rb_insert(dst->inputmap, inp, url);\n\n        if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) {\n          inp->semantic    = dae_semantic(inp->semanticRaw);\n          inp->next        = poly->base.input;\n          poly->base.input = inp;\n          poly->base.inputCount++;\n\n          if (inp->offset > indexoff)\n            indexoff = inp->offset;\n        } else {\n          dae_vertmap_add(dst, inp, &poly->base);\n          /* don't store VERTEX because it will be duplicated to all prims */\n          // poly->base.reserved1 = inp->offset;\n          // poly->base.reserved2 = inp->set;\n          // ak_free(inp);\n        }\n      }\n    } else if (xml_tag_eq(xml, _s_dae_p) && xml->val) {\n      AkUIntArray *intArray;\n      \n      if ((xml_strtoui_array(heap, poly, xml->val, &intArray) == AK_OK)) {\n        if (mode == AK_POLY_POLYLIST) {\n          poly->base.indices = intArray;\n        } else if (mode == AK_POLY_POLYGONS) {\n          /* TODO: do this for POLYLIST if vcount not exists */\n          flist_sp_insert(&polyi, intArray);\n          polygonsCount++;\n          indicesCount += intArray->count;\n        }\n      }\n    } else if (xml_tag_eq(xml, _s_dae_vcount) && xml->val) {\n      AkUIntArray *intArray;\n      if ((xml_strtoui_array(heap, poly, xml->val, &intArray) == AK_OK)) {\n        poly->vcount = intArray;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      poly->base.extra = tree_fromxml(heap, poly, xml);\n    }\n    xml = xml->next;\n  }\n\n  poly->base.indexStride = st = indexoff + 1;\n  \n  if (mode == AK_POLY_POLYGONS) {\n    FListItem   *p;\n    AkUIntArray *indices, *vcount;\n    AkUInt      *pIndices, *pVcount;\n\n    /* alloc indices array */\n    indices = ak_heap_alloc(heap,\n                            poly,\n                            sizeof(*indices) + sizeof(AkUInt) * indicesCount);\n    indices->count = indicesCount;\n\n    /* alloc vcount */\n    vcount = ak_heap_alloc(heap,\n                           poly,\n                           sizeof(*vcount) + sizeof(AkUInt) * polygonsCount);\n    vcount->count = polygonsCount;\n\n    pIndices = indices->items;\n    pVcount  = vcount->items;\n\n    p = polyi;\n    while (p) {\n      AkUIntArray *intArray;\n\n      intArray = p->data;\n\n      memcpy(pIndices, intArray->items, sizeof(AkUInt) * intArray->count);\n\n      *pVcount++ = (AkUInt)intArray->count / st;\n      pIndices  += intArray->count;\n\n      ak_free(intArray);\n\n      p = p->next;\n    }\n\n    poly->base.indices = indices;\n    poly->vcount       = vcount;\n\n    flist_sp_destroy(&polyi);\n  }\n\n  return poly;\n}\n"
  },
  {
    "path": "src/io/dae/core/poly.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_polygons_h\n#define dae_polygons_h\n\n#include \"../common.h\"\n\ntypedef enum AkPolygonMode {\n  AK_POLY_POLYLIST = 1,\n  AK_POLY_POLYGONS = 3\n} AkPolygonMode;\n\nAK_HIDE\nAkPolygon*\ndae_poly(DAEState * __restrict dst,\n         xml_t    * __restrict xml,\n         void     * __restrict memp,\n         AkPolygonMode         mode);\n\n#endif /* dae_polygons_h */\n"
  },
  {
    "path": "src/io/dae/core/scene.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"scene.h\"\n#include \"node.h\"\n#include \"../core/asset.h\"\n#include \"../core/asset.h\"\n#include \"../fx/mat.h\"\n#include \"../../../array.h\"\n#include \"../../../../include/ak/light.h\"\n\nstatic\nAkEvaluateScene*\ndae_evalScene(DAEState * __restrict dst,\n              xml_t    * __restrict xml,\n              void     * __restrict memp);\n\nAK_HIDE\nvoid*\ndae_vscene(DAEState * __restrict dst,\n           xml_t    * __restrict xml,\n           void     * __restrict memp) {\n  AkHeap        *heap;\n  AkVisualScene *vscn;\n\n  heap = dst->heap;\n  vscn = ak_heap_calloc(heap, memp, sizeof(*vscn));\n\n  ak_setypeid(vscn, AKT_SCENE);\n  xmla_setid(xml, heap, vscn);\n\n  vscn->name    = xmla_strdup_by(xml, heap, _s_dae_name, vscn);\n  vscn->cameras = ak_heap_calloc(heap, vscn, sizeof(*vscn->cameras));\n  vscn->lights  = ak_heap_calloc(heap, vscn, sizeof(*vscn->lights));\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, vscn, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_node)) {\n      AkNode *node;\n\n      if ((node = dae_node(dst, xml, vscn, vscn))) {\n        node->next = vscn->node;\n        vscn->node = node;\n        \n        if (vscn->node) {\n          vscn->node->prev = node;\n        }\n      }\n    } else if (xml_tag_eq(xml, _s_dae_evaluate_scene)) {\n      AkEvaluateScene *evalScene;\n\n      if ((evalScene = dae_evalScene(dst, xml, vscn))) {\n        vscn->evaluateScene = evalScene;\n        evalScene->next     = vscn->evaluateScene;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      vscn->extra = tree_fromxml(heap, vscn, xml);\n    }\n    xml = xml->next;\n  }\n\n  if (vscn->lights->count < 1\n      && ak_opt_get(AK_OPT_ADD_DEFAULT_LIGHT)) {\n    AkLight *light;\n    AkNode  *rootNode;\n    rootNode = vscn->node;\n    if (rootNode) {\n      AkHeap         *heap;\n      AkDoc          *doc;\n      AkInstanceBase *lightInst;\n\n      heap  = ak_heap_getheap(rootNode);\n      doc   = ak_heap_data(heap);\n      light = ak_defaultLight(rootNode);\n\n      lightInst = ak_instanceMake(heap, rootNode, light);\n      ak_instanceListEmpty(vscn->lights);\n      ak_instanceListAdd(vscn->lights, lightInst);\n\n      lightInst->next = rootNode->light;\n      rootNode->light = lightInst;\n\n      ak_libAddLight(doc, light);\n    }\n  }\n\n  return vscn;\n}\n\nAK_HIDE\nAkInstanceBase*\ndae_instVisualScene(DAEState * __restrict dst,\n                    xml_t    * __restrict xml,\n                    void     * __restrict memp) {\n  AkHeap         *heap;\n  AkInstanceBase *visualScene;\n  \n  heap        = dst->heap;\n  visualScene = ak_heap_calloc(heap, memp, sizeof(*visualScene));\n\n  sid_set(xml, heap, visualScene);\n  visualScene->name = xmla_strdup_by(xml, heap, _s_dae_name, visualScene);\n\n  url_set(dst, xml, _s_dae_url, visualScene, &visualScene->url);\n\n  return visualScene;\n}\n\nstatic\nAkEvaluateScene*\ndae_evalScene(DAEState * __restrict dst,\n              xml_t    * __restrict xml,\n              void     * __restrict memParent) {\n  AkEvaluateScene *evalScene;\n  AkHeap          *heap;\n\n  heap = dst->heap;\n  xml  = xml->val;\n\n  evalScene = ak_heap_calloc(heap, memParent, sizeof(*evalScene));\n  xmla_setid(xml, heap, evalScene);\n  sid_set(xml, heap, evalScene);\n  \n  evalScene->name   = xmla_strdup_by(xml, heap, _s_dae_name, evalScene);\n  evalScene->enable = xmla_bool(xmla(xml, _s_dae_enable), 0);\n  \n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n       (void)dae_asset(dst, xml, evalScene, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_render)) {\n      AkRender *ren;\n      xml_t    *xren;\n      \n      ren = ak_heap_calloc(heap, evalScene, sizeof(*ren));\n      sid_set(xml, heap, ren);\n      \n      ren->name       = xmla_strdup_by(xml, heap, _s_dae_name, ren);\n      ren->cameraNode = xmla_strdup_by(xml, heap, _s_dae_camera_node, ren);\n      \n      xren = xml->val;\n      while (xren) {\n        if (xml_tag_eq(xren, _s_dae_layer) && xren->val) {\n          AkStringArrayL *layer;\n          char           *contents;\n          AkResult        ret;\n          \n          contents                = xren->val;\n          contents[xren->valsize] = '\\0';\n          \n          /* TODO: */\n          ret = ak_strtostr_arrayL(heap, ren, contents, ' ', &layer);\n          if (ret == AK_OK) {\n            layer->next = ren->layer;\n            ren->layer  = layer;\n          }\n        } else if (xml_tag_eq(xren, _s_dae_instance_material)) {\n          AkInstanceMaterial *instmat;\n        \n          if ((instmat = dae_instMaterial(dst, xml, ren))) {\n            if (ren->instanceMaterial) {\n              ren->instanceMaterial->base.prev = &instmat->base;\n              instmat->base.next               = &ren->instanceMaterial->base;\n            }\n            \n            ren->instanceMaterial = instmat;\n          }\n        } else if (xml_tag_eq(xren, _s_dae_extra)) {\n           ren->extra = tree_fromxml(heap, ren, xml);\n        }\n\n        xren = xren->next;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      evalScene->extra = tree_fromxml(heap, evalScene, xml);\n    }\n    xml = xml->next;\n  }\n\n  return evalScene;\n}\n\nAK_HIDE\nvoid\ndae_scene(DAEState * __restrict dst,\n          xml_t    * __restrict xml) {\n  AkDoc    *doc;\n  AkHeap   *heap;\n\n  heap = dst->heap;\n  doc  = dst->doc;\n  xml  = xml->val;\n\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_instance_visual_scene)) {\n      doc->scene.visualScene = dae_instVisualScene(dst, xml, doc);\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      doc->scene.extra = tree_fromxml(heap, doc, xml);\n    }\n    xml = xml->next;\n  }\n}\n"
  },
  {
    "path": "src/io/dae/core/scene.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_scene_h\n#define dae_scene_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid*\ndae_vscene(DAEState * __restrict dst,\n           xml_t    * __restrict xml,\n           void     * __restrict memp);\n\nAK_HIDE\nAkInstanceBase*\ndae_instVisualScene(DAEState * __restrict dst,\n                    xml_t    * __restrict xml,\n                    void     * __restrict memp);\n\nAK_HIDE\nvoid\ndae_scene(DAEState * __restrict dst,\n          xml_t    * __restrict xml);\n\n#endif /* dae_scene_h */\n"
  },
  {
    "path": "src/io/dae/core/skin.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"skin.h\"\n#include \"../../../array.h\"\n#include \"source.h\"\n#include \"enum.h\"\n\nAK_HIDE\nAkSkin*\ndae_skin(DAEState * __restrict dst,\n         xml_t    * __restrict xml,\n         void     * __restrict memp) {\n  AkHeap      *heap;\n  AkSkin      *skin;\n  AkSkinDAE   *skindae;\n  const xml_t *sval;\n  bool         foundBindShape;\n\n  heap           = dst->heap;\n  skin           = ak_heap_calloc(heap, memp, sizeof(*skin));\n  skindae        = ak_heap_calloc(heap, skin, sizeof(*skindae));\n  foundBindShape = false;\n  \n  ak_heap_setUserData(heap, skin, skindae);\n  flist_sp_insert(&dst->linkedUserData, skin);\n\n  url_set(dst, xml, _s_dae_source, memp, &skindae->baseGeom);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_bind_shape_matrix) && (sval = xmls(xml))) {\n      xml_strtof_fast(sval, skin->bindShapeMatrix[0], 16);\n      glm_mat4_transpose(skin->bindShapeMatrix);\n      foundBindShape = true;\n    } else if (xml_tag_eq(xml, _s_dae_source)) {\n      AkSource *source;\n      if ((source = dae_source(dst, xml, NULL, 0))) {\n        source->next    = skindae->source;\n        skindae->source = source;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_joints)) {\n      AkInput *inp;\n      xml_t   *xjoints;\n      \n      xjoints = xml->val;\n      while (xjoints) {\n        if (xml_tag_eq(xjoints, _s_dae_input)) {\n          inp              = ak_heap_calloc(heap, skin, sizeof(*inp));\n          inp->semanticRaw = xmla_strdup_by(xjoints, heap, _s_dae_semantic, inp);\n          \n          if (!inp->semanticRaw) {\n            ak_free(inp);\n          } else {\n            AkURL *url;\n            AkEnum  inputSemantic;\n            \n            inputSemantic = dae_semantic(inp->semanticRaw);\n            \n            if (inputSemantic < 0)\n              inputSemantic = AK_INPUT_OTHER;\n            \n            inp->semantic = inputSemantic;\n            inp->offset   = xmla_u32(xmla(xjoints, _s_dae_offset), 0);\n            \n            inp->semantic = dae_semantic(inp->semanticRaw);\n            \n            url           = url_from(xjoints, _s_dae_source, memp);\n            \n            if (inputSemantic == AK_INPUT_JOINT) {\n              skindae->joints.joints = inp;\n              rb_insert(dst->inputmap, inp, url);\n            } else if (inputSemantic == AK_INPUT_INV_BIND_MATRIX) {\n              skindae->joints.invBindMatrix = inp;\n              rb_insert(dst->inputmap, inp, url);\n            } else {\n              /* do not support other inputs until needed,\n               probably will not. */\n              ak_free(inp);\n            }\n          }\n        }\n        xjoints = xjoints->next;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_vertex_weights)) {\n      AkBoneWeights *wei;\n      AkInput       *inp;\n      xml_t         *xwei;\n      \n      wei  = ak_heap_calloc(heap, skin, sizeof(*wei));\n      xwei = xml->val;\n\n      while (xwei) {\n        if (xml_tag_eq(xwei, _s_dae_input)) {\n          inp              = ak_heap_calloc(heap, skin, sizeof(*inp));\n          inp->semanticRaw = xmla_strdup_by(xwei, heap, _s_dae_semantic, inp);\n          \n          if (!inp->semanticRaw) {\n            ak_free(inp);\n          } else {\n            AkURL *url;\n            AkEnum  inputSemantic;\n            \n            inputSemantic = dae_semantic(inp->semanticRaw);\n            \n            if (inputSemantic < 0)\n              inputSemantic = AK_INPUT_OTHER;\n            \n            inp->semantic = inputSemantic;\n            inp->offset   = xmla_u32(xmla(xwei, _s_dae_offset), 0);\n            \n            inp->semantic = dae_semantic(inp->semanticRaw);\n            \n            url           = url_from(xwei, _s_dae_source, memp);\n            \n            if (inputSemantic == AK_INPUT_JOINT) {\n              skindae->weights.joints = inp;\n              rb_insert(dst->inputmap, inp, url);\n            } else if (inputSemantic == AK_INPUT_WEIGHT) {\n              skindae->weights.weights = inp;\n              rb_insert(dst->inputmap, inp, url);\n            } else {\n              /* do not support other inputs until needed,\n               probably will not. */\n              ak_free(inp);\n            }\n            \n            skindae->inputCount++;\n          }\n        } else if (xml_tag_eq(xwei, _s_dae_vcount) && (sval = xmls(xwei))) {\n          size_t    count, sz, i;\n          uint32_t *pSum, *pCount, next;\n          \n          if ((count = xml_strtok_count_fast(sval, NULL)) > 0) {\n            sz = count * sizeof(uint32_t);\n            \n            /*\n             \n             we use same temp array to store sum of counts to avoid to\n             create new pointer. Array layout:\n             \n             | pCount | pCountSum |\n             \n             */\n            \n            wei->counts  = pCount = ak_heap_alloc(heap, wei, 2 * sz);\n            \n            /* must equal to position count, we may fix this in postscript */\n            wei->nVertex = count;\n            \n            xml_strtoui_fast(sval, pCount, (unsigned long)count);\n            \n            /* calculate sum */\n            pSum = wei->counts + count;\n            for (i = next = 0; i < count; i++) {\n              pSum[i] = next;\n              next    = pCount[i] + next;\n            }\n          }\n        } else if (xml_tag_eq(xwei, _s_dae_v) && (sval = xmls(xwei))) {\n          AkUIntArray *intArray;\n          AkResult     ret;\n          \n          ret = xml_strtoui_array(heap, wei, sval, &intArray);\n          if (ret == AK_OK)\n            skindae->weights.v = intArray;\n        }\n        \n        /*\n        else if (xml_tag_eq(xwei, _s_dae_extra)) {\n          wei->extra = tree_fromxml(heap, wei, xwei);\n        }\n        */\n        xwei = xwei->next;\n      }\n      \n       skin->weights = (void *)wei;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      skindae->extra = tree_fromxml(heap, skindae, xml);\n    }\n    xml = xml->next;\n  }\n\n  if (!foundBindShape) {\n    glm_mat4_identity(skin->bindShapeMatrix);\n  }\n\n  return skin;\n}\n"
  },
  {
    "path": "src/io/dae/core/skin.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_skin_h\n#define dae_skin_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkSkin*\ndae_skin(DAEState * __restrict dst,\n         xml_t    * __restrict xml,\n         void     * __restrict memp);\n\n#endif /* dae_skin_h */\n"
  },
  {
    "path": "src/io/dae/core/source.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"source.h\"\n#include \"../core/asset.h\"\n#include \"../core/techn.h\"\n#include \"../core/enum.h\"\n#include \"../core/value.h\"\n\nAK_HIDE\nAkSource*\ndae_source(DAEState * __restrict dst,\n           xml_t    * __restrict xml,\n           AkEnum              (*asEnum)(const char *name, size_t nameLen),\n           AkTypeId              enumType) {\n  AkHeap        *heap;\n  AkSource      *source;\n  AkBuffer      *buffer;\n  AkTechnique   *tq;\n  AkAccessor    *acc;\n  AkAccessorDAE *accdae;\n  const xml_t   *sval;\n  void          *rootmemp, *tempmem;\n  uint32_t       count;\n  AkTypeId       t;\n  bool           isName;\n\n  heap     = dst->heap;\n  rootmemp = ak_heap_data(heap->data);\n  tempmem  = dst->tempmem;\n  isName   = false;\n  buffer   = NULL;\n  source   = ak_heap_calloc(heap, tempmem, sizeof(*source));\n  ak_setypeid(source, AKT_SOURCE);\n\n  xmla_setid(xml, heap, source);\n  source->name = xmla_strdup_by(xml, heap, _s_dae_name, source);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, source, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_techniquec)) {\n      xml_t       *xacc;\n      AkDataParam *dp_last;\n\n      if ((xacc = xml_elem(xml, _s_dae_accessor))) {\n        acc         = ak_heap_calloc(heap, rootmemp, sizeof(*acc));\n        accdae      = ak_heap_calloc(heap, tempmem,  sizeof(*accdae));\n        \n        ak_heap_setUserData(heap, acc, accdae);\n        \n        acc->count     = xmla_u32(xmla(xacc, _s_dae_count),  0);\n        accdae->offset = xmla_u32(xmla(xacc, _s_dae_offset), 0);\n        accdae->stride = xmla_u32(xmla(xacc, _s_dae_stride), 1);\n\n        ak_setypeid(acc, AKT_ACCESSOR);\n        url_set(dst, xacc, _s_dae_source, accdae, &accdae->source);\n\n        xacc    = xacc->val;\n        dp_last = NULL;\n\n        while (xacc) {\n          AkDataParam *dp;\n          \n          dp = ak_heap_calloc(heap, accdae, sizeof(*dp));\n          sid_set(xacc, heap, dp);\n\n          dp->name = xmla_strdup_by(xacc, heap, _s_dae_name, dp);\n          dae_dtype(xmla_strdup_by(xacc, heap, _s_dae_type, dp),  &dp->type);\n          \n          AK_APPEND_FLINK(accdae->param, dp_last, dp);\n          xacc = xacc->next;\n        }\n\n        source->tcommon = acc;\n\n        /* append accessor to global list */\n        /* this will be prepared in postprocess */\n        flist_sp_insert(&dst->accessors, acc);\n      }\n    } else if (xml_tag_eq(xml, _s_dae_technique)) {\n      tq                = dae_techn(xml, heap, source);\n      tq->next          = source->technique;\n      source->technique = tq;\n    } else if (xml_valtype(xml) == XML_STRING && (sval = xmls(xml))) {\n      count            = xmla_u32(xmla(xml, _s_dae_count), 0);\n      buffer           = ak_heap_alloc(heap, rootmemp, sizeof(*buffer));\n      buffer->name     = xmla_strdup_by(xml, heap, _s_dae_name, buffer);\n      source->buffer   = buffer;\n      \n      xmla_setid(xml, heap, buffer);\n      \n      if (xml_tag_eq(xml, _s_dae_float_array)) {\n        buffer->length = sizeof(float) * count;\n        buffer->data   = ak_heap_alloc(heap, buffer, buffer->length);\n        xml_strtof_fast(sval, buffer->data, count);\n        \n        ak_setUserData(buffer, (void *)(uintptr_t)AKT_FLOAT);\n      } else if (xml_tag_eq(xml, _s_dae_int_array)) {\n        buffer->length = sizeof(uint32_t) * count;\n        buffer->data   = ak_heap_alloc(heap, buffer, buffer->length);\n        xml_strtoi_fast(sval, buffer->data, count);\n        \n        ak_setUserData(buffer, (void *)(uintptr_t)AKT_INT);\n      } else if (xml_tag_eq(xml, _s_dae_bool_array)) {\n        buffer->length = sizeof(bool) * count;\n        buffer->data   = ak_heap_alloc(heap, buffer, buffer->length);\n        xml_strtob_fast(sval, buffer->data, count);\n        \n        ak_setUserData(buffer, (void *)(uintptr_t)AKT_BOOL);\n      } else if ((xml_tag_eq(xml, _s_dae_Name_array)   & (1|(t = AKT_NAME)))\n              || (xml_tag_eq(xml, _s_dae_IDREF_array)  & (1|(t = AKT_IDREF)))\n              || (xml_tag_eq(xml, _s_dae_SIDREF_array) & (1|(t = AKT_SIDREF)))\n              || (xml_tag_eq(xml, _s_dae_token_array)  & (1|(t = AKT_TOKEN)))) {\n        char        *pData, **iter, *tok, *tok_begin, *end, c;\n        const xml_t *v;\n        size_t       srclen, toklen, enumLen;\n        uint32_t     idx;\n        AkEnum       enumValue;\n\n        /*\n         |pSTR1|pSTR2|pSTR3|STR1\\0STR2\\0STR3|\n         \n         the last one is pointer to all data\n         */\n        \n        isName = true;\n        idx    = 0;\n\n        if (asEnum) {\n          ak_setUserData(buffer, (void *)(uintptr_t)enumType);\n          \n          enumLen        = ak_typeDesc(enumType)->size;\n          buffer->length = enumLen * count;\n          buffer->data   = ak_heap_alloc(heap, buffer, buffer->length);\n          pData          = buffer->data;\n\n          if ((v = sval) && (tok = v->val)) {\n            do {\n              if (idx >= count)\n                break;\n\n              srclen = v->valsize;\n              end    = tok + srclen;\n\n              do {\n                while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n                  tok++;\n                \n                tok_begin = tok;\n                \n                while (tok < end && !((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n                  tok++;\n                \n                toklen    = tok - tok_begin;\n                enumValue = asEnum(tok_begin, toklen);\n                memcpy(pData + idx * enumLen, &enumValue, enumLen);\n\n                idx++;\n              } while (idx < count && tok < end);\n            } while ((v = xmls_next(v)) && (tok = v->val));\n          }\n        } else {\n          ak_setUserData(buffer, (void *)(uintptr_t)t);\n\n          buffer->length = sizeof(char *) * count * 2\n                            + xmls_sumlen(sval) + 1 /* NULL */;\n          iter  = buffer->data = ak_heap_alloc(heap, buffer, buffer->length);\n          pData = (char *)buffer->data + sizeof(char *) * (count + 1);\n          \n          iter[count] = pData;\n\n          if ((v = sval) && (tok = v->val)) {\n            do {\n              if (idx >= count)\n                break;\n\n              srclen = v->valsize;\n              end    = tok + srclen;\n\n              do {\n                while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n                  tok++;\n                \n                tok_begin = tok;\n                \n                while (tok < end && !((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n                  tok++;\n\n                toklen = tok - tok_begin;\n                memcpy(pData, tok_begin, toklen);\n                iter[idx++] = pData;\n                \n                pData += toklen;\n                *pData++ = '\\0';\n              } while (idx < count && tok < end);\n            } while ((v = xmls_next(v)) && (tok = v->val));\n          }\n        } /* if asEnum */\n      }\n    }\n    \n    xml = xml->next;\n  }\n\n  if (source->tcommon\n      && isName\n      && asEnum\n      && (accdae = ak_userData(source->tcommon))) {\n\n    accdae->bound  = 1;\n    accdae->stride = 1;\n  }\n\n  return source;\n}\n"
  },
  {
    "path": "src/io/dae/core/source.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_source_h\n#define dae_source_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkSource*\ndae_source(DAEState * __restrict dst,\n           xml_t    * __restrict xml,\n           AkEnum              (*asEnum)(const char *name, size_t nameLen),\n           AkTypeId              enumType);\n\n#endif /* dae_source_h */\n"
  },
  {
    "path": "src/io/dae/core/spline.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"spline.h\"\n#include \"source.h\"\n#include \"enum.h\"\n#include \"vert.h\"\n\nAK_HIDE\nAkObject*\ndae_spline(DAEState   * __restrict dst,\n           xml_t      * __restrict xml,\n           AkGeometry * __restrict geom) {\n  AkHeap   *heap;\n  AkObject *obj;\n  AkSpline *spline;\n  AkSource *source;\n\n  heap   = dst->heap;\n  xml    = xml->val;\n\n  obj    = ak_objAlloc(heap, geom, sizeof(*spline), AK_GEOMETRY_SPLINE, true);\n  spline = ak_objGet(obj);\n\n  spline->geom   = geom;\n  spline->closed = xmla_u32(xmla(xml, _s_dae_closed), 0);\n  \n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_source)) {\n      if ((source = dae_source(dst, xml, NULL, 0))) {\n        source->next   = spline->source;\n        spline->source = source;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_control_vertices)) {\n      spline->cverts = dae_vert(dst, xml, obj);\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      spline->extra = tree_fromxml(heap, obj, xml);\n    }\n\n    xml = xml->next;\n  }\n\n  return obj;\n}\n"
  },
  {
    "path": "src/io/dae/core/spline.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_spline_h\n#define dae_spline_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkObject*\ndae_spline(DAEState   * __restrict dst,\n           xml_t      * __restrict xml,\n           AkGeometry * __restrict geom);\n\n#endif /* dae_spline_h */\n"
  },
  {
    "path": "src/io/dae/core/techn.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"techn.h\"\n#include \"../common.h\"\n\nAkTechnique*\ndae_techn(xml_t  * __restrict xml,\n          AkHeap * __restrict heap,\n          void   * __restrict memp) {\n  AkTechnique *techn;\n\n  techn          = ak_heap_calloc(heap, memp, sizeof(*techn));\n  techn->profile = xmla_strdup_by(xml, heap, _s_dae_profile, techn);\n  techn->xmlns   = xmla_strdup_by(xml, heap, _s_dae_xmlns, techn);\n  techn->chld    = tree_fromxml(heap, techn, xml);\n\n  return techn;\n}\n"
  },
  {
    "path": "src/io/dae/core/techn.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_technique_h\n#define dae_technique_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkTechnique*\ndae_techn(xml_t  * __restrict xml,\n          AkHeap * __restrict heap,\n          void   * __restrict memp);\n\n#endif /* dae_technique_h */\n"
  },
  {
    "path": "src/io/dae/core/triangle.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"triangle.h\"\n#include \"enum.h\"\n#include \"../../../array.h\"\n\nAK_HIDE\nAkTriangles*\ndae_triangles(DAEState * __restrict dst,\n              xml_t    * __restrict xml,\n              void     * __restrict memp,\n              AkTriangleMode        mode) {\n  AkTriangles *tri;\n  AkHeap      *heap;\n  uint32_t     indexoff;\n\n  heap = dst->heap;\n  tri  = ak_heap_calloc(heap, memp, sizeof(*tri));\n  \n  tri->mode      = mode;\n  tri->base.type = AK_PRIMITIVE_TRIANGLES;\n\n  tri->base.name         = xmla_strdup_by(xml, heap, _s_dae_name, tri);\n  tri->base.bindmaterial = xmla_strdup_by(xml, heap, _s_dae_material, tri);\n  tri->base.nPolygons    = xmla_u32(xmla(xml, _s_dae_count), 0);\n\n  indexoff = 0;\n  xml      = xml->val;\n\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_input)) {\n      AkInput *inp;\n\n      inp              = ak_heap_calloc(heap, tri, sizeof(*inp));\n      inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp);\n\n      if (!inp->semanticRaw) {\n        ak_free(inp);\n      } else {\n        AkURL *url;\n        AkEnum inputSemantic;\n\n        inputSemantic = dae_semantic(inp->semanticRaw);\n\n        if (inputSemantic < 0)\n          inputSemantic = AK_INPUT_OTHER;\n\n        inp->semantic = inputSemantic;\n        inp->offset   = xmla_u32(xmla(xml, _s_dae_offset), 0);\n        inp->set      = xmla_u32(xmla(xml, _s_dae_set),    0);\n\n        url = url_from(xml, _s_dae_source, memp);\n        rb_insert(dst->inputmap, inp, url);\n\n        if ((uint32_t)inp->semantic != AK_INPUT_SEMANTIC_VERTEX) {\n          inp->semantic   = dae_semantic(inp->semanticRaw);\n          inp->next       = tri->base.input;\n          tri->base.input = inp;\n          tri->base.inputCount++;\n\n          if (inp->offset > indexoff)\n            indexoff = inp->offset;\n        } else {\n          dae_vertmap_add(dst, inp, &tri->base);\n\n          /* don't store VERTEX because it will be duplicated to all prims */\n          // tri->base.reserved1 = inp->offset;\n          // tri->base.reserved2 = inp->set;\n          // ak_free(inp);\n        }\n      }\n    } else if (xml_tag_eq(xml, _s_dae_p) && xml->val) {\n      AkUIntArray *uintArray;\n      AkResult     ret;\n      \n      ret = xml_strtoui_array(heap, tri, xml->val, &uintArray);\n      if (ret == AK_OK)\n        tri->base.indices = uintArray;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      tri->base.extra = tree_fromxml(heap, tri, xml);\n    }\n    xml = xml->next;\n  }\n\n  tri->base.indexStride = indexoff + 1;\n  \n  return tri;\n}\n"
  },
  {
    "path": "src/io/dae/core/triangle.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_triangles_h\n#define dae_triangles_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkTriangles*\ndae_triangles(DAEState * __restrict dst,\n              xml_t    * __restrict xml,\n              void     * __restrict memp,\n              AkTriangleMode        mode);\n\n#endif /* dae_triangles_h */\n"
  },
  {
    "path": "src/io/dae/core/value.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"value.h\"\n#include \"../fx/samp.h\"\n#include \"../1.4/surface.h\"\n\n#define AK_CUSTOM_TYPE_SURFACE 1\n\ntypedef struct valpair {\n  const char *key;\n  AkTypeId    val;\n  int         m; /* this is userData for _CUSTOM */\n  int         n;\n  int         size;\n} valpair;\n\nstatic int valpair_cmp1(const void *, const void *);\nstatic int valpair_cmp2(const void *, const void *);\nstatic int valpair_cmpxt(const void *, const void *);\n\nstatic valpair valmap[] = {\n  {_s_dae_string,   AKT_STRING,   1, 1, sizeof(char *)},\n  {_s_dae_bool,     AKT_BOOL,     1, 1, sizeof(bool)},\n  {_s_dae_bool2,    AKT_BOOL2,    1, 2, sizeof(bool[2])},\n  {_s_dae_bool3,    AKT_BOOL3,    1, 3, sizeof(bool[3])},\n  {_s_dae_bool4,    AKT_BOOL4,    1, 4, sizeof(bool[4])},\n  {_s_dae_int,      AKT_INT,      1, 1, sizeof(int)},\n  {_s_dae_int2,     AKT_INT2,     1, 2, sizeof(int[2])},\n  {_s_dae_int3,     AKT_INT3,     1, 3, sizeof(int[3])},\n  {_s_dae_int4,     AKT_INT4,     1, 4, sizeof(int[4])},\n  {_s_dae_float,    AKT_FLOAT,    1, 1, sizeof(float)},\n  {_s_dae_float2,   AKT_FLOAT2,   1, 2, sizeof(float[2])},\n  {_s_dae_float3,   AKT_FLOAT3,   1, 3, sizeof(float[3])},\n  {_s_dae_float4,   AKT_FLOAT4,   1, 4, sizeof(float[4])},\n  {_s_dae_float2x2, AKT_FLOAT2x2, 2, 2, sizeof(float[2][2])},\n  {_s_dae_float3x3, AKT_FLOAT3x3, 3, 3, sizeof(float[3][3])},\n  {_s_dae_float4x4, AKT_FLOAT4x4, 4, 4, sizeof(float[4][4])},\n\n  /* samplers */\n  {_s_dae_sampler1d,     AKT_SAMPLER1D,     1, 1, sizeof(AkSampler)},\n  {_s_dae_sampler2d,     AKT_SAMPLER2D,     1, 1, sizeof(AkSampler)},\n  {_s_dae_sampler3d,     AKT_SAMPLER3D,     1, 1, sizeof(AkSampler)},\n  {_s_dae_sampler_cube,  AKT_SAMPLER_CUBE,  1, 1, sizeof(AkSampler)},\n  {_s_dae_sampler_rect,  AKT_SAMPLER_RECT,  1, 1, sizeof(AkSampler)},\n  {_s_dae_sampler_depth, AKT_SAMPLER_DEPTH, 1, 1, sizeof(AkSampler)},\n\n  /* COLLADA 1.4 */\n  {\n    _s_dae_surface,\n    AKT_CUSTOM,\n    AK_CUSTOM_TYPE_SURFACE,\n    1,\n    sizeof(AkDae14Surface)\n  },\n};\n\nstatic size_t valmapLen = 0;\n\nAK_HIDE\nvoid\ndae_dtype(const char *typeName, AkTypeDesc *type) {\n  valpair *found;\n\n  if (valmapLen == 0) {\n    valmapLen = AK_ARRAY_LEN(valmap);\n    qsort(valmap,\n          valmapLen,\n          sizeof(valmap[0]),\n          valpair_cmp1);\n  }\n\n  found = bsearch(typeName,\n                  valmap,\n                  valmapLen,\n                  sizeof(valmap[0]),\n                  valpair_cmp2);\n\n  if (!found) {\n    type->typeId   = AKT_UNKNOWN;\n    type->typeName = NULL;\n    type->size     = 0;\n    return;\n  }\n\n  type->size     = found->size;\n  type->typeId   = found->val;\n  type->typeName = found->key;\n}\n\nAK_HIDE\nAkValue*\ndae_value(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp) {\n  AkHeap        *heap;\n  AkValue       *val;\n  valpair       *found;\n  const xml_t   *sval;\n\n  heap = dst->heap;\n  sval = xmls(xml);\n  \n  if (valmapLen == 0) {\n    valmapLen = AK_ARRAY_LEN(valmap);\n    qsort(valmap, valmapLen, sizeof(valmap[0]), valpair_cmp1);\n  }\n  \n  found = bsearch(xml, valmap, valmapLen, sizeof(valmap[0]), valpair_cmpxt);\n\n  if (!found)\n    return NULL;\n\n  val                = ak_heap_calloc(heap, memp, sizeof(*val));\n  val->type.size     = found->size;\n  val->type.typeId   = found->val;\n  val->type.typeName = found->key;\n\n  switch (found->val) {\n    case AKT_STRING:\n      val->value = xml_strdup(xml, heap, val);\n      break;\n    case AKT_BOOL:\n    case AKT_BOOL2:\n    case AKT_BOOL3:\n    case AKT_BOOL4:{\n      AkBool *boolVal;\n\n      boolVal = ak_heap_calloc(heap,\n                               val,\n                               sizeof(*boolVal) * found->m * found->n);\n      xml_strtob_fast(sval, boolVal, found->m * found->n);\n\n      val->value = boolVal;\n      break;\n    }\n    case AKT_INT:\n    case AKT_INT2:\n    case AKT_INT3:\n    case AKT_INT4:{\n      AkInt *intVal;\n  \n      intVal = ak_heap_calloc(heap,\n                              memp,\n                              sizeof(*intVal) * found->m * found->n);\n      xml_strtoi_fast(sval, intVal, found->m * found->n);\n\n      val->value = intVal;\n      break;\n    }\n    case AKT_FLOAT:\n    case AKT_FLOAT2:\n    case AKT_FLOAT3:\n    case AKT_FLOAT4:\n    case AKT_FLOAT2x2:\n    case AKT_FLOAT3x3:\n    case AKT_FLOAT4x4:{\n      AkFloat *floatVal;\n\n      floatVal = ak_heap_calloc(heap,\n                                memp,\n                                sizeof(*floatVal) * found->m * found->n);\n      xml_strtof_fast(sval, floatVal, found->m * found->n);\n\n      val->value = floatVal;\n      break;\n    }\n    case AKT_SAMPLER1D:\n    case AKT_SAMPLER2D:\n    case AKT_SAMPLER3D:\n    case AKT_SAMPLER_CUBE:\n    case AKT_SAMPLER_RECT:\n    case AKT_SAMPLER_DEPTH: {\n      AkSampler *sampler;\n\n      if ((sampler = dae_sampler(dst, xml, val))) {\n        AkTexture *tex;\n        \n        tex = ak_heap_calloc(heap, val, sizeof(*tex));\n        ak_setypeid(tex, AKT_TEXTURE);\n\n        tex->sampler = sampler;\n        tex->type    = found->val;\n        val->value   = tex;\n      }\n\n      break;\n    }\n    case AKT_CUSTOM: {\n      switch (found->m) {\n        case AK_CUSTOM_TYPE_SURFACE: {\n          val->value = dae14_surface(dst, xml, val);\n          break;\n        }\n      }\n      break;\n    }\n    default:\n      break;\n  }\n\n  return val;\n}\n\nstatic\nint\nvalpair_cmp1(const void * a, const void * b) {\n  return strcmp(((const valpair *)a)->key, ((const valpair *)b)->key);\n}\n\nstatic\nint\nvalpair_cmp2(const void * a, const void * b) {  \n  return strcmp(a, ((const valpair *)b)->key);\n}\n\nstatic int\nvalpair_cmpxt(const void *a, const void *b) {\n  return xml_tag_cmp(a, ((const valpair *)b)->key);\n}\n"
  },
  {
    "path": "src/io/dae/core/value.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_value_h\n#define dae_value_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkValue*\ndae_value(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp);\n\nAK_HIDE\nvoid\ndae_dtype(const char *typeName, AkTypeDesc *type);\n\n#endif /* dae_value_h */\n"
  },
  {
    "path": "src/io/dae/core/vert.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"vert.h\"\n#include \"enum.h\"\n\nAK_HIDE\nAkVertices*\ndae_vert(DAEState * __restrict dst,\n         xml_t    * __restrict xml,\n         void     * __restrict memp) {\n  AkHeap     *heap;\n  AkVertices *vert;\n  \n  heap = dst->heap;\n  vert = ak_heap_calloc(heap, memp, sizeof(*vert));\n  xmla_setid(xml, heap, vert);\n  \n  vert->name = xmla_strdup_by(xml, heap, _s_dae_name, vert);\n  \n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_input)) {\n      AkInput *inp;\n      \n      inp              = ak_heap_calloc(heap, vert, sizeof(*inp));\n      inp->semanticRaw = xmla_strdup_by(xml, heap, _s_dae_semantic, inp);\n      \n      if (!inp->semanticRaw) {\n        ak_free(inp);\n      } else {\n        AkURL *url;\n        inp->semantic = dae_semantic(inp->semanticRaw);\n        \n        url = url_from(xml, _s_dae_source, memp);\n        rb_insert(dst->inputmap, inp, url);\n        \n        inp->next   = vert->input;\n        vert->input = inp;\n        vert->inputCount++;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      vert->extra = tree_fromxml(heap, vert, xml);\n    }\n    xml = xml->next;\n  }\n  \n  return vert;\n}\n"
  },
  {
    "path": "src/io/dae/core/vert.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_vertices_h\n#define dae_vertices_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkVertices*\ndae_vert(DAEState * __restrict dst,\n         xml_t    * __restrict xml,\n         void     * __restrict memp);\n\n#endif /* dae_vertices_h */\n"
  },
  {
    "path": "src/io/dae/ctlr.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n\nAK_EXPORT\nAkGeometry*\nak_baseGeometry(AkURL * __restrict baseurl) {\n  void         *found;\n  AkController *ctlr;\n  AkSkinDAE    *skindae;\n  AkMorphDAE   *morphdae;\n  AkTypeId      foundType;\n\n  if ((found = ak_getObjectByUrl(baseurl))) {\n    foundType = ak_typeid(found);\n    switch (foundType) {\n      case AKT_GEOMETRY:\n        return found;\n      case AKT_CONTROLLER: {\n        /* `ctlr->data` is the cross-format AkSkin/AkMorph (no baseGeom).\n           DAE-specific URL lives on the AkSkinDAE/AkMorphDAE attached as\n           userData by the parser. */\n        ctlr = found;\n        if (ctlr->type == AK_CONTROLLER_SKIN\n            && (skindae = ak_userData(ctlr->data))) {\n          return ak_baseGeometry(&skindae->baseGeom);\n        } else if (ctlr->type == AK_CONTROLLER_MORPH\n                   && (morphdae = ak_userData(ctlr->data))) {\n          return ak_baseGeometry(&morphdae->baseGeom);\n        }\n      }\n      default: break;\n    }\n  }\n\n  return NULL;\n}\n"
  },
  {
    "path": "src/io/dae/dae.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"dae.h\"\n#include \"common.h\"\n\n#include \"core/asset.h\"\n#include \"core/cam.h\"\n#include \"core/light.h\"\n#include \"core/geom.h\"\n#include \"core/ctlr.h\"\n#include \"core/node.h\"\n#include \"core/scene.h\"\n#include \"core/anim.h\"\n\n#include \"fx/effect.h\"\n#include \"fx/img.h\"\n#include \"fx/mat.h\"\n\n#include \"postscript.h\"\n#include \"../../id.h\"\n\n#include \"../../../include/ak/path.h\"\n\nstatic ak_enumpair daeVersions[] = {\n  {\"1.5.0\",             AK_COLLADA_VERSION_150},\n  {\"1.5\",               AK_COLLADA_VERSION_150},\n  {\"1.4.1\",             AK_COLLADA_VERSION_141},\n  {\"1.4.0\",             AK_COLLADA_VERSION_140},\n  {\"1.4\",               AK_COLLADA_VERSION_140},\n  {NULL, 0}\n};\n\ntypedef void*(*AkLoadLibraryItemFn)(DAEState * __restrict dst,\n                                    xml_t    * __restrict xml,\n                                    void     * __restrict memp);\nstatic void ak_daeFreeDupl(RBTree *, RBNode *);\n\nstatic\nvoid\ndae_lib(DAEState   * __restrict dst,\n        xml_t      * __restrict xml,\n        const char * __restrict name,\n        AkLoadLibraryItemFn     loadfn,\n        AkLibrary ** __restrict dest);\n\nAK_HIDE\nAkResult\ndae_doc(AkDoc     ** __restrict dest,\n        const char * __restrict filepath) {\n  AkHeap            *heap;\n  AkDoc             *doc;\n  const xml_doc_t   *xdoc;\n  xml_t             *xml, *assetEl;\n  AkAssetInf        *inf;\n  xml_attr_t        *versionAttr;\n  void              *xmlString;\n  AkLibraries       *libs;\n  FListItem         *freeUsrData;\n  DAEState           dstVal, *dst;\n  size_t             xmlSize;\n  AkResult           ret;\n\n  if ((ret = ak_readfile(filepath, NULL, &xmlString, &xmlSize)) != AK_OK)\n    return ret;\n\n  xdoc = xml_parse(xmlString, XML_PREFIXES | XML_READONLY);\n  if (!xdoc || !(xml = xdoc->root)) {\n    if (xdoc)\n      free((void *)xdoc);\n    ak_releasefile(xmlString, xmlSize);\n    return AK_ERR;\n  }\n\n  heap = ak_heap_new(NULL, NULL, NULL);\n  doc  = ak_heap_calloc(heap, NULL, sizeof(*doc));\n\n  doc->inf            = ak_heap_calloc(heap, doc, sizeof(*doc->inf));\n  doc->inf->name      = filepath;\n  doc->inf->dir       = ak_path_dir(heap, doc, filepath);\n  doc->inf->flipImage = true;\n  doc->inf->ftype     = AK_FILE_TYPE_COLLADA;\n  doc->coordSys       = AK_YUP; /* Default */\n\n  /* for fixing skin and morph vertices */\n  doc->reserved = rb_newtree_ptr();\n  ((RBTree *)doc->reserved)->onFreeNode = ak_daeFreeDupl;\n\n  if (doc->inf->dir)\n    doc->inf->dirlen = strlen(doc->inf->dir);\n\n  ak_heap_setdata(heap, doc);\n  ak_id_newheap(heap);\n\n  memset(&dstVal, 0, sizeof(dstVal));\n\n  dstVal.doc          = doc;\n  dstVal.heap         = heap;\n  dstVal.tempmem      = ak_heap_alloc(heap, doc, sizeof(void*));\n  dstVal.meshInfo     = rb_newtree_ptr();\n  dstVal.inputmap     = rb_newtree_ptr();\n  dstVal.texmap       = rb_newtree_ptr();\n  dstVal.instanceMap  = rb_newtree_ptr();\n\n  dstVal.meshTargets  = rb_newtree_ptr();\n\n  dst                 = &dstVal;\n\n  dstVal.texmap->userData = dst;\n\n  /* get version info */\n  /* because it is current and most used version */\n  dst->version = AK_COLLADA_VERSION_141;\n  if ((versionAttr = xmla(xml, _s_dae_version))) {\n    ak_enumpair *v;\n\n    for (v = daeVersions; v->key; v++) {\n      if (!strncmp(v->key, versionAttr->val, versionAttr->valsize)) {\n        dst->version = v->val;\n        break;\n      }\n    }\n  }\n  \n  libs    = &doc->lib;\n  assetEl = NULL;\n  xml     = xml->val;\n\n  /* with default Asset Parameters */\n  assetEl = xml_elem(xml->parent, _s_dae_asset);\n  if ((inf = dae_asset(dst, assetEl, doc, &doc->inf->base))) {\n    doc->coordSys = inf->coordSys;\n    doc->unit     = inf->unit;\n  }\n  \n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_lib_cameras)) {\n      dae_lib(dst, xml, _s_dae_camera, dae_cam, &libs->cameras);\n    } else if (xml_tag_eq(xml, _s_dae_lib_lights)) {\n      dae_lib(dst, xml, _s_dae_light, dae_light, &libs->lights);\n    } else if (xml_tag_eq(xml, _s_dae_lib_geometries)) {\n      dae_lib(dst, xml, _s_dae_geometry, dae_geom, &libs->geometries);\n    } else if (xml_tag_eq(xml, _s_dae_lib_effects)) {\n      dae_lib(dst, xml, _s_dae_effect, dae_effect, &libs->effects);\n    } else if (xml_tag_eq(xml, _s_dae_lib_images)) {\n      dae_lib(dst, xml, _s_dae_image, dae_image, &libs->libimages);\n    } else if (xml_tag_eq(xml, _s_dae_lib_materials)) {\n      dae_lib(dst, xml, _s_dae_material, dae_material, &libs->materials);\n    } else if (xml_tag_eq(xml, _s_dae_lib_controllers)) {\n      dae_lib(dst, xml, _s_dae_controller, dae_ctlr, &libs->controllers);\n    } else if (xml_tag_eq(xml, _s_dae_lib_visual_scenes)) {\n      dae_lib(dst, xml, _s_dae_visual_scene, dae_vscene, &libs->visualScenes);\n    } else if (xml_tag_eq(xml, _s_dae_lib_nodes)) {\n      dae_lib(dst, xml, _s_dae_node, dae_node2, &libs->nodes);\n    } else if (xml_tag_eq(xml, _s_dae_lib_animations)) {\n      dae_lib(dst, xml, _s_dae_animation, dae_anim, &libs->animations);\n    } else if (xml_tag_eq(xml, _s_dae_scene)) {\n      dae_scene(dst, xml);\n    }\n    xml = xml->next;\n  }\n\n  *dest = doc;\n\n  /* post-parse operations */\n  dae_postscript(dst);\n\n  /* cleanup up details */\n  freeUsrData = dst->linkedUserData;\n  while (freeUsrData) {\n    void *tofree;\n\n    if ((tofree = ak_userData(freeUsrData->data)))\n      ak_free(tofree);\n\n    ak_heap_ext_rm(heap, ak__alignof(freeUsrData->data), AK_HEAP_NODE_FLAGS_USR);\n    freeUsrData = freeUsrData->next;\n  }\n\n  ak_free(dstVal.tempmem);\n\n  flist_sp_destroy(&dst->linkedUserData);\n\n  rb_destroy(dstVal.meshInfo);\n  rb_destroy(dstVal.inputmap);\n  rb_destroy(dstVal.texmap);\n  rb_destroy(dstVal.instanceMap);\n\n  flist_sp_destroy(&dstVal.vertMap);\n\n  rb_destroy(dstVal.meshTargets);\n\n  if (xdoc)\n    free((void *)xdoc);\n  \n  if (xmlString)\n    ak_releasefile(xmlString, xmlSize);\n\n  /* TODO: memory leak, free this RBTree*/\n  /* rb_destroy(doc->reserved); */\n\n  return AK_OK;\n}\n\nstatic\nvoid\nak_daeFreeDupl(RBTree *tree, RBNode *node) {\n  if (node == tree->nullNode)\n    return;\n  ak_free(node->val);\n}\n\nstatic\nvoid\ndae_lib(DAEState   * __restrict dst,\n        xml_t      * __restrict xml,\n        const char * __restrict name,\n        AkLoadLibraryItemFn     loadfn,\n        AkLibrary ** __restrict dest) {\n  AkHeap           *heap;\n  AkDoc            *doc;\n  AkLibrary        *lib;\n  AkOneWayIterBase *it;\n  \n  heap      = dst->heap;\n  doc       = dst->doc;\n\n  lib       = ak_heap_calloc(heap, doc, sizeof(*lib));\n  lib->name = xmla_strdup_by(xml, heap, _s_dae_name, lib);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, name)) {\n      if ((it = loadfn(dst, xml, lib))) {\n        it->next  = lib->chld;\n        lib->chld = it;\n        lib->count++;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      lib->extra = tree_fromxml(heap, lib, xml);\n    }\n    xml = xml->next;\n  }\n\n  lib->next = *dest;\n  *dest     = lib;\n}\n"
  },
  {
    "path": "src/io/dae/dae.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_h\n#define dae_h\n\n#include \"../../../include/ak/assetkit.h\"\n\n#include <stdlib.h>\n\nAK_HIDE\nAkResult\ndae_doc(AkDoc     ** __restrict dest,\n        const char * __restrict filepath);\n\n#endif /* dae_h */\n"
  },
  {
    "path": "src/io/dae/fixup/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/dae/fixup/angle.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"angle.h\"\n\n/*\n COLLADA uses degress for all angles, convert desgress to radians. It may exists\n in these places as far as I know:\n\n 1. Rotate element - fixed in place\n 2. Perspective (xfov, yfov) - fixed in place\n 3. Light (fallofAngle) - fixed in place\n 4. Skew element - fixed in place\n 5. Animation data (output, tangents...)!!!! - NEEDS TO BE FIXED?\n\n */\n\nstatic\nvoid\ndae_cvtAnglesAt(AkAccessor * __restrict acc,\n                AkBuffer   * __restrict buff,\n                uint32_t                paramIndex) {\n  AkAccessorDAE *accdae;\n  float         *pbuff;\n  size_t         i, count, st, off;\n\n  if (!acc || !buff || !buff->data || !(accdae = ak_userData(acc)))\n    return;\n\n  acc->componentType = (AkTypeId)(uintptr_t)ak_userData(buff);\n  if (acc->componentType != AKT_FLOAT)\n    return;\n\n  st = accdae->stride ? accdae->stride : 1;\n  if (paramIndex >= st)\n    return;\n\n  off   = accdae->offset + paramIndex;\n  count = acc->count;\n  pbuff = buff->data;\n\n  for (i = 0; i < count; i++)\n    glm_make_rad(pbuff + off + i * st);\n}\n\nAK_HIDE\nvoid\ndae_cvtAngles(AkAccessor * __restrict acc,\n              AkBuffer   * __restrict buff,\n              const char * __restrict paramName) {\n  AkAccessorDAE *accdae;\n  AkDataParam   *param;\n  uint32_t       index;\n  \n  if (!(accdae = ak_userData(acc)))\n    return;\n\n  index = 0;\n  param = accdae->param;\n  while (param) {\n    if (param->name && strcasecmp(param->name, paramName) == 0)\n      dae_cvtAnglesAt(acc, buff, index);\n\n    index++;\n    param = param->next;\n  }\n}\n\nstatic\nvoid\ndae_fixAngleTangent(AkInput  * __restrict inp,\n                    uint32_t              outputAngleIndex,\n                    uint32_t              outputStride) {\n  AkAccessor    *acc;\n  AkAccessorDAE *accdae;\n  AkBuffer      *buff;\n  uint32_t       st;\n  uint32_t       idx;\n\n  if (!inp\n      || !(acc = inp->accessor)\n      || !(accdae = ak_userData(acc))\n      || !(buff = ak_getObjectByUrl(&accdae->source)))\n    return;\n\n  st = accdae->stride ? accdae->stride : 1;\n\n  if (st == outputStride) {\n    idx = outputAngleIndex;\n  } else if (st >= outputStride * 2\n             && outputAngleIndex * 2 + 1 < st) {\n    /* Bezier-style tangents are usually (time, value) pairs. Only the\n       value component is angular; the time component stays seconds. */\n    idx = outputAngleIndex * 2 + 1;\n  } else if (st == 1) {\n    idx = 0;\n  } else if (outputAngleIndex < st) {\n    idx = outputAngleIndex;\n  } else {\n    return;\n  }\n\n  dae_cvtAnglesAt(acc, buff, idx);\n}\n\n/* TODO: This works for BERZIER but HERMITE?? */\nAK_HIDE\nvoid\ndae_fixAngles(DAEState * __restrict dst) {\n  /* TODO: */\n  FListItem     *item;\n  AkAnimSampler *sampler;\n  AkDataParam   *param;\n  AkAccessor    *acc;\n  AkBuffer      *buff;\n  AkAccessorDAE *accdae;\n  uint32_t       index, outStride;\n  \n  item = dst->toRadiansSampelers;\n  while (item) {\n    sampler = item->data;\n    acc     = NULL;\n    buff    = NULL;\n\n    if ((acc = sampler->outputInput->accessor)\n        && (accdae = ak_userData(acc))\n        && (buff = ak_getObjectByUrl(&accdae->source))) {\n      bool foundAngle;\n\n      foundAngle = false;\n      index      = 0;\n      outStride  = accdae->stride ? accdae->stride : 1;\n\n      if ((param = accdae->param)) {\n        do {\n          if (param->name && strcasecmp(param->name, _s_dae_angle) == 0) {\n            foundAngle = true;\n            break;\n          }\n          \n          index++;\n        } while ((param = param->next));\n      }\n\n      if (!foundAngle)\n        goto nxt_sampler;\n\n      dae_cvtAngles(acc, buff, _s_dae_angle);\n\n      dae_fixAngleTangent(sampler->inTangentInput,  index, outStride);\n      dae_fixAngleTangent(sampler->outTangentInput, index, outStride);\n    }\n\n  nxt_sampler:\n    item = item->next;\n  }\n\n  flist_sp_destroy(&dst->toRadiansSampelers);\n}\n"
  },
  {
    "path": "src/io/dae/fixup/angle.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_fixangle_h\n#define dae_fixangle_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ndae_fixAngles(DAEState * __restrict dst);\n\nAK_HIDE\nvoid\ndae_cvtAngles(AkAccessor * __restrict acc,\n              AkBuffer   * __restrict buff,\n              const char * __restrict paramName);\n\n#endif /* dae_fixangle_h */\n"
  },
  {
    "path": "src/io/dae/fixup/channel.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"channel.h\"\n#include <stdlib.h>\n#include <string.h>\n\ntypedef struct DAEMatrixAnimFix {\n  struct DAEMatrixAnimFix *next;\n  AkAccessor             *acc;\n} DAEMatrixAnimFix;\n\n/*----------------------------------------------------------------------------\n * Channel target parsing.\n *\n * Accepted forms (DAE COLLADA 1.4 §5.3.2 + real-world exporter quirks):\n *   \"id(N)\"                       — bare source id + index\n *   \"prefix/.../id(N)\"            — SID-style path, last segment is \"id(N)\"\n *\n * `outIdLen`/`outId` point into the input buffer (no allocation); caller\n * uses ak_heap_strndup or ak_getObjectById_n if it needs to keep it.\n *--------------------------------------------------------------------------*/\n\nstatic\nbool\ndae_parseChannelTargetIndexed(const char  *target,\n                              const char **outIdStart,\n                              size_t      *outIdLen,\n                              uint32_t    *outIdx) {\n  const char *open, *close, *idStart, *p;\n  long        idx;\n  char       *end;\n\n  if (!target) return false;\n\n  /* Trailing \"(N)\" must be present and well-formed. */\n  if (!(open = strrchr(target, '('))\n      || !(close = strchr(open + 1, ')'))\n      || close == open + 1)\n    return false;\n\n  idx = strtol(open + 1, &end, 10);\n  if (end != close || idx < 0)\n    return false;\n\n  /* Id portion: everything from the last '/' (or start) up to '('. */\n  if ((idStart = strrchr(target, '/')) && idStart < open)\n    idStart++;\n  else\n    idStart = target;\n\n  if (idStart >= open) return false; /* \"(N)\" with empty id */\n\n  /* Matrix element targets use forms like \"matrix(0)(0)\"; those are not\n     morph weight arrays and must stay on the normal SID resolver path. */\n  for (p = idStart; p < open; p++) {\n    if (*p == '(' || *p == ')') return false;\n  }\n\n  *outIdStart = idStart;\n  *outIdLen   = (size_t)(open - idStart);\n  *outIdx     = (uint32_t)idx;\n  return true;\n}\n\n/*----------------------------------------------------------------------------\n * Topology lookups.\n *--------------------------------------------------------------------------*/\n\n/* Walk visual scene tree until we find an AkInstanceMorph that wraps the\n   given AkMorph. Returns first match (multi-instance disambiguation is a\n   spec gray area; first-found is what most authoring tools imply). */\nstatic\nAkInstanceMorph *\ndae_findInstanceMorph_node(AkNode * __restrict node, AkMorph *morph) {\n  AkInstanceGeometry *instGeom;\n  AkInstanceMorph    *found;\n  AkNode             *child;\n\n  for (; node; node = (AkNode *)node->next) {\n    for (instGeom = node->geometry; instGeom;\n         instGeom = (AkInstanceGeometry *)instGeom->base.next) {\n      if (instGeom->morpher && instGeom->morpher->morph == morph)\n        return instGeom->morpher;\n    }\n    if ((child = node->chld)\n        && (found = dae_findInstanceMorph_node(child, morph)))\n      return found;\n  }\n  return NULL;\n}\n\nstatic\nAkInstanceMorph *\ndae_findInstanceMorph(AkDoc * __restrict doc, AkMorph *morph) {\n  AkVisualScene   *vscn;\n  AkInstanceMorph *found;\n\n  if (!morph || !doc->lib.visualScenes) return NULL;\n\n  for (vscn = (void *)doc->lib.visualScenes->chld;\n       vscn;\n       vscn = (void *)vscn->base.next) {\n    if ((found = dae_findInstanceMorph_node(vscn->node, morph)))\n      return found;\n  }\n  return NULL;\n}\n\n/* Find the morph controller whose morphdae->source chain contains src.\n   Linear scan over doc->lib.controllers — fine for typical asset sizes;\n   if multi-controller perf becomes a real workload we'd build a\n   source→controller map up front. */\nstatic\nAkController *\ndae_findControllerForSource(AkDoc * __restrict doc, AkSource *src) {\n  AkController *ctlr;\n  AkMorph      *morph;\n  AkMorphDAE   *morphdae;\n  AkSource     *s;\n\n  if (!src || !doc->lib.controllers) return NULL;\n\n  for (ctlr = (AkController *)doc->lib.controllers->chld;\n       ctlr;\n       ctlr = (AkController *)ctlr->base.next) {\n    if (ctlr->type != AK_CONTROLLER_MORPH)              continue;\n    if (!(morph = ctlr->data))                          continue;\n    if (!(morphdae = ak_userData(morph)))               continue;\n\n    for (s = morphdae->source; s; s = s->next) {\n      if (s == src) return ctlr;\n    }\n  }\n  return NULL;\n}\n\nstatic\nAkInstanceMorph *\ndae_resolveMorpher(AkDoc      * __restrict doc,\n                   const char *idStart,\n                   size_t      idLen) {\n  void           *element;\n  AkController   *ctlr;\n  char            idbuf[256];\n\n  /* Bounded inline copy to NUL-terminate the id slice. Source ids in DAE\n     are typically short (<64 chars); the buffer is generous. Rather than\n     allocate, we just bail if it's longer than expected. */\n  if (idLen == 0 || idLen >= sizeof(idbuf)) return NULL;\n  memcpy(idbuf, idStart, idLen);\n  idbuf[idLen] = '\\0';\n\n  /* Doc-wide id lookup (hash table — O(1)). The \"id\" segment of a DAE\n     channel target is typically the SID inside a controller scope; many\n     real-world exporters set the source's id to the same string as the\n     SID, so the doc id table catches the common case. */\n  if (!(element = ak_getObjectById(doc, idbuf))) return NULL;\n  if (ak_typeid(element) != AKT_SOURCE)          return NULL;\n\n  if (!(ctlr = dae_findControllerForSource(doc, (AkSource *)element)))\n    return NULL;\n\n  return dae_findInstanceMorph(doc, (AkMorph *)ctlr->data);\n}\n\nstatic\nbool\ndae_resolveMatrixElement(AkContext        * __restrict ctx,\n                         const char       * __restrict target,\n                         AkResolvedTarget * __restrict rt) {\n  const char *seg, *open1, *close1, *open2, *close2, *attr;\n  AkObject   *obj;\n  long        a, b;\n  char       *end;\n  char        base[512];\n  size_t      n;\n\n  if (!ctx || !target || !rt)\n    return false;\n\n  if ((seg = strrchr(target, '/')))\n    seg++;\n  else\n    seg = target;\n\n  if (!(open1 = strchr(seg, '('))\n      || !(close1 = strchr(open1 + 1, ')'))\n      || close1 == open1 + 1)\n    return false;\n\n  a = strtol(open1 + 1, &end, 10);\n  if (end != close1 || a < 0)\n    return false;\n\n  open2 = close2 = NULL;\n  b     = -1;\n  if (*(close1 + 1) == '(') {\n    open2 = close1 + 1;\n    if (!(close2 = strchr(open2 + 1, ')')) || close2 == open2 + 1)\n      return false;\n\n    b = strtol(open2 + 1, &end, 10);\n    if (end != close2 || b < 0 || close2[1] != '\\0')\n      return false;\n  } else if (close1[1] != '\\0') {\n    return false;\n  }\n\n  n = (size_t)(open1 - target);\n  if (n == 0 || n >= sizeof(base))\n    return false;\n\n  memcpy(base, target, n);\n  base[n] = '\\0';\n\n  attr = NULL;\n  obj  = ak_sid_resolve(ctx, base, &attr);\n  if (!obj || attr || ak_typeid(obj) != AKT_OBJECT\n      || (AkTypeId)obj->type != AKT_MATRIX)\n    return false;\n\n  if (b >= 0) {\n    if (a >= 4 || b >= 4)\n      return false;\n    rt->off = (uint32_t)(b * 4 + a);\n  } else {\n    if (a >= 16)\n      return false;\n    rt->off = (uint32_t)((a % 4) * 4 + (a / 4));\n  }\n\n  rt->target    = obj;\n  rt->isPartial = true;\n  return true;\n}\n\nstatic\nAkInput *\ndae_animSamplerInput(AkAnimSampler    * __restrict samp,\n                     AkInputSemantic               sem) {\n  AkInput *inp;\n\n  if (!samp) return NULL;\n\n  switch (sem) {\n    case AK_INPUT_INPUT:         if (samp->inputInput)      return samp->inputInput;      break;\n    case AK_INPUT_OUTPUT:        if (samp->outputInput)     return samp->outputInput;     break;\n    case AK_INPUT_IN_TANGENT:    if (samp->inTangentInput)  return samp->inTangentInput;  break;\n    case AK_INPUT_OUT_TANGENT:   if (samp->outTangentInput) return samp->outTangentInput; break;\n    case AK_INPUT_INTERPOLATION: if (samp->interpInput)     return samp->interpInput;     break;\n    default: break;\n  }\n\n  for (inp = samp->input; inp; inp = inp->next) {\n    if (inp->semantic == sem)\n      return inp;\n  }\n\n  return NULL;\n}\n\nstatic\nbool\ndae_matrixAnimFixed(DAEMatrixAnimFix * __restrict it,\n                    AkAccessor       * __restrict acc) {\n  for (; it; it = it->next) {\n    if (it->acc == acc)\n      return true;\n  }\n\n  return false;\n}\n\nstatic\nvoid\ndae_matrixAnimMark(DAEState          * __restrict dst,\n                   DAEMatrixAnimFix ** __restrict done,\n                   AkAccessor        * __restrict acc) {\n  DAEMatrixAnimFix *it;\n\n  it      = ak_heap_calloc(dst->heap, dst->doc, sizeof(*it));\n  it->acc = acc;\n  it->next = *done;\n  *done   = it;\n}\n\nstatic\nbool\ndae_transposeMat4Output(AkAccessor * __restrict acc) {\n  char     *base;\n  float    *m;\n  size_t    st;\n  uint32_t  i;\n\n  if (!acc\n      || !acc->buffer\n      || !acc->buffer->data\n      || acc->componentType != AKT_FLOAT\n      || acc->componentCount != 16)\n    return false;\n\n  st = acc->byteStride;\n  if (st == 0)\n    st = sizeof(float) * 16;\n\n  base = (char *)acc->buffer->data + acc->byteOffset;\n  for (i = 0; i < acc->count; i++) {\n    m = (float *)(base + st * i);\n    glm_mat4_transpose((vec4 *)m);\n  }\n\n  return true;\n}\n\nstatic\nvoid\ndae_fixupMatrixAccessor(DAEState          * __restrict dst,\n                        AkInput           * __restrict inp,\n                        DAEMatrixAnimFix ** __restrict done) {\n  AkAccessor *acc;\n\n  if (!inp || !(acc = inp->accessor))\n    return;\n\n  if (dae_matrixAnimFixed(*done, acc))\n    return;\n\n  if (dae_transposeMat4Output(acc))\n    dae_matrixAnimMark(dst, done, acc);\n}\n\nstatic\nvoid\ndae_fixupMatrixChannel(DAEState          * __restrict dst,\n                       AkContext         * __restrict ctx,\n                       AkChannel         * __restrict ch,\n                       DAEMatrixAnimFix ** __restrict done) {\n  AkResolvedTarget rt;\n  AkAnimSampler   *samp;\n  AkObject        *obj;\n\n  rt = ak_channelTarget(ctx, ch);\n  if (!rt.target || ak_typeid(rt.target) != AKT_OBJECT)\n    return;\n\n  obj = rt.target;\n  if ((AkTypeId)obj->type != AKT_MATRIX)\n    return;\n\n  samp = ak_getObjectByUrl(&ch->source);\n\n  /* DAE <matrix> values are authored row-major. Static node matrices are\n     normalized to cglm/AssetKit column-major during node parse; animation\n     OUTPUT matrices need the same one-time normalization. Tangents are not\n     evaluated today, but if an exporter authors 16-float matrix tangents,\n     keep them in the same convention for future Bezier/Hermite support. */\n  dae_fixupMatrixAccessor(dst,\n                          dae_animSamplerInput(samp, AK_INPUT_OUTPUT),\n                          done);\n  dae_fixupMatrixAccessor(dst,\n                          dae_animSamplerInput(samp, AK_INPUT_IN_TANGENT),\n                          done);\n  dae_fixupMatrixAccessor(dst,\n                          dae_animSamplerInput(samp, AK_INPUT_OUT_TANGENT),\n                          done);\n}\n\nstatic\nvoid\ndae_fixup_channel_walk(DAEState          * __restrict dst,\n                       AkAnimation       * __restrict anim,\n                       AkContext         * __restrict ctx,\n                       DAEMatrixAnimFix ** __restrict done) {\n  AkAnimation      *sub;\n  AkChannel        *ch;\n  AkResolvedTarget *rt;\n  AkResolvedTarget  mrt;\n  AkInstanceMorph  *morpher;\n  const char       *idStart;\n  size_t            idLen;\n  uint32_t          idx;\n\n  for (; anim; anim = (AkAnimation *)anim->base.next) {\n    for (ch = anim->channel; ch; ch = ch->next) {\n      if (!ch->resolvedTarget) {\n        memset(&mrt, 0, sizeof(mrt));\n        if (dae_resolveMatrixElement(ctx, ch->target, &mrt)) {\n          rt                 = ak_heap_calloc(dst->heap, ch, sizeof(*rt));\n          *rt                = mrt;\n          ch->resolvedTarget = rt;\n          ch->targetType     = AK_TARGET_FLOAT;\n        } else if (dae_parseChannelTargetIndexed(ch->target,\n                                                 &idStart,\n                                                 &idLen,\n                                                 &idx)\n                   && (morpher = dae_resolveMorpher(dst->doc,\n                                                    idStart,\n                                                    idLen))) {\n          rt                 = ak_heap_calloc(dst->heap, ch, sizeof(*rt));\n          rt->target         = morpher;\n          rt->off            = idx;\n          rt->isPartial      = true;\n          ch->resolvedTarget = rt;\n          ch->targetType     = AK_TARGET_WEIGHTS;\n        }\n      }\n\n      dae_fixupMatrixChannel(dst, ctx, ch, done);\n    }\n\n    if ((sub = anim->animation))\n      dae_fixup_channel_walk(dst, sub, ctx, done);\n  }\n}\n\nAK_HIDE\nvoid\ndae_fixup_channel(DAEState * __restrict dst) {\n  AkAnimation      *anim;\n  DAEMatrixAnimFix *done;\n  AkContext         ctx;\n\n  if (!dst->doc->lib.animations) return;\n\n  anim    = (AkAnimation *)dst->doc->lib.animations->chld;\n  done    = NULL;\n  memset(&ctx, 0, sizeof(ctx));\n  ctx.doc = dst->doc;\n\n  dae_fixup_channel_walk(dst, anim, &ctx, &done);\n}\n"
  },
  {
    "path": "src/io/dae/fixup/channel.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_fixup_channel_h\n#define dae_fixup_channel_h\n\n#include \"../common.h\"\n\n/*\n * Resolve/fix DAE animation channels that need loader-side help:\n *   - parenthesized index syntax used for morph weights, e.g.\n *     <channel target=\"morph-weights(0)\">\n *   - matrix component targets, e.g. <channel target=\"node/matrix(1)(2)\">\n *     where DAE row/column indices need AssetKit column-major offsets\n *   - row-major MAT4 OUTPUT arrays targeting <matrix> transforms; static\n *     DAE matrices are normalized to AssetKit column-major during node\n *     parse, so animation matrices are normalized here to match.\n *\n * For matched morph patterns this builds an AkResolvedTarget pointing at\n * the AkInstanceMorph (target), the index inside the parentheses (off),\n * and isPartial=true; ak_channelTarget then returns it directly. Channels\n * with conventional \"node/transform.attr\" SID syntax stay on the normal\n * resolver path.\n *\n * Must run AFTER dae_fixup_instctlr so AkInstanceMorph instances exist.\n */\nAK_HIDE\nvoid\ndae_fixup_channel(DAEState * __restrict dst);\n\n#endif /* dae_fixup_channel_h */\n"
  },
  {
    "path": "src/io/dae/fixup/ctlr.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ctlr.h\"\n\nstatic\nAkResult\nak_fixBoneWeights(AkHeap        *heap,\n                  size_t         nMeshVertex,\n                  AkSkin        *skin,\n                  AkDuplicator  *duplicator,\n                  AkBoneWeights *intrWeights,\n                  AkBoneWeights *weights,\n                  AkAccessor    *weightsAcc,\n                  uint32_t       jointOffset,\n                  uint32_t       weightsOffset);\n\nAK_INLINE\nuint32_t\nak_daeSafeWeightCount(AkBoneWeights * __restrict intrWeights,\n                      AkUIntArray   * __restrict v,\n                      uint32_t                   viStride,\n                      size_t                     oldIdx) {\n  uint32_t *pCount;\n  uint32_t *pSum;\n  size_t    off;\n  size_t    avail;\n  uint32_t  count;\n\n  if (!intrWeights || !intrWeights->counts || !v || viStride == 0)\n    return 0;\n  if (oldIdx >= intrWeights->nVertex)\n    return 0;\n\n  pCount = intrWeights->counts;\n  pSum   = intrWeights->counts + intrWeights->nVertex;\n  off    = pSum[oldIdx];\n  count  = pCount[oldIdx];\n\n  if (off > v->count / viStride)\n    return 0;\n\n  avail = v->count / viStride - off;\n  if (count > avail)\n    count = (uint32_t)avail;\n\n  return count;\n}\n\nAK_INLINE\nfloat\nak_daeReadSkinWeight(AkAccessor * __restrict acc,\n                     uint32_t                idx) {\n  AkBuffer *buff;\n  char     *base;\n  size_t    stride;\n  float     val;\n\n  if (!acc || idx >= acc->count || !(buff = acc->buffer) || !buff->data)\n    return 0.0f;\n\n  stride = acc->byteStride ? acc->byteStride : sizeof(float);\n  base   = (char *)buff->data + acc->byteOffset;\n\n  memcpy(&val, base + (size_t)idx * stride, sizeof(val));\n\n  return val;\n}\n\nAK_HIDE\nvoid\ndae_fixup_ctlr(DAEState * __restrict dst) {\n  AkDoc        *doc;\n  AkController *ctlr;\n\n  doc  = dst->doc;\n  ctlr = (void *)dst->doc->lib.controllers->chld;\n  while (ctlr) {\n    switch (ctlr->type) {\n      case AK_CONTROLLER_SKIN: {\n        AkSkin     *skin;\n        AkSkinDAE  *skindae;\n        AkGeometry *geom;\n\n        skin    = ctlr->data;\n        skindae = ak_userData(skin);\n        if (!(geom = ak_baseGeometry(&skindae->baseGeom)))\n          goto nxt_ctlr;\n\n        switch (geom->gdata->type) {\n          case AK_GEOMETRY_MESH: {\n            AkMesh          *mesh;\n            AkDaeMeshInfo   *meshInfo;\n            AkMeshPrimitive *prim;\n            AkBoneWeights   *intrWeights; /* interleaved */\n            AkInput         *jointswInp,  *weightsInp;\n            AkAccessor      *weightsAcc;\n            size_t           nMeshVertex;\n            uint32_t         primIndex;\n\n            mesh          = ak_objGet(geom->gdata);\n            prim          = mesh->primitive;\n            intrWeights   = (void *)skin->weights;\n            if (!intrWeights || !intrWeights->counts)\n              goto nxt_ctlr;\n\n            primIndex     = 0;\n            meshInfo      = rb_find(dst->meshInfo, mesh);\n\n            jointswInp  = skindae->weights.joints;\n            weightsInp  = skindae->weights.weights;\n            if (!jointswInp || !weightsInp || !(weightsAcc = weightsInp->accessor))\n              goto nxt_ctlr;\n\n            skin->weights = ak_heap_calloc(dst->heap,\n                                           ctlr->data,\n                                           sizeof(void *)\n                                           * mesh->primitiveCount);\n\n            nMeshVertex = meshInfo ? meshInfo->nVertex : intrWeights->nVertex;\n\n            flist_sp_insert(&mesh->skins, skin);\n\n            while (prim) {\n              AkAccessor    *posAcc;\n              AkBoneWeights *weights; /* per-primitive weights */\n              AkDuplicator  *dupl;\n              size_t         count;\n\n              if (!prim->pos || !(posAcc = prim->pos->accessor)) {\n                primIndex++;\n                prim = prim->next;\n                continue;\n              }\n\n              dupl    = rb_find(doc->reserved, prim);\n              count = 0;\n              if (dupl && dupl->range)\n                count = dupl->bufCount + dupl->dupCount;\n              if (count == 0)\n                count = posAcc->count;\n\n              weights = ak_heap_calloc(dst->heap, ctlr->data, sizeof(*weights));\n\n              weights->counts  = ak_heap_calloc(dst->heap,\n                                                ctlr->data,\n                                                count * sizeof(uint32_t));\n              weights->indexes = ak_heap_calloc(dst->heap,\n                                                ctlr->data,\n                                                count * sizeof(size_t));\n\n              weights->nVertex = count;\n\n              ak_fixBoneWeights(dst->heap,\n                                nMeshVertex,\n                                skin,\n                                dupl,\n                                intrWeights,\n                                weights,\n                                weightsAcc,\n                                jointswInp->offset,\n                                weightsInp->offset);\n\n              skin->weights[primIndex] = weights;\n              primIndex++;\n              prim = prim->next;\n            }\n\n            skin->nPrims = primIndex;\n\n            ak_free(intrWeights);\n\n            break;\n          }\n          default:\n            break;\n        }\n        break;\n      }\n      case AK_CONTROLLER_MORPH: {\n        AkMorph       *morph;\n        AkMorphDAE    *morphdae;\n        AkGeometry    *baseGeom;\n        AkInput       *input, *targetInput, *weightInput;\n        AkAccessor    *targetAcc, *weightAcc;\n        AkBuffer      *targetBuff, *weightBuff;\n        AkMorphTarget *prevTarget;\n        uint32_t       basePrimCount;\n        size_t         count, i;\n        AkContext      sidCtx = { .doc = doc };\n\n        morph    = ctlr->data;\n        morphdae = ak_userData(morph);\n        if (!(baseGeom = ak_baseGeometry(&morphdae->baseGeom)))\n          goto nxt_ctlr;\n\n        /* DAE morph has exactly one MORPH_TARGET input (a NAME/IDREF/SIDREF\n           array of per-target geometries) and one MORPH_WEIGHT input (a\n           float array of default weights). Split the chain so we can walk\n           targets while still having the weights handy for defaultWeights. */\n        targetInput = weightInput = NULL;\n        for (input = morphdae->input; input; input = input->next) {\n          if      (input->semantic == AK_INPUT_MORPH_TARGET) targetInput = input;\n          else if (input->semantic == AK_INPUT_MORPH_WEIGHT) weightInput = input;\n        }\n        if (!targetInput || !(targetAcc = targetInput->accessor)\n            || !(targetBuff = targetAcc->buffer)\n            || !targetBuff->data\n            || targetAcc->count == 0)\n          goto nxt_ctlr;\n\n        /* Per AkMorphTarget, primitiveCount mirrors the base mesh — DAE\n           assumes target geometries share the base topology (same prim\n           layout, same vertex count). Compute once. */\n        basePrimCount = (baseGeom->gdata\n                         && baseGeom->gdata->type == AK_GEOMETRY_MESH)\n                          ? ((AkMesh *)ak_objGet(baseGeom->gdata))->primitiveCount\n                          : 1;\n\n        /* Build AkMorphTarget chain in source order (tail-insert). DAE\n           animation channels reference targets via position — preserving\n           parse order keeps weight indices aligned with the source <Name>\n           array. */\n        count      = targetAcc->count;\n        prevTarget = NULL;\n\n        for (i = 0; i < count; i++) {\n          const char    *id;\n          void          *resolved;\n          AkObject      *wrap;\n          AkMorphTarget *target;\n          const char   **idArr;\n\n          /* Source array entries are typed by the accessor's componentType.\n             COLLADA 1.4/1.5 spec lists IDREF and Name as the typical morph\n             target source types; SIDREF is technically allowed via the\n             generic <param> mechanism. */\n          idArr = targetBuff->data;\n          if (!(id = idArr[i])) continue;\n\n          switch (targetAcc->componentType) {\n            case AKT_IDREF:\n            case AKT_NAME:\n              resolved = ak_getObjectById(doc, id);\n              break;\n            case AKT_SIDREF:\n              /* SIDREF source-array entries store the absolute scoped\n                 path (e.g. \"geom_id/sid_path\"). Resolve directly via\n                 ak_sid_resolve — `ak_sid_resolve_from` is for\n                 already-split (id, sid) pairs and would mis-prefix\n                 the path here. */\n              resolved = ak_sid_resolve(&sidCtx, id, NULL);\n              break;\n            default:\n              resolved = NULL;\n              break;\n          }\n          if (!resolved || ak_typeid(resolved) != AKT_GEOMETRY)\n            continue;\n\n          /* AkObject wrap carries the type tag used by intr.c switch\n             dispatch. Payload is one pointer-sized slot storing the\n             AkGeometry* — read back via ak_objGetTarget on C/Swift sides.\n             The geometry itself stays in doc->lib.geometries with its own\n             lifetime; this wrap just references it. */\n          wrap                   = ak_objAlloc(dst->heap, morph,\n                                               sizeof(AkGeometry *),\n                                               AK_MORPHABLE_GEOMETRY,\n                                               true);\n          ak_objGetTarget(wrap)  = (AkGeometry *)resolved;\n\n          target                 = ak_heap_calloc(dst->heap, morph,\n                                                  sizeof(*target));\n          target->target         = wrap;\n          target->primitiveCount = basePrimCount;\n\n          if (prevTarget) prevTarget->next = target;\n          else            morph->target    = target;\n          prevTarget = target;\n          morph->targetCount++;\n        }\n\n        if (morph->targetCount == 0)\n          goto nxt_ctlr;\n\n        /* MORPH_WEIGHT → defaultWeights. Spec requires the weight array's\n           length to match the target count; if it mismatches we still take\n           min(count, targetCount) so partial assets don't silently drop\n           on the floor. */\n        if (weightInput\n            && (weightAcc  = weightInput->accessor)\n            && (weightBuff = weightAcc->buffer)\n            && weightBuff->data\n            && weightAcc->count > 0) {\n          AkFloatArray *defaults;\n          float        *src;\n          size_t        nWeights;\n\n          nWeights = weightAcc->count < morph->targetCount\n                       ? weightAcc->count\n                       : morph->targetCount;\n\n          defaults = ak_heap_alloc(dst->heap, morph,\n                                   sizeof(*defaults)\n                                    + sizeof(float) * nWeights);\n          defaults->count = nWeights;\n\n          /* Weights are stored densely in the source's accessor; respect\n             stride for safety (DAE float source from <float_array> is\n             typically tightly packed but technically allowed to be\n             interleaved within its <source>). */\n          src = weightBuff->data;\n          if (weightAcc->byteStride\n              && weightAcc->byteStride != sizeof(float)) {\n            char *base = (char *)src + weightAcc->byteOffset;\n            for (i = 0; i < nWeights; i++) {\n              defaults->items[i] =\n                *(float *)(base + i * weightAcc->byteStride);\n            }\n          } else {\n            src = (float *)((char *)src + weightAcc->byteOffset);\n            for (i = 0; i < nWeights; i++)\n              defaults->items[i] = src[i];\n          }\n\n          morph->defaultWeights = defaults;\n        }\n\n        /* Register geom→morph for instance hookup later (DAE node walker\n           reads meshTargets to attach AkInstanceMorph when it sees an\n           <instance_controller> referring to a morph controller). */\n        rb_insert(dst->meshTargets, baseGeom, morph);\n\n        break;\n      }\n      default:\n        break;\n    }\n\n  nxt_ctlr:\n    ctlr = (AkController *)ctlr->base.next;\n  }\n}\n\nAK_HIDE\nvoid\ndae_fixup_instctlr(DAEState * __restrict dst) {\n  AkSkinDAE            *skindae;\n  FListItem            *item;\n  AkInstanceController *instCtlr;\n  AkController         *ctlr;\n  AkNode               *node;\n  AkInstanceGeometry   *instGeom;\n  AkContext             ctx = { .doc = dst->doc };\n\n  item = dst->instCtlrs;\n  while (item) {\n    AkMorphDAE *morphdae;\n\n    instCtlr = item->data;\n    ctlr     = ak_instanceObject(&instCtlr->base);\n    node     = instCtlr->base.node;\n    instGeom = ak_heap_calloc(dst->heap, node, sizeof(*instGeom));\n\n    switch (ctlr->type) {\n      case AK_CONTROLLER_SKIN: {\n        AkInstanceSkin *instSkin;\n        AkSkin         *skin;\n        AkNode        **joints;\n        AkInput        *jointsInp,  *matrixInp;\n        AkAccessor     *jointsAcc,  *matrixAcc;\n        AkBuffer       *jointsBuff, *matrixBuff;\n        FListItem      *skel;\n        const char     *sid, **it;\n        AkFloat        *mit;\n        AkFloat4x4     *invm;\n        size_t          count, i;\n\n        skin      = ctlr->data;\n        skindae   = ak_userData(skin);\n        instSkin  = ak_heap_calloc(dst->heap, node, sizeof(*instSkin));\n\n        skin      = ctlr->data;\n        jointsInp = skindae->joints.joints;\n        matrixInp = skindae->joints.invBindMatrix;\n        invm      = NULL;\n        joints    = NULL;\n\n        if ((jointsAcc = jointsInp->accessor)) {\n          matrixAcc  = matrixInp->accessor;\n          jointsBuff = jointsAcc->buffer;\n          matrixBuff = matrixAcc->buffer;\n\n          it         = jointsBuff->data;\n          mit        = matrixBuff->data;\n          count      = jointsAcc->count;\n          joints     = ak_heap_alloc(dst->heap, instCtlr, sizeof(void **) * count);\n          invm       = ak_heap_alloc(dst->heap, ctlr->data, sizeof(mat4) * count);\n\n          for (i = 0; i < count; i++) {\n            if (!(sid = it[i]))\n              continue;\n\n            switch (jointsAcc->componentType) {\n              case AKT_IDREF:\n                joints[i] = ak_getObjectById(dst->doc, sid);\n                break;\n              case AKT_SIDREF:\n              case AKT_NAME:\n                if ((skel = instCtlr->reserved)) {\n                  do {\n                    if ((joints[i] = ak_sid_resolve_from(&ctx, skel->data, sid, NULL)))\n                      break;\n                  } while ((skel = skel->next));\n                }\n                break;\n              default:\n                break;\n            }\n\n            /* move invBindMatrix to new location */\n            memcpy(invm[i], mit + 16 * i, sizeof(AkFloat) * 16);\n            glm_mat4_transpose(invm[i]);\n          }\n\n          skin->nJoints            = count;\n          skin->invBindPoses       = invm;\n\n          /* DAE persists skeleton root as <skeleton> URL on each\n             <instance_controller>; the same skin can be re-used with\n             different skeletons per instance, but for a single-instance\n             setup the first URL is a faithful AkSkin.skeleton hint.\n             Fall back silently when missing — callers default to\n             joints[0]. */\n          if (!skin->skeleton && instCtlr->reserved) {\n            const char *skelUrl = instCtlr->reserved->data;\n            void       *resolved;\n            if (skelUrl\n                && (resolved = ak_getObjectById(dst->doc, skelUrl + 1))\n                && ak_typeid(resolved) == AKT_NODE) {\n              skin->skeleton = resolved;\n            }\n          }\n\n          instSkin->skin           = skin;\n          instSkin->overrideJoints = joints;\n\n          /* COLLADA permits chained controllers — skin's source can be\n             another controller (typically a morph from Maya):\n                 <skin source=\"#someMorph\"/>\n                 <morph source=\"#baseGeom\"/>\n             Single-level lookahead is enough: AkInstanceGeometry has\n             one morpher + one skinner slot, and no GPU renderer\n             (SceneKit / Three.js / Filament / typical Metal/Vulkan\n             pipelines) consumes a deeper controller stack — the spec's\n             \"arbitrary recursion\" was never adopted in practice.\n             ak_baseGeometry() collapses the rest of the chain when\n             resolving `base.object`. */\n          {\n            void *src = ak_getObjectByUrl(&skindae->baseGeom);\n            if (src && ak_typeid(src) == AKT_CONTROLLER) {\n              AkController *intermediate = src;\n              if (intermediate->type == AK_CONTROLLER_MORPH) {\n                AkMorphDAE      *morphdae2;\n                AkInstanceMorph *instMorph;\n                morphdae2 = ak_userData(intermediate->data);\n                instMorph = ak_heap_calloc(dst->heap, node,\n                                           sizeof(*instMorph));\n                instMorph->morph           = intermediate->data;\n                instMorph->overrideWeights = NULL;\n                instGeom->morpher          = instMorph;\n                (void)morphdae2;\n              }\n              /* skin→skin nesting is exotic and the data model can't\n                 represent two skinners — silently use the outer one. */\n            }\n            instGeom->base.object = ak_baseGeometry(&skindae->baseGeom);\n          }\n\n          /* create instance geometry for skin */\n          instGeom->skinner        = instSkin;\n          instGeom->bindMaterial   = instCtlr->bindMaterial;\n          ak_heap_setpm(instCtlr->bindMaterial, instGeom);\n\n          instGeom->base.next = (AkInstanceBase *)node->geometry;\n          if (node->geometry)\n            node->geometry->base.prev = (AkInstanceBase *)instGeom;\n          node->geometry = instGeom;\n        }\n        break;\n      }\n      case AK_CONTROLLER_MORPH: {\n        AkInstanceMorph *instMorph;\n        AkMorph         *morph;\n\n        morph     = ctlr->data;\n        morphdae  = ak_userData(morph);\n        instMorph = ak_heap_calloc(dst->heap, node, sizeof(*instMorph));\n\n        instMorph->morph           = morph;\n        instMorph->overrideWeights = NULL; /* DAE has no per-instance weights;\n                                              animation drives morph.targets   */\n\n        instGeom->morpher          = instMorph;\n        instGeom->bindMaterial     = instCtlr->bindMaterial;\n\n        /* Symmetric to SKIN case: morph→skin→geom is rare but spec-allowed.\n           Single-level lookahead; deeper chains collapse via ak_baseGeometry. */\n        {\n          void *src = ak_getObjectByUrl(&morphdae->baseGeom);\n          if (src && ak_typeid(src) == AKT_CONTROLLER) {\n            AkController *intermediate = src;\n            if (intermediate->type == AK_CONTROLLER_SKIN) {\n              AkInstanceSkin *instSkin = ak_heap_calloc(dst->heap, node,\n                                                        sizeof(*instSkin));\n              instSkin->skin           = intermediate->data;\n              instSkin->overrideJoints = NULL; /* picked up from default joints */\n              instGeom->skinner        = instSkin;\n            }\n          }\n        }\n        instGeom->base.object = ak_baseGeometry(&morphdae->baseGeom);\n        ak_heap_setpm(instCtlr->bindMaterial, instGeom);\n\n        instGeom->base.next = (AkInstanceBase *)node->geometry;\n        if (node->geometry)\n          node->geometry->base.prev = (AkInstanceBase *)instGeom;\n        node->geometry = instGeom;\n        break;\n      }\n      default: break;\n    }\n    item = item->next;\n  }\n}\n\nstatic\nAkResult\nak_fixBoneWeights(AkHeap        *heap,\n                  size_t         nMeshVertex,\n                  AkSkin        *skin,\n                  AkDuplicator  *duplicator,\n                  AkBoneWeights *intrWeights,\n                  AkBoneWeights *weights,\n                  AkAccessor    *weightsAcc,\n                  uint32_t       jointOffset,\n                  uint32_t       weightsOffset) {\n  AkSkinDAE    *skindae;\n  AkBoneWeight *w, *iw;\n  AkBuffer     *weightsBuff;\n  AkUIntArray  *dupc, *dupcsum, *v;\n  uint32_t     *pv, *pOldCountSum, *old;\n  size_t       *wi, vc, d, s, pno, poo, nwsum, newidx, next, tmp;\n  uint32_t     *nj, i, j, k, vcount, viStride, widx;\n  bool          useDupl;\n\n  if (!skin || !intrWeights || !weights || !weightsAcc)\n    return AK_ERR;\n\n  skindae  = ak_userData(skin);\n  dupc     = NULL;\n  dupcsum  = NULL;\n  useDupl  = false;\n  nj       = weights->counts;\n  wi       = weights->indexes;\n  nwsum    = 0;\n\n  if (duplicator && duplicator->range) {\n    dupc    = duplicator->range->dupc;\n    dupcsum = duplicator->range->dupcsum;\n    useDupl = dupc && dupcsum;\n  }\n\n  if (!nj || !wi\n      || !intrWeights->counts\n      || !(weightsBuff = weightsAcc->buffer)\n      || !weightsBuff->data\n      || !(v        = skindae->weights.v)\n      || !(pv       = v->items))\n    return AK_ERR;\n\n  pOldCountSum = intrWeights->counts + intrWeights->nVertex;\n  viStride     = skindae->inputCount; /* input count in <v> element */\n  if (viStride == 0\n      || jointOffset >= viStride\n      || weightsOffset >= viStride)\n    return AK_ERR;\n\n  vc = nMeshVertex;\n  if (intrWeights->nVertex < vc)\n    vc = intrWeights->nVertex;\n  if (useDupl && dupc->count < vc)\n    vc = dupc->count;\n  if (!useDupl && weights->nVertex < vc)\n    vc = weights->nVertex;\n\n  /* copy to new location and duplicate if needed */\n  if (useDupl) {\n    for (i = 0; i < vc; i++) {\n      if ((poo = dupc->items[3 * i + 2]) == 0)\n        continue;\n\n      pno    = dupc->items[3 * i];\n      d      = dupc->items[3 * i + 1];\n      if (pno >= dupcsum->count)\n        continue;\n\n      s      = dupcsum->items[pno];\n      vcount = ak_daeSafeWeightCount(intrWeights, v, viStride, poo - 1);\n\n      for (j = 0; j <= d; j++) {\n        newidx = pno + j + s;\n        if (newidx >= weights->nVertex)\n          continue;\n        wi[newidx] = vcount;\n        nj[newidx] = vcount;\n        nwsum     += vcount;\n      }\n    }\n  } else {\n    for (i = 0; i < vc; i++) {\n      vcount = ak_daeSafeWeightCount(intrWeights, v, viStride, i);\n      wi[i]  = vcount;\n      nj[i]  = vcount;\n      nwsum += vcount;\n    }\n  }\n\n  /* prepare weight index */\n  for (next = j = 0; j < weights->nVertex; j++) {\n    tmp   = wi[j];\n    wi[j] = next;\n    next  = tmp + next;\n  }\n\n  /* now we know the size of arrays: weights, pJointsCount, npWeightsIndex */\n  w     = nwsum > 0 ? ak_heap_alloc(heap, weights, sizeof(*w) * nwsum) : NULL;\n  nwsum = 0;\n\n  if (useDupl) {\n    for (i = 0; i < vc; i++) {\n      if ((poo = dupc->items[3 * i + 2]) == 0)\n        continue;\n      pno    = dupc->items[3 * i];\n      d      = dupc->items[3 * i + 1];\n      if (pno >= dupcsum->count)\n        continue;\n\n      s      = dupcsum->items[pno];\n      vcount = ak_daeSafeWeightCount(intrWeights, v, viStride, poo - 1);\n      old    = &pv[pOldCountSum[poo - 1] * viStride];\n\n      for (j = 0; j <= d; j++) {\n        tmp = pno + j + s;\n        if (tmp >= weights->nVertex)\n          continue;\n\n        newidx = wi[tmp];\n\n        for (k = 0; k < vcount; k++) {\n          widx       = old[k * viStride + weightsOffset];\n          iw         = &w[newidx + k];\n          iw->joint  = old[k * viStride + jointOffset];\n          iw->weight = ak_daeReadSkinWeight(weightsAcc, widx);\n        }\n\n        nwsum += vcount;\n      }\n    }\n  } else {\n    for (i = 0; i < vc; i++) {\n      vcount = ak_daeSafeWeightCount(intrWeights, v, viStride, i);\n      old    = &pv[pOldCountSum[i] * viStride];\n      newidx = wi[i];\n\n      for (k = 0; k < vcount; k++) {\n        widx       = old[k * viStride + weightsOffset];\n        iw         = &w[newidx + k];\n        iw->joint  = old[k * viStride + jointOffset];\n        iw->weight = ak_daeReadSkinWeight(weightsAcc, widx);\n      }\n\n      nwsum += vcount;\n    }\n  }\n\n  weights->weights  = w;\n  weights->nWeights = nwsum;\n\n  return AK_OK;\n}\n"
  },
  {
    "path": "src/io/dae/fixup/ctlr.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_ctlr_h\n#define dae_ctlr_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ndae_fixup_ctlr(DAEState * __restrict dst);\n\nAK_HIDE\nvoid\ndae_fixup_instctlr(DAEState * __restrict dst);\n\n#endif /* dae_ctlr_h */\n"
  },
  {
    "path": "src/io/dae/fixup/geom.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"geom.h\"\n#include \"mesh.h\"\n\nAK_HIDE\nAkResult\ndae_geom_fixup_all(AkDoc * doc) {\n  AkLibrary  *geomLib;\n  AkGeometry *geom;\n\n  geomLib = doc->lib.geometries;\n  while (geomLib) {\n    geom = (void *)geomLib->chld;\n    while (geom) {\n      dae_geom_fixup(geom);\n      geom = (AkGeometry *)geom->base.next;\n    }\n\n    geomLib = geomLib->next;\n  }\n\n  return AK_OK;\n}\n\nAK_HIDE\nAkResult\ndae_geom_fixup(AkGeometry * geom) {\n  AkObject *primitive;\n\n  primitive = geom->gdata;\n  switch ((AkGeometryType)primitive->type) {\n    case AK_GEOMETRY_MESH:\n      dae_mesh_fixup(ak_objGet(primitive));\n    default:\n      break;\n  }\n\n  return AK_OK;\n}\n"
  },
  {
    "path": "src/io/dae/fixup/geom.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_geom_fixup_h\n#define dae_geom_fixup_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkResult\ndae_geom_fixup(AkGeometry * geom);\n\nAK_HIDE\nAkResult\ndae_geom_fixup_all(AkDoc * doc);\n\n#endif /* dae_geom_fixup */\n"
  },
  {
    "path": "src/io/dae/fixup/mesh.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"mesh.h\"\n#include \"../../../mesh/index.h\"\n#include \"../../../topo/topo.h\"\n\nAK_HIDE\nAkResult\ndae_mesh_fixup(AkMesh * mesh) {\n  AkMeshEditHelper *edith;\n  AkHeap           *heap;\n  AkDoc            *doc;\n\n  heap = ak_heap_getheap(mesh->geom);\n  doc  = ak_heap_data(heap);\n\n  topofix(mesh);\n\n  /* first fixup coord system because verts will be duplicated,\n     reduce extra process */\n  if (ak_opt_get(AK_OPT_COORD_CONVERT_TYPE) == AK_COORD_CVT_ALL\n      && (void *)ak_opt_get(AK_OPT_COORD) != doc->coordSys)\n    ak_changeCoordSysMesh(mesh, (void *)ak_opt_get(AK_OPT_COORD));\n\n  if (!mesh->primitive)\n    return AK_OK;\n\n  ak_meshBeginEdit(mesh);\n\n  edith                 = mesh->edith;\n  edith->skipFixIndices = true; /* to do it once per mesh */\n\n  if (ak_opt_get(AK_OPT_TRIANGULATE))\n    ak_meshTriangulate(mesh);\n\n  if (ak_opt_get(AK_OPT_GEN_NORMALS_IF_NEEDED))\n    if (ak_meshNeedsNormals(mesh))\n      ak_meshGenNormals(mesh);\n\n  edith->skipFixIndices = false;\n  ak_meshFixIndices(mesh);\n\n  ak_meshEndEdit(mesh);\n\n  if (ak_opt_get(AK_OPT_COMPUTE_BBOX))\n    ak_bbox_mesh(mesh);\n  \n  return AK_OK;\n}\n"
  },
  {
    "path": "src/io/dae/fixup/mesh.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_mesh_fixup_h\n#define dae_mesh_fixup_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkResult\ndae_mesh_fixup(AkMesh * mesh);\n\n#endif /* dae_mesh_fixup_h */\n"
  },
  {
    "path": "src/io/dae/fixup/node.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"node.h\"\n#include <cglm/cglm.h>\n\n/*!\n * @brief fix camera, light\n *\n * @todo new node lost its id, sid and name, FIX THIS!\n */\nAK_HIDE\nvoid\ndae_nodeFixupFixedCoord(AkHeap * __restrict heap,\n                        AkNode * __restrict node) {\n  AkNode *newNode;\n\n  if (!node->camera\n       && !node->light)\n      return;\n\n  if (!node->geometry\n      && !node->chld\n      && !node->node) {\n    node->flags |= AK_NODEF_FIXED_COORD;\n    return;\n  }\n\n  /* move to new node, if we move to new child node we would not neeed to,\n   duplicate transform but the node may be used by sid path, so we couldn't\n   move to child, instead we have to duplicate transfroms :(\n   */\n\n  newNode = ak_heap_calloc(heap, node, sizeof(*newNode));\n  newNode->nodeType = AK_NODE_TYPE_NODE;\n\n  if (node->camera) {\n    AkInstanceBase *inst;\n    inst = node->camera;\n    while (inst) {\n      ak_heap_setpm(inst, newNode);\n      inst = inst->next;\n    }\n\n    newNode->camera = node->camera;\n    node->camera    = NULL;\n  }\n\n  if (node->light) {\n    AkInstanceBase *inst;\n    inst = node->light;\n    while (inst) {\n      ak_heap_setpm(inst, newNode);\n      inst = inst->next;\n    }\n\n    newNode->light = node->light;\n    node->light    = NULL;\n  }\n\n  /* duplicate all transforms before apply rotations */\n  ak_transformDup(node, newNode);\n}\n\nAK_HIDE\nvoid\ndae_nodeFixup(AkHeap * __restrict heap,\n              AkNode * __restrict node) {\n  if (node->camera || node->light)\n    dae_nodeFixupFixedCoord(heap, node);\n\n  ak_fixNodeCoordSys(node);\n}\n"
  },
  {
    "path": "src/io/dae/fixup/node.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_node_fixup_h\n#define dae_node_fixup_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ndae_nodeFixupFixedCoord(AkHeap * __restrict heap,\n                        AkNode * __restrict node);\n\nAK_HIDE\nvoid\ndae_nodeFixup(AkHeap * __restrict heap,\n              AkNode*  __restrict node);\n\n#endif /* dae_node_fixup_h */\n"
  },
  {
    "path": "src/io/dae/fixup/tex.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"tex.h\"\n\nAK_HIDE\nvoid\ndae_tex_walk(RBTree *tree, RBNode *rbnode);\n\nAK_HIDE\nvoid\ndae_fix_textures(DAEState * __restrict dst) {\n  rb_walk(dst->texmap, dae_tex_walk);\n}\n\nAK_HIDE\nvoid\ndae_tex_walk(RBTree *tree, RBNode *rbnode) {\n  AkHeap          *heap;\n  AkNewParam      *newparam;\n  AkColorDesc     *cd;\n  AkDAETextureRef *dtex;\n  AkTextureRef    *texref;\n  AkTexture       *tex;\n  AkImage         *image;\n  DAEState        *dst;\n  AkInstanceBase  *instanceImage;\n  AkContext        actx = {0};\n\n  cd       = rbnode->key;\n  dtex     = rbnode->val;\n  heap     = ak_heap_getheap(cd);\n  \n  actx.doc = ak_heap_data(heap);\n\n  texref   = ak_heap_calloc(heap, cd, sizeof(*texref));\n  newparam = ak_sid_resolve(&actx, dtex->texture, NULL);\n  \n  if (!newparam\n      || !(tex = newparam->val->value)) {\n    ak_free(texref);\n    return;\n  }\n\n  dst           = tree->userData;\n  instanceImage = rb_find(dst->instanceMap, tex->sampler);\n  image         = ak_instanceObject(instanceImage);\n  \n  texref->texture = tex;\n  ak_texref_usage(texref, dtex->colorSpace, dtex->channels);\n  \n  /* this is the default */\n  /* use bind_material to set texcoord */\n  texref->coordInputName = ak_heap_strdup(heap, texref, \"TEXCOORD\");\n  tex->image             = image;\n  cd->texture            = texref;\n  \n  if (dtex->texcoord)\n    texref->texcoord = ak_heap_strdup(heap, texref, dtex->texcoord);\n}\n"
  },
  {
    "path": "src/io/dae/fixup/tex.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_tex_fixup_h\n#define dae_tex_fixup_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ndae_fix_textures(DAEState * __restrict dst);\n\n#endif /* dae_tex_fixup_h */\n"
  },
  {
    "path": "src/io/dae/fx/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/dae/fx/colortex.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"colortex.h\"\n#include \"../core/param.h\"\n#include \"../core/color.h\"\n#include \"../core/enum.h\"\n\nAK_HIDE\nvoid\ndae_colorOrTexSet(DAEState    * __restrict dst,\n                  xml_t       * __restrict xml,\n                  void        * __restrict memp,\n                  AkColorDesc * __restrict clr) {\n  AkHeap      *heap;\n\n  heap = dst->heap;\n  xml  = xml->val;\n\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_color)) {\n      clr->color = ak_heap_calloc(heap, memp, sizeof(*clr->color));\n      dae_color(xml, clr->color, true, false, clr->color);\n    } else if (xml_tag_eq(xml, _s_dae_texture)) {\n      AkDAETextureRef *tex;\n\n      tex = ak_heap_calloc(heap, memp, sizeof(*tex));\n      ak_setypeid(tex, AKT_TEXTURE);\n\n      tex->texture  = xmla_strdup(xmla(xml, _s_dae_texture),  heap, tex);\n      tex->texcoord = xmla_strdup(xmla(xml, _s_dae_texcoord), heap, tex);\n\n      if (tex->texture)\n        ak_setypeid((void *)tex->texture, AKT_TEXTURE_NAME);\n\n      if (tex->texcoord)\n        ak_setypeid((void *)tex->texcoord, AKT_TEXCOORD);\n\n      rb_insert(dst->texmap, clr, tex);\n    } else if (xml_tag_eq(xml, _s_dae_param)) {\n      AkParam *param;\n\n      if ((param = dae_param(dst, xml, clr))) {\n        if (clr->param)\n          clr->param->prev = param;\n\n        param->next = clr->param;\n        clr->param  = param;\n      }\n    }\n    xml = xml->next;\n  }\n}\n"
  },
  {
    "path": "src/io/dae/fx/colortex.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_colortex_h\n#define dae_colortex_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ndae_colorOrTexSet(DAEState    * __restrict dst,\n                  xml_t       * __restrict xml,\n                  void        * __restrict memp,\n                  AkColorDesc * __restrict clr);\n\nAK_INLINE\nAkColorDesc*\ndae_colorOrTex(DAEState * __restrict dst,\n               xml_t    * __restrict xml,\n               void     * __restrict memp) {\n  AkHeap      *heap;\n  AkColorDesc *clr;\n\n  heap = dst->heap;\n  clr  = ak_heap_calloc(heap, memp, sizeof(*clr));\n\n  dae_colorOrTexSet(dst, xml, clr, clr);\n\n  return clr;\n}\n\n#endif /* dae_colortex_h */\n"
  },
  {
    "path": "src/io/dae/fx/effect.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"effect.h\"\n#include \"profile.h\"\n\n#include \"../core/asset.h\"\n#include \"../core/techn.h\"\n#include \"../core/param.h\"\n\n#include \"../1.4/image.h\"\n\n AK_HIDE\nvoid*\ndae_effect(DAEState * __restrict dst,\n           xml_t    * __restrict xml,\n           void     * __restrict memp) {\n  AkHeap   *heap;\n  AkEffect *effect;\n\n  heap   = dst->heap;\n  effect = ak_heap_calloc(heap, memp, sizeof(*effect));\n  ak_setypeid(effect, AKT_EFFECT);\n\n  xmla_setid(xml, heap, effect);\n\n  effect->name = xmla_strdup_by(xml, heap, _s_dae_name, effect);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, effect, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_newparam)) {\n      AkNewParam *newparam;\n\n      if ((newparam = dae_newparam(dst, xml, effect))) {\n        if (effect->newparam)\n          effect->newparam->prev = newparam;\n\n        newparam->next   = effect->newparam;\n        effect->newparam = newparam;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_prfl_common)) {\n      AkProfile *profile;\n\n      if ((profile = dae_profile(dst, xml, effect))) {\n        profile->next   = effect->profile;\n        effect->profile = profile;\n      }\n    } else if (dst->version < AK_COLLADA_VERSION_150\n               && xml_tag_eq(xml, _s_dae_image)) {\n      /* migration from 1.4 */\n      dae14_fxMigrateImg(dst, xml, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      effect->extra = tree_fromxml(heap, effect, xml);\n    }\n    xml = xml->next;\n  }\n\n  return effect;\n}\n\nAK_HIDE\nAkInstanceEffect*\ndae_instEffect(DAEState * __restrict dst,\n               xml_t    * __restrict xml,\n               void     * __restrict memp) {\n  AkHeap           *heap;\n  AkInstanceEffect *instEffect;\n  xml_attr_t       *att;\n\n  heap       = dst->heap;\n  instEffect = ak_heap_calloc(heap, memp, sizeof(*instEffect));\n\n  xmla_setid(xml, heap, instEffect);\n\n  instEffect->base.type = AK_INSTANCE_EFFECT;\n  instEffect->base.name = xmla_strdup_by(xml, heap, _s_dae_name, instEffect);\n\n  url_set(dst, xml, _s_dae_url, instEffect, &instEffect->base.url);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_technique_hint)) {\n      AkTechniqueHint *techHint;\n      \n      techHint = ak_heap_calloc(heap, instEffect, sizeof(*techHint));\n\n      if ((att = xmla(xml, _s_dae_ref)))\n        techHint->ref = xmla_strdup(att, heap, techHint);\n      \n      if ((att = xmla(xml, _s_dae_profile)))\n        techHint->profile = xmla_strdup(att, heap, techHint);\n      \n      if ((att = xmla(xml, _s_dae_platform)))\n        techHint->platform = xmla_strdup(att, heap, techHint);\n      \n      techHint->next            = instEffect->techniqueHint;\n      instEffect->techniqueHint = techHint;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      instEffect->base.extra = tree_fromxml(heap, instEffect, xml);\n    }\n    xml = xml->next;\n  }\n\n  return instEffect;\n}\n"
  },
  {
    "path": "src/io/dae/fx/effect.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_fx_effect_h\n#define dae_fx_effect_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid*\ndae_effect(DAEState * __restrict dst,\n           xml_t    * __restrict xml,\n           void     * __restrict memp);\n\nAK_HIDE\nAkInstanceEffect*\ndae_instEffect(DAEState * __restrict dst,\n               xml_t    * __restrict xml,\n               void     * __restrict memp);\n\n#endif /* dae_fx_effect_h */\n"
  },
  {
    "path": "src/io/dae/fx/fltprm.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"fltprm.h\"\n#include \"../core/param.h\"\n\nAK_HIDE \nfloat\ndae_float(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp,\n          size_t                off,\n          float                 defaultVal) {\n  AkHeap         *heap;\n  const xml_t    *sval;\n  float           flt;\n\n  flt  = defaultVal;\n  heap = dst->heap;\n  xml  = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_float) && (sval = xmls(xml))) {\n      sid_seta(xml, heap, memp, memp + off);\n      flt = xml_float(xml, defaultVal);\n    }\n\n    /*\n    else if (xml_tag_eq(xml, _s_dae_param)) {\n      AkParam *param;\n      \n      if ((param = dae_param(dst, xml, flt))) {\n        if (flt->param)\n          flt->param->prev = param;\n        \n        param->next = flt->param;\n        flt->param  = param;\n      }\n    } */\n    xml = xml->next;\n  }\n\n  return flt;\n}\n//\n//AK_HIDE AkFloatOrParam*\n//dae_floatOrParam(DAEState * __restrict dst,\n//                 xml_t    * __restrict xml,\n//                 void     * __restrict memp) {\n//  AkHeap         *heap;\n//  AkFloatOrParam *flt;\n//  const xml_t    *sval;\n//\n//  heap = dst->heap;\n//  flt  = ak_heap_calloc(heap, memp, sizeof(*flt));\n//\n//  xml = xml->val;\n//  while (xml) {\n//    if (xml_tag_eq(xml, _s_dae_float) && (sval = xmls(xml))) {\n//      float *valuef;\n//      \n//      valuef  = ak_heap_calloc(heap, flt, sizeof(*valuef));\n//      xml_strtof_fast(sval, valuef, 1);\n//      \n//      sid_set(xml, heap, valuef);\n//      \n//      flt->val = valuef;\n//    } else if (xml_tag_eq(xml, _s_dae_param)) {\n//      AkParam *param;\n//      \n//      if ((param = dae_param(dst, xml, flt))) {\n//        if (flt->param)\n//          flt->param->prev = param;\n//        \n//        param->next = flt->param;\n//        flt->param  = param;\n//      }\n//    }\n//    xml = xml->next;\n//  }\n//\n//  return flt;\n//}\n"
  },
  {
    "path": "src/io/dae/fx/fltprm.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_fltprm_h\n#define dae_fltprm_h\n\n#include \"../common.h\"\n\n//AK_HIDE AkFloatOrParam*\n//dae_floatOrParam(DAEState * __restrict dst,\n//                 xml_t    * __restrict xml,\n//                 void     * __restrict memp);\n\nAK_HIDE\nfloat\ndae_float(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp,\n          size_t                off,\n          float                 defaultVal);\n\n#endif /* dae_fltprm_h */\n"
  },
  {
    "path": "src/io/dae/fx/img.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"img.h\"\n#include \"../core/asset.h\"\n#include \"../1.4/image.h\"\n#include \"../core/enum.h\"\n\nstatic\nAkInitFrom*\ndae_initFrom(DAEState * __restrict dst,\n             xml_t    * __restrict xml,\n             void     * __restrict memp);\n\nstatic\nAkImageFormat*\ndae_imageFormat(DAEState * __restrict dst,\n                xml_t    * __restrict xml,\n                void     * __restrict memp);\n\nstatic\nAkImage2d*\ndae_create2d(DAEState * __restrict dst,\n             xml_t    * __restrict xml,\n             void     * __restrict memp);\n\nstatic\nAkImage3d*\ndae_create3d(DAEState * __restrict dst,\n             xml_t    * __restrict xml,\n             void     * __restrict memp);\n\nstatic\nAkImageCube*\ndae_createCube(DAEState * __restrict dst,\n               xml_t    * __restrict xml,\n               void     * __restrict memp);\n\nAK_HIDE\nvoid*\ndae_image(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp) {\n  AkHeap     *heap;\n  AkImage    *img;\n  xml_attr_t *att;\n\n  if (dst->version < AK_COLLADA_VERSION_150) {\n    dae14_fxMigrateImg(dst, xml, memp);\n    return NULL;\n  }\n\n  heap = dst->heap;\n  img  = ak_heap_calloc(heap, memp, sizeof(*img));\n\n  xmla_setid(xml, heap, img);\n  sid_set(xml, heap, img);\n  \n  img->name = xmla_strdup_by(xml, heap, _s_dae_name, img);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, img, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_renderable)) {\n      img->renderable = (att = xmla(xml, _s_dae_share))\n                          && att->val\n                          && strcasecmp(att->val, _s_dae_true) == 0;\n    } else if (xml_tag_eq(xml, _s_dae_init_from)) {\n      img->initFrom = dae_initFrom(dst, xml, img);\n    } else if (xml_tag_eq(xml, _s_dae_create_2d)) {\n      AkImage2d *image2d;\n      if ((image2d = dae_create2d(dst, xml, img)))\n          img->image = &image2d->base;\n    } else if (xml_tag_eq(xml, _s_dae_create_3d)) {\n      AkImage3d *image3d;\n      if ((image3d = dae_create3d(dst, xml, img)))\n          img->image = &image3d->base;\n    } else if (xml_tag_eq(xml, _s_dae_create_cube)) {\n      AkImageCube *imageCube;\n      if ((imageCube = dae_createCube(dst, xml, img)))\n          img->image = &imageCube->base;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      img->extra = tree_fromxml(heap, img, xml);\n    }\n    xml = xml->next;\n  }\n\n  return img;\n}\n\nAK_HIDE\nAkInstanceBase*\ndae_instImage(DAEState * __restrict dst,\n              xml_t    * __restrict xml,\n              void     * __restrict memp) {\n  AkHeap         *heap;\n  AkInstanceBase *instImg;\n\n  heap          = dst->heap;\n  instImg       = ak_heap_calloc(heap, memp, sizeof(*instImg));\n  instImg->name = xmla_strdup_by(xml, heap, _s_dae_name, instImg);\n  \n  sid_set(xml, heap, instImg);\n  url_set(dst, xml, _s_dae_url, instImg, &instImg->url);\n\n  return instImg;\n}\n\nstatic\nAkInitFrom*\ndae_initFrom(DAEState * __restrict dst,\n             xml_t    * __restrict xml,\n             void     * __restrict memp) {\n  AkHeap     *heap;\n  AkInitFrom *initFrom;\n  AkHexData  *hex;\n  xml_attr_t *att;\n  \n  heap     = dst->heap;\n  initFrom = ak_heap_calloc(heap, memp, sizeof(*initFrom));\n  \n  initFrom->mipsGenerate = xmla_u32(xmla(xml, _s_dae_mips_generate), 0);\n  initFrom->arrayIndex   = xmla_u32(xmla(xml, _s_dae_array_index), 0);\n  initFrom->mipIndex     = xmla_u32(xmla(xml, _s_dae_mip_index), 0);\n  initFrom->depth        = xmla_u32(xmla(xml, _s_dae_depth), 0);\n\n  if ((att = xmla(xml, _s_dae_face)) && att->val)\n    initFrom->face = dae_face(att);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_ref)) {\n      initFrom->ref = xml_strdup(xml, heap, initFrom);\n    } else if (xml_tag_eq(xml, _s_dae_hex)) {\n      hex         = ak_heap_calloc(heap, initFrom, sizeof(*hex));\n      hex->format = xmla_strdup(xmla(xml, _s_dae_format), heap, hex);\n\n      if (hex->format) {\n        hex->hexval = xml_strdup(xml, heap, initFrom);\n        initFrom->hex = hex;\n      } else {\n        ak_free(hex);\n      }\n    }\n    xml = xml->next;\n  }\n  \n  return initFrom;\n}\n\nstatic\nAkImageFormat*\ndae_imageFormat(DAEState * __restrict dst,\n                xml_t    * __restrict xml,\n                void     * __restrict memp) {\n  AkHeap        *heap;\n  AkImageFormat *format;\n  xml_attr_t    *att;\n\n  heap   = dst->heap;\n  format = ak_heap_calloc(heap, memp, sizeof(*format));\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_hint)) {\n      if ((att = xmla(xml, _s_dae_channels)) && att->val)\n        format->channel = dae_enumChannel(att->val, att->valsize);\n      \n      if ((att = xmla(xml, _s_dae_range)) && att->val)\n        format->range = dae_range(att->val, att->valsize);\n      \n      if ((att = xmla(xml, _s_dae_precision)) && att->val)\n        format->precision = dae_precision(att->val, att->valsize);\n      \n      if ((att = xmla(xml, _s_dae_space)) && att->val)\n        format->space = xmla_strdup(att, heap, format);\n    } else if (xml_tag_eq(xml, _s_dae_exact)) {\n      format->exact = xml_strdup(xml, heap, format);\n    }\n    xml = xml->next;\n  }\n\n  return format;\n}\n\nstatic\nAkImage2d*\ndae_create2d(DAEState * __restrict dst,\n             xml_t    * __restrict xml,\n             void     * __restrict memp) {\n  AkHeap    *heap;\n  AkImage2d *img;\n\n  heap = dst->heap;\n  img  = ak_heap_calloc(heap, memp, sizeof(*img));\n\n  img->base.type = AK_IMAGE_TYPE_2D;\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_size_exact)) {\n      AkSizeExact *sizeExact;\n      \n      sizeExact = ak_heap_calloc(heap, img, sizeof(*sizeExact));\n      \n      sizeExact->width  = xmla_u32(xmla(xml, _s_dae_width), 0);\n      sizeExact->height = xmla_u32(xmla(xml, _s_dae_height), 0);\n      img->sizeExact    = sizeExact;\n    } else if (xml_tag_eq(xml, _s_dae_size_ratio)) {\n      AkSizeRatio *sizeRatio;\n      \n      sizeRatio = ak_heap_calloc(heap, img, sizeof(*sizeRatio));\n      \n      sizeRatio->width  = xmla_float(xmla(xml, _s_dae_width), 0);\n      sizeRatio->height = xmla_float(xmla(xml, _s_dae_height), 0);\n      img->sizeRatio    = sizeRatio;\n    } else if (xml_tag_eq(xml, _s_dae_mips)) {\n      AkMips *mips;\n      \n      mips = ak_heap_calloc(heap, img, sizeof(*mips));\n      \n      mips->levels       = xmla_u32(xmla(xml, _s_dae_levels), 0);\n      mips->autoGenerate = xmla_u32(xmla(xml, _s_dae_auto_generate), 0);\n      img->mips          = mips;\n    } else if (xml_tag_eq(xml, _s_dae_unnormalized)) {\n      img->unnormalized = xml_strdup(xml, heap, img);\n    } else if (xml_tag_eq(xml, _s_dae_array)) {\n      img->base.arrayLen = xmla_u32(xmla(xml, _s_dae_length), 0);\n    } else if (xml_tag_eq(xml, _s_dae_format)) {\n      img->base.format = dae_imageFormat(dst, xml, img);\n    } else if (xml_tag_eq(xml, _s_dae_size_exact)) {\n      img->base.initFrom = dae_initFrom(dst, xml, img);\n    }\n    xml = xml->next;\n  }\n\n  return img;\n}\n\nstatic\nAkImage3d*\ndae_create3d(DAEState * __restrict dst,\n             xml_t    * __restrict xml,\n             void     * __restrict memp) {\n  AkHeap    *heap;\n  AkImage3d *img;\n\n  heap = dst->heap;\n  img  = ak_heap_calloc(heap, memp, sizeof(*img));\n  \n  img->base.type = AK_IMAGE_TYPE_3D;\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_size)) {\n      img->size.width  = xmla_u32(xmla(xml, _s_dae_width),  0);\n      img->size.height = xmla_u32(xmla(xml, _s_dae_height), 0);\n      img->size.depth  = xmla_u32(xmla(xml, _s_dae_depth),  0);\n    } else if (xml_tag_eq(xml, _s_dae_mips)) {\n      img->mips.levels       = xmla_u32(xmla(xml, _s_dae_levels), 0);\n      img->mips.autoGenerate = xmla_u32(xmla(xml, _s_dae_auto_generate), 0);\n    } else if (xml_tag_eq(xml, _s_dae_array)) {\n      img->base.arrayLen = xmla_u32(xmla(xml, _s_dae_length), 0);\n    } else if (xml_tag_eq(xml, _s_dae_format)) {\n      img->base.format = dae_imageFormat(dst, xml, img);\n    } else if (xml_tag_eq(xml, _s_dae_size_exact)) {\n      img->base.initFrom = dae_initFrom(dst, xml, img);\n    }\n    xml = xml->next;\n  }\n\n  return img;\n}\n\nstatic\nAkImageCube*\ndae_createCube(DAEState * __restrict dst,\n               xml_t    * __restrict xml,\n               void     * __restrict memp) {\n  AkHeap      *heap;\n  AkImageCube *img;\n  \n  heap = dst->heap;\n  img  = ak_heap_calloc(heap, memp, sizeof(*img));\n  \n  img->base.type = AK_IMAGE_TYPE_CUBE;\n  \n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_size)) {\n      img->width = xmla_u32(xmla(xml, _s_dae_width),  0);\n    } else if (xml_tag_eq(xml, _s_dae_mips)) {\n      img->mips.levels       = xmla_u32(xmla(xml, _s_dae_levels), 0);\n      img->mips.autoGenerate = xmla_u32(xmla(xml, _s_dae_auto_generate), 0);\n    } else if (xml_tag_eq(xml, _s_dae_array)) {\n      img->base.arrayLen = xmla_u32(xmla(xml, _s_dae_length), 0);\n    } else if (xml_tag_eq(xml, _s_dae_format)) {\n      img->base.format = dae_imageFormat(dst, xml, img);\n    } else if (xml_tag_eq(xml, _s_dae_size_exact)) {\n      img->base.initFrom = dae_initFrom(dst, xml, img);\n    }\n    xml = xml->next;\n  }\n\n  return img;\n}\n"
  },
  {
    "path": "src/io/dae/fx/img.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_fx_img_h\n#define dae_fx_img_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid*\ndae_image(DAEState * __restrict dst,\n          xml_t    * __restrict xml,\n          void     * __restrict memp);\n\nAK_HIDE\nAkInstanceBase*\ndae_instImage(DAEState * __restrict dst,\n              xml_t    * __restrict xml,\n              void     * __restrict memp);\n\n#endif /* dae_fx_img_h */\n"
  },
  {
    "path": "src/io/dae/fx/mat.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"mat.h\"\n#include \"effect.h\"\n#include \"../core/asset.h\"\n#include \"../core/param.h\"\n#include \"../core/techn.h\"\n\nAK_HIDE\nvoid*\ndae_material(DAEState * __restrict dst,\n             xml_t    * __restrict xml,\n             void     * __restrict memp) {\n  AkHeap     *heap;\n  AkMaterial *mat;\n\n  heap = dst->heap;\n  mat  = ak_heap_calloc(heap, memp, sizeof(*mat));\n  \n  xmla_setid(xml, heap, mat);\n  \n  mat->name = xmla_strdup_by(xml, heap, _s_dae_name, mat);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, mat, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_inst_effect)) {\n      AkInstanceEffect *instEffect;\n\n      if ((instEffect = dae_instEffect(dst, xml, mat))) {\n        if (mat->effect) {\n          mat->effect->base.prev = &instEffect->base;\n          instEffect->base.next  = &mat->effect->base;\n        }\n      \n        mat->effect = instEffect;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      mat->extra = tree_fromxml(heap, mat, xml);\n    }\n    xml = xml->next;\n  }\n\n  return mat;\n}\n\nAK_HIDE\nAkBindMaterial*\ndae_bindMaterial(DAEState * __restrict dst,\n                 xml_t    * __restrict xml,\n                 void     * __restrict memp) {\n  AkHeap         *heap;\n  AkBindMaterial *bindmat;\n\n  heap    = dst->heap;\n  bindmat = ak_heap_calloc(heap, memp, sizeof(*bindmat));\n  \n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_param)) {\n      AkParam *param;\n      if ((param = dae_param(dst, xml, bindmat))) {\n        if (bindmat->param) {\n          bindmat->param->prev = param;\n          param->next          = bindmat->param;\n        }\n        bindmat->param = param;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_techniquec)) {\n      AkInstanceMaterial *imat;\n      xml_t              *ximat;\n      \n      ximat = xml->val;\n      while (ximat) {\n        if (xml_tag_eq(ximat, _s_dae_instance_material)) {\n          if ((imat = dae_instMaterial(dst, ximat, bindmat))) {\n            if (bindmat->tcommon) {\n              bindmat->tcommon->base.prev = &imat->base;\n              imat->base.next             = &bindmat->tcommon->base;\n            }\n\n            bindmat->tcommon = imat;\n          }\n        }\n        ximat = ximat->next;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_technique)) {\n      AkTechnique *tq;\n      if ((tq = dae_techn(xml, heap, bindmat))) {\n        tq->next           = bindmat->technique;\n        bindmat->technique = tq;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      bindmat->extra = tree_fromxml(heap, bindmat, xml);\n    }\n    xml = xml->next;\n  }\n\n  return bindmat;\n}\n\nAK_HIDE\nAkInstanceMaterial*\ndae_instMaterial(DAEState * __restrict dst,\n                 xml_t    * __restrict xml,\n                 void     * __restrict memp) {\n  AkHeap             *heap;\n  AkInstanceMaterial *mat;\n  xml_attr_t         *att;\n\n  heap = dst->heap;\n  mat  = ak_heap_calloc(heap, memp, sizeof(*mat));\n\n  sid_set(xml, heap, mat);\n\n  mat->base.name = xmla_strdup_by(xml, heap, _s_dae_name,   mat);\n  mat->symbol    = xmla_strdup_by(xml, heap, _s_dae_symbol, mat);\n\n  url_set(dst, xml, _s_dae_target, mat, &mat->base.url);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_bind)) {\n      AkBind *bind;\n      bind = ak_heap_calloc(heap, mat, sizeof(*bind));\n      \n      bind->semantic = xmla_strdup_by(xml, heap, _s_dae_semantic, mat);\n      bind->target   = xmla_strdup_by(xml, heap, _s_dae_target,   mat);\n      \n      bind->next = mat->bind;\n      mat->bind  = bind;\n    } else if (xml_tag_eq(xml, _s_dae_bind_vertex_input)) {\n      AkBindVertexInput *bvi;\n      bvi = ak_heap_calloc(heap, mat, sizeof(*bvi));\n      \n      bvi->semantic      = xmla_strdup_by(xml, heap, _s_dae_semantic, mat);\n      bvi->inputSemantic = xmla_strdup_by(xml, heap, _s_dae_input_semantic,\n                                          mat);\n\n      if ((att = xmla(xml, _s_dae_input_set)))\n        bvi->inputSet = xmla_u32(att, 0);\n      \n      bvi->next            = mat->bindVertexInput;\n      mat->bindVertexInput = bvi;\n    } else if (xml_tag_eq(xml, _s_dae_technique_override)) {\n      AkTechniqueOverride *technOv;\n\n      technOv       = ak_heap_calloc(heap, mat, sizeof(*technOv));\n      technOv->pass = xmla_strdup_by(xml, heap, _s_dae_pass, technOv);\n      technOv->ref  = xmla_strdup_by(xml, heap, _s_dae_ref,  technOv);\n      \n      mat->techniqueOverride = technOv;\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      mat->base.extra = tree_fromxml(heap, mat, xml);\n    }\n    xml = xml->next;\n  }\n\n  return mat;\n}\n"
  },
  {
    "path": "src/io/dae/fx/mat.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_fx_material_h\n#define dae_fx_material_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid*\ndae_material(DAEState * __restrict dst,\n             xml_t    * __restrict xml,\n             void     * __restrict memp);\n\nAK_HIDE\nAkBindMaterial*\ndae_bindMaterial(DAEState * __restrict dst,\n                 xml_t    * __restrict xml,\n                 void     * __restrict memp);\n\nAK_HIDE\nAkInstanceMaterial*\ndae_instMaterial(DAEState * __restrict dst,\n                 xml_t    * __restrict xml,\n                 void     * __restrict memp);\n\n#endif /* dae_fx_material_h */\n"
  },
  {
    "path": "src/io/dae/fx/profile.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"profile.h\"\n#include \"techn.h\"\n\n#include \"../core/param.h\"\n#include \"../core/asset.h\"\n#include \"../1.4/image.h\"\n\nAK_HIDE\nAkProfile*\ndae_profile(DAEState * __restrict dst,\n            xml_t    * __restrict xml,\n            void     * __restrict memp) {\n  AkHeap    *heap;\n  AkProfile *profile;\n\n  heap = dst->heap;\n\n  if (!xml_tag_eq(xml, _s_dae_prfl_common))\n    return NULL;\n \n  profile       = ak_heap_calloc(heap, memp, sizeof(AkProfileCommon));\n  profile->type = AK_PROFILE_TYPE_COMMON;\n\n  ak_setypeid(profile, AKT_PROFILE);\n  xmla_setid(xml, heap, profile);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, profile, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_newparam)) {\n      AkNewParam *newparam;\n      \n      if ((newparam = dae_newparam(dst, xml, profile))) {\n        if (profile->newparam)\n          profile->newparam->prev = newparam;\n\n        newparam->next    = profile->newparam;\n        profile->newparam = newparam;\n      }\n    } else if (xml_tag_eq(xml, _s_dae_technique)) {\n      AkTechniqueFx *techn;\n      \n      if ((techn = dae_techniqueFx(dst, xml, profile))) {\n        techn->next        = profile->technique;\n        profile->technique = techn;\n      }\n    } else if (dst->version < AK_COLLADA_VERSION_150\n               && xml_tag_eq(xml, _s_dae_image)) {\n      /* migration from 1.4 */\n      dae14_fxMigrateImg(dst, xml, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      profile->extra = tree_fromxml(heap, profile, xml);\n    }\n    xml = xml->next;\n  }\n\n  return profile;\n}\n"
  },
  {
    "path": "src/io/dae/fx/profile.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_fx_profile_h\n#define dae_fx_profile_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkProfile*\ndae_profile(DAEState * __restrict dst,\n            xml_t    * __restrict xml,\n            void     * __restrict memp);\n\n#endif /* dae_fx_profile_h */\n"
  },
  {
    "path": "src/io/dae/fx/samp.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"samp.h\"\n#include \"img.h\"\n#include \"../core/color.h\"\n#include \"../core/enum.h\"\n\n#include \"../1.4/dae14.h\"\n#include \"../1.4/surface.h\"\n\nAK_HIDE\nAkSampler*\ndae_sampler(DAEState * __restrict dst,\n            xml_t    * __restrict xml,\n            void     * __restrict memp) {\n  AkHeap    *heap;\n  AkSampler *samp;\n\n  heap = dst->heap;\n  samp = ak_heap_calloc(heap, memp, sizeof(*samp));\n  ak_setypeid(samp, AKT_SAMPLER);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_source)) {\n      /* COLLADA 1.4 uses source -> <surface> for texturing */\n      if (dst->version < AK_COLLADA_VERSION_150) {\n        dae14_loadjobs_add(dst,\n                           samp,\n                           xml_strdup(xml, heap, samp),\n                           AK_DAE14_LOADJOB_SURFACE);\n      }\n    } else if (xml_tag_eq(xml, _s_dae_instance_image)) {\n      AkInstanceBase *instImage;\n      if ((instImage = dae_instImage(dst, xml, samp)))\n        rb_insert(dst->instanceMap, samp, instImage);\n    } else if (xml_tag_eq(xml, _s_dae_wrap_s)) {\n      samp->wrapS = dae_wrap(xml);\n    } else if (xml_tag_eq(xml, _s_dae_wrap_t)) {\n      samp->wrapT = dae_wrap(xml);\n    } else if (xml_tag_eq(xml, _s_dae_wrap_p)) {\n      samp->wrapP = dae_wrap(xml);\n    } else if (xml_tag_eq(xml, _s_dae_minfilter)) {\n      samp->minfilter = dae_minfilter(xml);\n    } else if (xml_tag_eq(xml, _s_dae_magfilter)) {\n      samp->magfilter = dae_magfilter(xml);\n    } else if (xml_tag_eq(xml, _s_dae_mipfilter)) {\n      samp->mipfilter = dae_mipfilter(xml);\n    } else if (xml_tag_eq(xml, _s_dae_border_color)) {\n      AkColor *color;\n\n      color = ak_heap_calloc(heap, samp, sizeof(*color));\n      dae_color(xml, samp, true, false, color);\n      \n      samp->borderColor = color;\n    } else if (xml_tag_eq(xml, _s_dae_mip_max_level)) {\n      samp->mipMaxLevel = xml_u32(xml, 0);\n    } else if (xml_tag_eq(xml, _s_dae_mip_min_level)) {\n      samp->mipMinLevel = xml_u32(xml, 0);\n    } else if (xml_tag_eq(xml, _s_dae_mip_bias)) {\n      samp->mipBias = xml_float(xml, 0);\n    } else if (xml_tag_eq(xml, _s_dae_max_anisotropy)) {\n      samp->maxAnisotropy = xml_u32(xml, 1l);\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      samp->extra = tree_fromxml(heap, samp, xml);\n    }\n    xml = xml->next;\n  }\n\n  return samp;\n}\n"
  },
  {
    "path": "src/io/dae/fx/samp.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_fx_sampler_h\n#define dae_fx_sampler_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkSampler*\ndae_sampler(DAEState * __restrict dst,\n            xml_t    * __restrict xml,\n            void     * __restrict memp);\n\n#endif /* dae_fx_sampler_h */\n"
  },
  {
    "path": "src/io/dae/fx/techn.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"techn.h\"\n#include \"colortex.h\"\n#include \"fltprm.h\"\n\n#include \"../core/asset.h\"\n#include \"../core/enum.h\"\n#include \"../1.4/image.h\"\n#include \"../bugfix/transp.h\"\n#include \"../../../default/material.h\"\n\nstatic\nAkTechniqueFxCommon*\ndae_techniqueFxCmn(DAEState * __restrict dst,\n                   xml_t    * __restrict xml,\n                   void     * __restrict memp,\n                   AkMaterialType        mattype);\n\nAK_HIDE\nAkTechniqueFx*\ndae_techniqueFx(DAEState * __restrict dst,\n                xml_t    * __restrict xml,\n                void     * __restrict memp) {\n  AkHeap              *heap;\n  AkTechniqueFx       *techn;\n  AkMaterialType       m;\n\n  heap  = dst->heap;\n  techn = ak_heap_calloc(heap, memp, sizeof(*techn));\n  ak_setypeid(techn, AKT_TECHNIQUE_FX);\n\n  xmla_setid(xml, heap, techn);\n  sid_set(xml, heap, techn);\n\n  xml = xml->val;\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_asset)) {\n      (void)dae_asset(dst, xml, techn, NULL);\n    } else if ((xml_tag_eq(xml, _s_dae_phong)   && (m = AK_MATERIAL_PHONG))\n           || (xml_tag_eq(xml, _s_dae_blinn)    && (m = AK_MATERIAL_BLINN))\n           || (xml_tag_eq(xml, _s_dae_lambert)  && (m = AK_MATERIAL_LAMBERT))\n           || (xml_tag_eq(xml, _s_dae_constant) && (m = AK_MATERIAL_CONSTANT))) {\n      techn->common = dae_techniqueFxCmn(dst, xml, techn, m);\n    } else if (dst->version < AK_COLLADA_VERSION_150\n               && xml_tag_eq(xml, _s_dae_image)) {\n      /* migration from 1.4 */\n      dae14_fxMigrateImg(dst, xml, NULL);\n    } else if (xml_tag_eq(xml, _s_dae_extra)) {\n      techn->extra = tree_fromxml(heap, techn, xml);\n    }\n    xml = xml->next;\n  }\n\n  return techn;\n}\n\nstatic\nvoid\ndae_colorDescTextureUsage(DAEState            * __restrict dst,\n                          AkColorDesc         * __restrict clr,\n                          AkTextureColorSpace              colorSpace,\n                          AkTextureChannels                channels) {\n  AkDAETextureRef *tex;\n\n  if (!clr || !dst->texmap)\n    return;\n\n  if ((tex = rb_find(dst->texmap, clr))) {\n    tex->colorSpace = colorSpace;\n    tex->channels   = channels;\n  }\n}\n\nstatic\nAkTextureChannels\ndae_transparentTextureChannels(AkOpaque opaque) {\n  switch (opaque) {\n    case AK_OPAQUE_A_ONE:\n    case AK_OPAQUE_A_ZERO:\n    case AK_OPAQUE_MASK:\n      return AK_TEXTURE_CHANNEL_A;\n    case AK_OPAQUE_RGB_ONE:\n    case AK_OPAQUE_RGB_ZERO:\n      return AK_TEXTURE_CHANNEL_RGB;\n    default:\n      return AK_TEXTURE_CHANNEL_RGBA;\n  }\n}\n\nstatic\nAkTechniqueFxCommon*\ndae_techniqueFxCmn(DAEState * __restrict dst,\n                   xml_t    * __restrict xml,\n                   void     * __restrict memp,\n                   AkMaterialType        mattype) {\n  AkHeap              *heap;\n  AkTechniqueFxCommon *techn;\n  xml_attr_t          *att;\n  AkTransparent       *transp;\n  AkOpaque             opaque;\n\n  heap        = dst->heap;\n  techn       = ak_heap_calloc(heap, memp, sizeof(*techn));\n  techn->type = mattype;\n  xml         = xml->val;\n\n  while (xml) {\n    if (xml_tag_eq(xml, _s_dae_emission)) {\n      AkMaterialEmissionProp *emission;\n\n      if (!(emission = techn->emission)) {\n        emission           = ak_heap_calloc(heap, techn, sizeof(*emission));\n        techn->emission    = emission;\n        emission->strength = 1.0f;\n      }\n\n      dae_colorOrTexSet(dst, xml, techn, &techn->emission->color);\n      dae_colorDescTextureUsage(dst, &techn->emission->color,\n                                AK_TEXTURE_COLORSPACE_SRGB,\n                                AK_TEXTURE_CHANNEL_RGB);\n    } else if (xml_tag_eq(xml, _s_dae_ambient)) {\n      techn->ambient = dae_colorOrTex(dst, xml, techn);\n      dae_colorDescTextureUsage(dst, techn->ambient,\n                                AK_TEXTURE_COLORSPACE_SRGB,\n                                AK_TEXTURE_CHANNEL_RGB);\n    } else if (xml_tag_eq(xml, _s_dae_diffuse)) {\n      techn->diffuse = dae_colorOrTex(dst, xml, techn);\n      dae_colorDescTextureUsage(dst, techn->diffuse,\n                                AK_TEXTURE_COLORSPACE_SRGB,\n                                AK_TEXTURE_CHANNEL_RGBA);\n    } else if (xml_tag_eq(xml, _s_dae_specular)) {\n      AkMaterialSpecularProp *specularProp;\n\n      if (!(specularProp = techn->specular)) {\n        specularProp    = ak_heap_calloc(heap, techn, sizeof(*specularProp));\n        techn->specular = specularProp;\n      }\n\n      specularProp->color = dae_colorOrTex(dst, xml, specularProp);\n      dae_colorDescTextureUsage(dst, specularProp->color,\n                                AK_TEXTURE_COLORSPACE_SRGB,\n                                AK_TEXTURE_CHANNEL_RGB);\n    } else if (xml_tag_eq(xml, _s_dae_reflective)) {\n      if (!techn->reflective)\n        techn->reflective = ak_heap_calloc(heap, techn, sizeof(*techn->reflective));\n      techn->reflective->color = dae_colorOrTex(dst, xml, techn);\n      dae_colorDescTextureUsage(dst, techn->reflective->color,\n                                AK_TEXTURE_COLORSPACE_SRGB,\n                                AK_TEXTURE_CHANNEL_RGB);\n    } else if (xml_tag_eq(xml, _s_dae_transparent)) {\n      if (!techn->transparent) {\n        transp             = ak_heap_calloc(heap, techn, sizeof(*transp));\n        transp->amount     = 1.0f;\n        techn->transparent = transp;\n      }\n      \n      if ((att = xmla(xml, _s_dae_opaque)))\n        opaque = dae_opaque(att);\n      else\n        opaque = AK_OPAQUE_A_ONE;\n      \n      techn->transparent->color  = dae_colorOrTex(dst, xml, techn);\n      techn->transparent->opaque = opaque;\n      dae_colorDescTextureUsage(dst, techn->transparent->color,\n                                AK_TEXTURE_COLORSPACE_SRGB,\n                                dae_transparentTextureChannels(opaque));\n    } else if (xml_tag_eq(xml, _s_dae_shininess)) {\n      AkMaterialSpecularProp *specularProp;\n\n      if (!(specularProp = techn->specular)) {\n        specularProp    = ak_heap_calloc(heap, techn, sizeof(*specularProp));\n        techn->specular = specularProp;\n      }\n\n      specularProp->strength = dae_float(dst, xml, specularProp, \n                                         offsetof(AkMaterialSpecularProp, shininess), 1.0f);\n    } else if (xml_tag_eq(xml, _s_dae_reflectivity)) {\n      if (!techn->reflective)\n        techn->reflective = ak_heap_calloc(heap, techn, sizeof(*techn->reflective));\n      techn->reflective->amount = dae_float(dst, xml, techn->reflective, \n                                            offsetof(AkReflective, amount), 0.0f);\n    } else if (xml_tag_eq(xml, _s_dae_transparency)) {\n      if (!techn->transparent) {\n        transp             = ak_heap_calloc(heap, techn, sizeof(*transp));\n        transp->amount     = 1.0f;\n        techn->transparent = transp;\n      }\n      techn->transparent->amount = dae_float(dst, xml, techn->transparent,\n                                             offsetof(AkTransparent, amount), 1.0f);\n\n      /* some old version of tools e.g. SketchUp exports incorrect */\n      if (ak_opt_get(AK_OPT_BUGFIXES))\n        dae_bugfix_transp(techn->transparent);\n    } else if (xml_tag_eq(xml, _s_dae_index_of_refraction)) {\n      /* TODO: assumed 0.0 for COLLADA */\n      techn->ior = dae_float(dst, xml, techn,\n                             offsetof(AkTechniqueFxCommon, ior), 0.0f);\n    }\n    xml = xml->next;\n  }\n\n  return techn;\n}\n"
  },
  {
    "path": "src/io/dae/fx/techn.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_technique_fx_h\n#define dae_technique_fx_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkTechniqueFx*\ndae_techniqueFx(DAEState * __restrict dst,\n                xml_t    * __restrict xml,\n                void     * __restrict memp);\n\n#endif /* dae_technique_fx_h */\n"
  },
  {
    "path": "src/io/dae/postscript.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"postscript.h\"\n#include \"../../xml.h\"\n\n#include \"1.4/dae14.h\"\n\n#include \"fixup/geom.h\"\n#include \"fixup/angle.h\"\n#include \"fixup/tex.h\"\n#include \"fixup/ctlr.h\"\n#include \"fixup/channel.h\"\n#include \"bugfix/scenekit.h\"\n\nAK_HIDE void\ndae_retain_refs(DAEState * __restrict dst);\n\nAK_HIDE void\ndae_fixup_accessors(DAEState * __restrict dst);\n\nAK_HIDE void\ndae_pre_mesh(DAEState * __restrict dst);\n\nAK_HIDE void\ndae_pre_walk(RBTree *tree, RBNode *rbnode);\n\nAK_HIDE void\ndae_input_walk(RBTree *tree, RBNode *rbnode);\n\nAK_HIDE void\ndae_attach_orphan_morphs(DAEState * __restrict dst);\n\nAK_HIDE\nvoid\ndae_spread_vert(DAEState * __restrict dst) {\n  AkHeap               *heap;\n  AkVertices           *vert;\n  AkMeshPrimitive      *prim;\n  AkDAEVerticesMapItem *item;\n  FListItem            *fitem;\n  AkInput              *inp;\n  AkInput              *inpv;\n  AkURL                *url;\n\n  if (!(heap = dst->heap) || !(fitem = dst->vertMap)) {\n    return;\n  }\n\n  /* copy <vertices> to all primitives */\n  do {\n    item = fitem->data;\n    prim = item->prim;\n    inp  = item->inp;\n    url  = rb_find(dst->inputmap, inp);\n\n    if (!(vert = ak_getObjectByUrl(url)))\n      continue;\n\n    inpv = vert->input;\n\n    while (inpv) {\n      inp  = ak_heap_calloc(heap, prim, sizeof(*inp));\n      inp->semantic = inpv->semantic;\n      if (inpv->semanticRaw)\n        inp->semanticRaw = ak_heap_strdup(heap, inp, inpv->semanticRaw);\n\n      inp->offset = prim->reserved1;\n      inp->set    = prim->reserved2;\n      inp->next   = prim->input;\n      prim->input = inp;\n\n      if (inp->semantic == AK_INPUT_POSITION) {\n        prim->pos = inp;\n        if (!rb_find(dst->meshInfo, prim->mesh)) {\n          AkDaeMeshInfo *mi;\n\n          mi      = ak_heap_calloc(heap, NULL, sizeof(*mi));\n          mi->pos = inp;\n\n          rb_insert(dst->meshInfo, prim->mesh, mi);\n        }\n      }\n\n      if ((url = rb_find(dst->inputmap, inpv))) {\n        ak_url_dup(url, inp, url);\n        rb_insert(dst->inputmap, inp, url);\n      }\n\n      prim->inputCount++;\n      inpv = inpv->next;\n    }\n\n    /* cleanup will be automatically,\n       because same vertices may be used in multiple places\n     */\n  } while ((fitem = fitem->next));\n}\n\nAK_HIDE void\ndae_postscript(DAEState * __restrict dst) {\n  AkCoordCvtType coordCvtType;\n  AkCoordSys    *sourceCoordSys, *targetCoordSys;\n  bool           fixTransform;\n\n  coordCvtType    = (AkCoordCvtType)ak_opt_get(AK_OPT_COORD_CONVERT_TYPE);\n  sourceCoordSys  = dst->doc ? dst->doc->coordSys : NULL;\n  targetCoordSys  = (void *)ak_opt_get(AK_OPT_COORD);\n  fixTransform    = coordCvtType == AK_COORD_CVT_FIX_TRANSFORM\n                    && sourceCoordSys\n                    && targetCoordSys\n                    && sourceCoordSys != targetCoordSys\n                    && !ak_coordOrientationIsEq(sourceCoordSys, targetCoordSys);\n\n  dae_spread_vert(dst);\n\n  /* first migrate 1.4 to 1.5 */\n  if (dst->version < AK_COLLADA_VERSION_150)\n    dae14_loadjobs_finish(dst);\n\n  dae_retain_refs(dst);\n  rb_walk(dst->inputmap, dae_input_walk);\n  dae_fixAngles(dst);\n  dae_fixup_accessors(dst);\n  dae_pre_mesh(dst);\n  dae_bugfix_scenekit_backfaces(dst);\n\n  /* fixup when finished,\n     because we need to collect about source/array usages\n     also we can run fixups as parallels here\n  */\n  if (!ak_opt_get(AK_OPT_INDICES_DEFAULT))\n    dae_geom_fixup_all(dst->doc);\n\n  /* fixup morph and skin because order of vertices may be changed */\n  if (dst->doc->lib.controllers) {\n    dae_fixup_ctlr(dst);\n    dae_fixup_instctlr(dst);\n\n    /* Soft attach: many DAE assets — particularly glTF→DAE converted ones —\n       reference the morph base mesh via <instance_geometry url=\"#mesh\"/>\n       and forget to wrap it in <instance_controller>. The morph controller\n       is in the library but never instanced, so dae_fixup_instctlr never\n       sees it. Walk the scene graph and attach any morph controller that\n       targets this geometry. */\n    dae_attach_orphan_morphs(dst);\n  }\n\n  /* Resolve animation channel targets that need controller/instance\n     topology — currently the indexed-array form used for morph weights,\n     e.g. <channel target=\"morph-weights(0)\"/>. Must run after morph\n     instances exist (including the orphan-attach pass above). */\n  if (dst->doc->lib.animations)\n    dae_fixup_channel(dst);\n\n  /* now set used coordSys */\n  if (coordCvtType != AK_COORD_CVT_DISABLED)\n    dst->doc->coordSys = targetCoordSys;\n\n  dae_fix_textures(dst);\n  \n  if (dst->doc && dst->doc->lib.visualScenes) {\n    for (AkVisualScene *vscn = (void *)dst->doc->lib.visualScenes->chld;\n         vscn;\n         vscn = (void *)vscn->base.next) {\n      if (fixTransform)\n        ak_fixSceneCoordSys(vscn);\n    }\n  }\n}\n\nAK_HIDE void\ndae_retain_refs(DAEState * __restrict dst) {\n  AkHeapAllocator *alc;\n  AkURLQueue      *it, *tofree;\n  AkURL           *url;\n  AkHeapNode      *hnode;\n  int             *refc;\n  AkResult         ret;\n\n  alc = dst->heap->allocator;\n  it  = dst->urlQueue;\n\n  while (it) {\n    url    = it->url;\n    tofree = it;\n\n    /* currently only retain objects in this doc */\n    if (it->url->doc == dst->doc) {\n      hnode = NULL;\n      ret   = ak_heap_getNodeByURL(dst->heap, url, &hnode);\n      if (ret == AK_OK && hnode) {\n        /* retain <source> and source arrays ... */\n        refc         = ak_heap_ext_add(dst->heap, hnode, AK_HEAP_NODE_FLAGS_REFC);\n        it->url->ptr = ak__alignas(hnode);\n\n        (*refc)++;\n      }\n    }\n\n    it = it->next;\n    alc->free(tofree);\n  }\n}\n\n/*\n * For every <instance_geometry> in the scene graph, check whether the\n * geometry it references has a registered morph controller in\n * meshTargets. If so, synthesize an AkInstanceMorph and attach it. This\n * compensates for DAE exporters (typically glTF→DAE converters) that\n * emit a morph controller but reference the base geometry directly via\n * <instance_geometry> rather than wrapping it in <instance_controller>.\n *\n * Idempotent: skips instGeoms that already have a morpher (real\n * <instance_controller> path already attached one in dae_fixup_instctlr).\n */\nstatic\nvoid\ndae_attach_orphan_morphs_node(DAEState * __restrict dst, AkNode *node) {\n  AkInstanceGeometry *instGeom;\n  AkInstanceMorph    *instMorph;\n  AkMorph            *morph;\n  AkGeometry         *geom;\n\n  for (; node; node = (AkNode *)node->next) {\n    for (instGeom = node->geometry; instGeom;\n         instGeom = (AkInstanceGeometry *)instGeom->base.next) {\n      if (instGeom->morpher)                         continue;\n      if (!(geom = instGeom->base.url.ptr))          continue;\n      if (ak_typeid(geom) != AKT_GEOMETRY)           continue;\n      if (!(morph = rb_find(dst->meshTargets, geom))) continue;\n\n      instMorph                  = ak_heap_calloc(dst->heap, node,\n                                                  sizeof(*instMorph));\n      instMorph->morph           = morph;\n      instMorph->overrideWeights = NULL;\n      instGeom->morpher          = instMorph;\n    }\n\n    if (node->chld)\n      dae_attach_orphan_morphs_node(dst, node->chld);\n  }\n}\n\nAK_HIDE void\ndae_attach_orphan_morphs(DAEState * __restrict dst) {\n  AkVisualScene *vscn;\n\n  if (!dst->meshTargets || !dst->doc->lib.visualScenes) return;\n\n  for (vscn = (void *)dst->doc->lib.visualScenes->chld;\n       vscn;\n       vscn = (void *)vscn->base.next) {\n    dae_attach_orphan_morphs_node(dst, vscn->node);\n  }\n}\n\nAK_HIDE void\ndae_input_walk(RBTree *tree, RBNode *rbnode) {\n  AkAccessor *acc;\n  AkSource   *src;\n  AkInput    *inp;\n  AkURL      *url;\n\n  AK__UNUSED(tree);\n\n  inp = rbnode->key;\n  if (inp->semantic == AK_INPUT_SEMANTIC_VERTEX) {\n    return;\n  }\n\n  url = rbnode->val;\n  if (!(src = ak_getObjectByUrl(url)))\n    return;\n\n  acc           = src->tcommon;\n  inp->accessor = acc;\n\n  /* TODO: handle error if null?? */\n  if (acc) {\n    ak_retain(acc);\n  }\n\n  /* TODO: */\n//  ak_free(src);\n//  ak_free(url);\n//\n//  rb_destroy(tree);\n}\n\nAK_HIDE void\ndae_fixup_accessors(DAEState * __restrict dst) {\n  AkHeap        *heap;\n  AkDoc         *doc;\n  FListItem     *item;\n  AkAccessor    *acc;\n  AkAccessorDAE *accdae;\n  AkBuffer      *buff;\n  AkTypeDesc    *type;\n\n  item = dst->accessors;\n  heap = dst->heap;\n  doc  = dst->doc;\n  \n  while (item) {\n    acc         = item->data;\n    accdae      = ak_userData(acc);\n    buff        = ak_getObjectByUrl(&accdae->source);\n    acc->buffer = buff;\n\n    if ((buff = ak_getObjectByUrl(&accdae->source))) {\n      AkBuffer    *newbuff;\n      AkDataParam *dp;\n      char        *olditms, *newitms;\n      uint32_t     i, j, count, dpoff, bytesPerComponent;\n      size_t       oldByteStride, newByteStride;\n\n      acc->componentType = (AkTypeId)(uintptr_t)ak_userData(buff);\n\n      if ((type = ak_typeDesc(acc->componentType)))\n        bytesPerComponent = type->size;\n      else\n        goto cont;\n\n      count                  = acc->count;\n      acc->byteStride        = accdae->stride * bytesPerComponent;\n      acc->byteLength        = count * accdae->stride * bytesPerComponent;\n      acc->byteOffset        = accdae->offset * bytesPerComponent;\n      accdae->bound          = accdae->stride;\n\n      acc->fillByteSize      = accdae->bound * bytesPerComponent;\n      acc->componentCount    = accdae->bound;\n      acc->bytesPerComponent = bytesPerComponent;\n\n      /*--------------------------------------------------------------------*\n\n         eliminate / remove Data Params e.g. X, Y, Z\n         to make Accessor more small and cleaner\n       \n       *--------------------------------------------------------------------*/\n      \n      /* the buffer is used more than one place, so duplicate data */\n      /* TODO: check param that has empty name */\n      if (acc->buffer && ak_refc(buff) > 1) {\n        oldByteStride   = acc->byteStride;\n        newByteStride   = accdae->bound * bytesPerComponent;\n        newbuff         = ak_heap_calloc(heap, doc, sizeof(*newbuff));\n        newbuff->length = count * newByteStride;\n        newbuff->data   = ak_heap_calloc(heap, newbuff, newbuff->length);\n\n        newitms         = (char *)newbuff->data;\n        olditms         = (char *)buff->data + acc->byteOffset;\n        \n        for (i = 0; i < count; i++) {\n          j     = 0;\n          dpoff = 0;\n          dp    = accdae->param;\n\n          while (dp) {\n            if (dp->name) {\n              memcpy(newitms + newByteStride * i + bytesPerComponent * j++,\n                     olditms + oldByteStride * i + dpoff,\n                     dp->type.size);\n            }\n\n            dpoff += dp->type.size;\n            dp     = dp->next;\n          }\n        }\n        \n        ak_release(acc->buffer);\n        ak_retain(newbuff);\n\n        acc->buffer = newbuff;\n      }\n\n      ak_heap_ext_rm(heap, ak__alignof(buff), AK_HEAP_NODE_FLAGS_USR);\n    }\n\n    ak_heap_ext_rm(heap, ak__alignof(accdae), AK_HEAP_NODE_FLAGS_USR);\n    ak_free(accdae);\n\n  cont:\n    item = item->next;\n  }\n\n  flist_sp_destroy(&dst->accessors);\n}\n\nAK_HIDE void\ndae_pre_walk(RBTree *tree, RBNode *rbnode) {\n  AkDaeMeshInfo *mi;\n  AkAccessor    *posAcc;\n\n  AK__UNUSED(tree);\n\n  mi     = rbnode->val;\n  posAcc = NULL;\n\n  if (!(posAcc = mi->pos->accessor))\n    return;\n\n  mi->nVertex = posAcc->count;\n}\n\nAK_HIDE void\ndae_pre_mesh(DAEState * __restrict dst) {\n  rb_walk(dst->meshInfo, dae_pre_walk);\n}\n"
  },
  {
    "path": "src/io/dae/postscript.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_postscript_h\n#define dae_postscript_h\n\n#include \"common.h\"\n\nAK_HIDE\nvoid\ndae_postscript(DAEState * __restrict dst);\n\n#endif /* dae_postscript_h */\n"
  },
  {
    "path": "src/io/dae/strpool.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _DAE_STRPOOL_\n#  define _DAE_STRPOOL_\n#endif\n\n#include \"strpool.h\"\n#include <string.h>\n\nconst char _s_dae_pool_0[] =\n\" \\0\"\n\"COLLADA\\0\"\n\"asset\\0\"\n\"contributor\\0\"\n\"author\\0\"\n\"author_email\\0\"\n\"author_website\\0\"\n\"authoring_tool\\0\"\n\"comments\\0\"\n\"copyright\\0\"\n\"source_data\\0\"\n\"created\\0\"\n\"modified\\0\"\n\"keywords\\0\"\n\"revision\\0\"\n\"subject\\0\"\n\"title\\0\"\n\"unit\\0\"\n\"name\\0\"\n\"meter\\0\"\n\"up_axis\\0\"\n\"X_UP\\0\"\n\"Z_UP\\0\"\n\"Y_UP\\0\"\n\"extra\\0\"\n\"library_cameras\\0\"\n\"id\\0\"\n\"camera\\0\"\n\"optics\\0\"\n\"technique\\0\"\n\"technique_common\\0\"\n\"profile\\0\"\n\"xmlns\\0\"\n\"perspective\\0\"\n\"xfov\\0\"\n\"yfov\\0\"\n\"sid\\0\"\n\"aspect_ratio\\0\"\n\"znear\\0\"\n\"zfar\\0\"\n\"orthographic\\0\"\n\"xmag\\0\"\n\"ymag\\0\"\n\"ambient\\0\"\n\"color\\0\"\n\"directional\\0\"\n\"point\\0\"\n\"constant_attenuation\\0\"\n\"linear_attenuation\\0\"\n\"quadratic_attenuation\\0\"\n\"spot\\0\"\n\"falloff_angle\\0\"\n\"falloff_exponent\\0\"\n\"imager\\0\"\n\"light\\0\"\n\"library_lights\\0\"\n\"library_effects\\0\"\n\"effect\\0\"\n\"newparam\\0\"\n\"profile_COMMON\\0\"\n\"string\\0\"\n\"bool\\0\"\n\"bool2\\0\"\n\"bool3\\0\"\n\"bool4\\0\"\n\"int\\0\"\n\"int2\\0\"\n\"int3\\0\"\n\"int4\\0\"\n\"float\\0\"\n\"float2\\0\"\n\"float3\\0\"\n\"float4\\0\"\n\"float2x2\\0\"\n\"float3x3\\0\"\n\"float4x4\\0\"\n\"semantic\\0\"\n\"ref\\0\"\n\"type\\0\"\n\"platform\\0\"\n\"url\\0\"\n\"pass\\0\"\n\"blinn\\0\"\n\"constant\\0\"\n\"lambert\\0\"\n\"phong\\0\"\n\"emission\\0\"\n\"diffuse\\0\"\n\"specular\\0\"\n\"shininess\\0\"\n\"reflective\\0\"\n\"reflectivity\\0\"\n\"transparent\\0\"\n\"transparency\\0\"\n\"index_of_refraction\\0\"\n\"opaque\\0\"\n\"texture\\0\"\n\"texcoord\\0\"\n\"param\\0\"\n\"func\\0\"\n\"value\\0\"\n\"src\\0\"\n\"dest\\0\"\n\"src_rgb\\0\"\n\"dest_rgb\\0\"\n\"src_alpha\\0\"\n\"dest_alpha\\0\"\n\"rgb\\0\"\n\"alpha\\0\"\n\"face\\0\"\n\"mode\\0\"\n\"mask\\0\"\n\"fail\\0\"\n\"zfail\\0\"\n\"zpass\\0\"\n\"front\\0\"\n\"back\\0\"\n\"index\\0\"\n\"instance_image\\0\"\n\"renderable\\0\"\n\"share\\0\"\n\"true\\0\"\n\"false\\0\"\n\"init_from\\0\"\n\"create_2d\\0\"\n\"create_3d\\0\"\n\"create_cube\\0\"\n\"image\\0\"\n\"mips_generate\\0\"\n\"array_index\\0\"\n\"mip_index\\0\"\n\"depth\\0\"\n\"hex\\0\"\n\"format\\0\"\n\"size_exact\\0\"\n\"width\\0\"\n\"height\\0\"\n\"size_ratio\\0\"\n\"mips\\0\"\n\"levels\\0\"\n\"auto_generate\\0\"\n\"unnormalized\\0\"\n\"array\\0\"\n\"length\\0\"\n\"hint\\0\"\n\"channels\\0\"\n\"range\\0\"\n\"precision\\0\"\n\"space\\0\"\n\"exact\\0\"\n\"size\\0\"\n\"wrap_s\\0\"\n\"wrap_t\\0\"\n\"wrap_p\\0\"\n\"minfilter\\0\"\n\"magfilter\\0\"\n\"mipfilter\\0\"\n\"border_color\\0\"\n\"mip_max_level\\0\"\n\"mip_min_level\\0\"\n\"mip_bias\\0\"\n\"max_anisotropy\\0\"\n\"target\\0\"\n\"symbol\\0\"\n\"slice\\0\"\n\"mip\\0\"\n\"library_images\\0\"\n\"library_materials\\0\"\n\"instance_effect\\0\"\n\"technique_hint\\0\"\n\"material\\0\"\n\"library_geometries\\0\"\n\"geometry\\0\"\n\"mesh\\0\"\n\"source\\0\"\n\"count\\0\"\n\"accessor\\0\"\n\"stride\\0\"\n\"X\\0\"\n\"Y\\0\"\n\"Z\\0\"\n\"vertices\\0\"\n\"input\\0\"\n\"offset\\0\"\n\"polylist\\0\"\n\"vcount\\0\"\n\"p\\0\"\n\"convex_mesh\\0\"\n\"spline\\0\"\n\"brep\\0\"\n\"lines\\0\"\n\"linestrips\\0\"\n\"polygons\\0\"\n\"triangles\\0\"\n\"trifans\\0\"\n\"tristrips\\0\"\n\"float_array\\0\"\n\"bool_array\\0\"\n\"IDREF_array\\0\"\n\"int_array\\0\"\n\"Name_array\\0\"\n\"SIDREF_array\\0\"\n\"token_array\\0\"\n\"ph\\0\"\n\"h\\0\"\n\"convex_hull_of\\0\"\n\"control_vertices\\0\"\n\"closed\\0\"\n\"set\\0\"\n\"line\\0\"\n\"circle\\0\"\n\"ellipse\\0\"\n\"parabola\\0\"\n\"hyperbola\\0\"\n\"nurbs\\0\"\n\"orient\\0\"\n\"origin\\0\"\n\"curve\\0\"\n\"direction\\0\"\n\"radius\\0\"\n\"degree\\0\"\n\"degree_u\\0\"\n\"closed_u\\0\"\n\"degree_v\\0\"\n\"closed_v\\0\"\n\"cone\\0\"\n\"plane\\0\"\n\"cylinder\\0\"\n\"nurbs_surface\\0\"\n\"sphere\\0\"\n\"torus\\0\"\n\"swept_surface\\0\"\n\"surface\\0\"\n\"angle\\0\"\n\"axis\\0\"\n\"curves\\0\"\n\"surface_curves\\0\"\n\"surfaces\\0\"\n\"edges\\0\"\n\"wires\\0\"\n\"faces\\0\"\n\"pcurves\\0\"\n;\n\nconst char _s_dae_pool_1[] =\n\"shells\\0\"\n\"solids\\0\"\n\"library_controllers\\0\"\n\"controller\\0\"\n\"skin\\0\"\n\"morph\\0\"\n\"bind_shape_matrix\\0\"\n\"joints\\0\"\n\"vertex_weights\\0\"\n\"targets\\0\"\n\"v\\0\"\n\"method\\0\"\n\"library_visual_scenes\\0\"\n\"visual_scene\\0\"\n\"node\\0\"\n\"evaluate_scene\\0\"\n\"layer\\0\"\n\"lookat\\0\"\n\"matrix\\0\"\n\"rotate\\0\"\n\"scale\\0\"\n\"skew\\0\"\n\"translate\\0\"\n\"instance_camera\\0\"\n\"instance_controller\\0\"\n\"skeleton\\0\"\n\"bind_material\\0\"\n\"instance_material\\0\"\n\"bind\\0\"\n\"bind_vertex_input\\0\"\n\"input_semantic\\0\"\n\"input_set\\0\"\n\"instance_geometry\\0\"\n\"instance_light\\0\"\n\"instance_node\\0\"\n\"proxy\\0\"\n\"render\\0\"\n\"camera_node\\0\"\n\"technique_override\\0\"\n\"enable\\0\"\n\"library_nodes\\0\"\n\"instance_visual_scene\\0\"\n\"scene\\0\"\n\"sampler1D\\0\"\n\"sampler2D\\0\"\n\"sampler3D\\0\"\n\"samplerRECT\\0\"\n\"samplerCUBE\\0\"\n\"samplerDEPTH\\0\"\n\"data\\0\"\n\"version\\0\"\n\"format_hint\\0\"\n\"viewport_ratio\\0\"\n\"mip_levels\\0\"\n\"mipmap_generate\\0\"\n\"option\\0\"\n\"init_as_target\\0\"\n\"init_cube\\0\"\n\"all\\0\"\n\"primary\\0\"\n\"order\\0\"\n\"sketchup\\0\"\n\"library_animations\\0\"\n\"animation\\0\"\n\"sampler\\0\"\n\"channel\\0\"\n\"pre_behavior\\0\"\n\"post_behavior\\0\"\n\"focal\\0\"\n\"equation\\0\"\n;\n\n#undef _DAE_STRPOOL_\n"
  },
  {
    "path": "src/io/dae/strpool.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef dae_strpool_h\n#  define dae_strpool_h\n\n#ifndef _DAE_STRPOOL_\n#  define _AK_EXTERN extern\n#else\n#  define _AK_EXTERN\n#endif\n\n_AK_EXTERN const char _s_dae_pool_0[];\n_AK_EXTERN const char _s_dae_pool_1[];\n\n#define _s_dae_0(x) (_s_dae_pool_0 + x)\n#define _s_dae_1(x) (_s_dae_pool_1 + x)\n\n/* _s_dae_pool_0 */\n#define _s_dae_space _s_dae_0(0)\n#define _s_dae_collada _s_dae_0(2)\n#define _s_dae_asset _s_dae_0(10)\n#define _s_dae_contributor _s_dae_0(16)\n#define _s_dae_author _s_dae_0(28)\n#define _s_dae_author_email _s_dae_0(35)\n#define _s_dae_author_website _s_dae_0(48)\n#define _s_dae_authoring_tool _s_dae_0(63)\n#define _s_dae_comments _s_dae_0(78)\n#define _s_dae_copyright _s_dae_0(87)\n#define _s_dae_source_data _s_dae_0(97)\n#define _s_dae_created _s_dae_0(109)\n#define _s_dae_modified _s_dae_0(117)\n#define _s_dae_keywords _s_dae_0(126)\n#define _s_dae_revision _s_dae_0(135)\n#define _s_dae_subject _s_dae_0(144)\n#define _s_dae_title _s_dae_0(152)\n#define _s_dae_unit _s_dae_0(158)\n#define _s_dae_name _s_dae_0(163)\n#define _s_dae_meter _s_dae_0(168)\n#define _s_dae_up_axis _s_dae_0(174)\n#define _s_dae_x_up _s_dae_0(182)\n#define _s_dae_z_up _s_dae_0(187)\n#define _s_dae_y_up _s_dae_0(192)\n#define _s_dae_extra _s_dae_0(197)\n#define _s_dae_lib_cameras _s_dae_0(203)\n#define _s_dae_id _s_dae_0(219)\n#define _s_dae_camera _s_dae_0(222)\n#define _s_dae_optics _s_dae_0(229)\n#define _s_dae_technique _s_dae_0(236)\n#define _s_dae_techniquec _s_dae_0(246)\n#define _s_dae_profile _s_dae_0(263)\n#define _s_dae_xmlns _s_dae_0(271)\n#define _s_dae_perspective _s_dae_0(277)\n#define _s_dae_xfov _s_dae_0(289)\n#define _s_dae_yfov _s_dae_0(294)\n#define _s_dae_sid _s_dae_0(299)\n#define _s_dae_aspect_ratio _s_dae_0(303)\n#define _s_dae_znear _s_dae_0(316)\n#define _s_dae_zfar _s_dae_0(322)\n#define _s_dae_orthographic _s_dae_0(327)\n#define _s_dae_xmag _s_dae_0(340)\n#define _s_dae_ymag _s_dae_0(345)\n#define _s_dae_ambient _s_dae_0(350)\n#define _s_dae_color _s_dae_0(358)\n#define _s_dae_directional _s_dae_0(364)\n#define _s_dae_point _s_dae_0(376)\n#define _s_dae_const_attn _s_dae_0(382)\n#define _s_dae_linear_attn _s_dae_0(403)\n#define _s_dae_quad_attn _s_dae_0(422)\n#define _s_dae_spot _s_dae_0(444)\n#define _s_dae_falloff_angle _s_dae_0(449)\n#define _s_dae_falloff_exp _s_dae_0(463)\n#define _s_dae_imager _s_dae_0(480)\n#define _s_dae_light _s_dae_0(487)\n#define _s_dae_lib_lights _s_dae_0(493)\n#define _s_dae_lib_effects _s_dae_0(508)\n#define _s_dae_effect _s_dae_0(524)\n#define _s_dae_newparam _s_dae_0(531)\n#define _s_dae_prfl_common _s_dae_0(540)\n#define _s_dae_string _s_dae_0(555)\n#define _s_dae_bool _s_dae_0(562)\n#define _s_dae_bool2 _s_dae_0(567)\n#define _s_dae_bool3 _s_dae_0(573)\n#define _s_dae_bool4 _s_dae_0(579)\n#define _s_dae_int _s_dae_0(585)\n#define _s_dae_int2 _s_dae_0(589)\n#define _s_dae_int3 _s_dae_0(594)\n#define _s_dae_int4 _s_dae_0(599)\n#define _s_dae_float _s_dae_0(604)\n#define _s_dae_float2 _s_dae_0(610)\n#define _s_dae_float3 _s_dae_0(617)\n#define _s_dae_float4 _s_dae_0(624)\n#define _s_dae_float2x2 _s_dae_0(631)\n#define _s_dae_float3x3 _s_dae_0(640)\n#define _s_dae_float4x4 _s_dae_0(649)\n#define _s_dae_semantic _s_dae_0(658)\n#define _s_dae_ref _s_dae_0(667)\n#define _s_dae_type _s_dae_0(671)\n#define _s_dae_platform _s_dae_0(676)\n#define _s_dae_url _s_dae_0(685)\n#define _s_dae_pass _s_dae_0(689)\n#define _s_dae_blinn _s_dae_0(694)\n#define _s_dae_constant _s_dae_0(700)\n#define _s_dae_lambert _s_dae_0(709)\n#define _s_dae_phong _s_dae_0(717)\n#define _s_dae_emission _s_dae_0(723)\n#define _s_dae_diffuse _s_dae_0(732)\n#define _s_dae_specular _s_dae_0(740)\n#define _s_dae_shininess _s_dae_0(749)\n#define _s_dae_reflective _s_dae_0(759)\n#define _s_dae_reflectivity _s_dae_0(770)\n#define _s_dae_transparent _s_dae_0(783)\n#define _s_dae_transparency _s_dae_0(795)\n#define _s_dae_index_of_refraction _s_dae_0(808)\n#define _s_dae_opaque _s_dae_0(828)\n#define _s_dae_texture _s_dae_0(835)\n#define _s_dae_texcoord _s_dae_0(843)\n#define _s_dae_param _s_dae_0(852)\n#define _s_dae_func _s_dae_0(858)\n#define _s_dae_value _s_dae_0(863)\n#define _s_dae_src _s_dae_0(869)\n#define _s_dae_dest _s_dae_0(873)\n#define _s_dae_src_rgb _s_dae_0(878)\n#define _s_dae_dest_rgb _s_dae_0(886)\n#define _s_dae_src_alpha _s_dae_0(895)\n#define _s_dae_dest_alpha _s_dae_0(905)\n#define _s_dae_rgb _s_dae_0(916)\n#define _s_dae_alpha _s_dae_0(920)\n#define _s_dae_face _s_dae_0(926)\n#define _s_dae_mode _s_dae_0(931)\n#define _s_dae_mask _s_dae_0(936)\n#define _s_dae_fail _s_dae_0(941)\n#define _s_dae_zfail _s_dae_0(946)\n#define _s_dae_zpass _s_dae_0(952)\n#define _s_dae_front _s_dae_0(958)\n#define _s_dae_back _s_dae_0(964)\n#define _s_dae_index _s_dae_0(969)\n#define _s_dae_instance_image _s_dae_0(975)\n#define _s_dae_renderable _s_dae_0(990)\n#define _s_dae_share _s_dae_0(1001)\n#define _s_dae_true _s_dae_0(1007)\n#define _s_dae_false _s_dae_0(1012)\n#define _s_dae_init_from _s_dae_0(1018)\n#define _s_dae_create_2d _s_dae_0(1028)\n#define _s_dae_create_3d _s_dae_0(1038)\n#define _s_dae_create_cube _s_dae_0(1048)\n#define _s_dae_image _s_dae_0(1060)\n#define _s_dae_mips_generate _s_dae_0(1066)\n#define _s_dae_array_index _s_dae_0(1080)\n#define _s_dae_mip_index _s_dae_0(1092)\n#define _s_dae_depth _s_dae_0(1102)\n#define _s_dae_hex _s_dae_0(1108)\n#define _s_dae_format _s_dae_0(1112)\n#define _s_dae_size_exact _s_dae_0(1119)\n#define _s_dae_width _s_dae_0(1130)\n#define _s_dae_height _s_dae_0(1136)\n#define _s_dae_size_ratio _s_dae_0(1143)\n#define _s_dae_mips _s_dae_0(1154)\n#define _s_dae_levels _s_dae_0(1159)\n#define _s_dae_auto_generate _s_dae_0(1166)\n#define _s_dae_unnormalized _s_dae_0(1180)\n#define _s_dae_array _s_dae_0(1193)\n#define _s_dae_length _s_dae_0(1199)\n#define _s_dae_hint _s_dae_0(1206)\n#define _s_dae_channels _s_dae_0(1211)\n#define _s_dae_range _s_dae_0(1220)\n#define _s_dae_precision _s_dae_0(1226)\n#define _s_dae_spaceText _s_dae_0(1236)\n#define _s_dae_exact _s_dae_0(1242)\n#define _s_dae_size _s_dae_0(1248)\n#define _s_dae_wrap_s _s_dae_0(1253)\n#define _s_dae_wrap_t _s_dae_0(1260)\n#define _s_dae_wrap_p _s_dae_0(1267)\n#define _s_dae_minfilter _s_dae_0(1274)\n#define _s_dae_magfilter _s_dae_0(1284)\n#define _s_dae_mipfilter _s_dae_0(1294)\n#define _s_dae_border_color _s_dae_0(1304)\n#define _s_dae_mip_max_level _s_dae_0(1317)\n#define _s_dae_mip_min_level _s_dae_0(1331)\n#define _s_dae_mip_bias _s_dae_0(1345)\n#define _s_dae_max_anisotropy _s_dae_0(1354)\n#define _s_dae_target _s_dae_0(1369)\n#define _s_dae_symbol _s_dae_0(1376)\n#define _s_dae_slice _s_dae_0(1383)\n#define _s_dae_mip _s_dae_0(1389)\n#define _s_dae_lib_images _s_dae_0(1393)\n#define _s_dae_lib_materials _s_dae_0(1408)\n#define _s_dae_inst_effect _s_dae_0(1426)\n#define _s_dae_technique_hint _s_dae_0(1442)\n#define _s_dae_material _s_dae_0(1457)\n#define _s_dae_lib_geometries _s_dae_0(1466)\n#define _s_dae_geometry _s_dae_0(1485)\n#define _s_dae_mesh _s_dae_0(1494)\n#define _s_dae_source _s_dae_0(1499)\n#define _s_dae_count _s_dae_0(1506)\n#define _s_dae_accessor _s_dae_0(1512)\n#define _s_dae_stride _s_dae_0(1521)\n#define _s_dae_X _s_dae_0(1528)\n#define _s_dae_Y _s_dae_0(1530)\n#define _s_dae_Z _s_dae_0(1532)\n#define _s_dae_vertices _s_dae_0(1534)\n#define _s_dae_input _s_dae_0(1543)\n#define _s_dae_offset _s_dae_0(1549)\n#define _s_dae_polylist _s_dae_0(1556)\n#define _s_dae_vcount _s_dae_0(1565)\n#define _s_dae_p _s_dae_0(1572)\n#define _s_dae_convex_mesh _s_dae_0(1574)\n#define _s_dae_spline _s_dae_0(1586)\n#define _s_dae_brep _s_dae_0(1593)\n#define _s_dae_lines _s_dae_0(1598)\n#define _s_dae_linestrips _s_dae_0(1604)\n#define _s_dae_polygons _s_dae_0(1615)\n#define _s_dae_triangles _s_dae_0(1624)\n#define _s_dae_trifans _s_dae_0(1634)\n#define _s_dae_tristrips _s_dae_0(1642)\n#define _s_dae_float_array _s_dae_0(1652)\n#define _s_dae_bool_array _s_dae_0(1664)\n#define _s_dae_IDREF_array _s_dae_0(1675)\n#define _s_dae_int_array _s_dae_0(1687)\n#define _s_dae_Name_array _s_dae_0(1697)\n#define _s_dae_SIDREF_array _s_dae_0(1708)\n#define _s_dae_token_array _s_dae_0(1721)\n#define _s_dae_ph _s_dae_0(1733)\n#define _s_dae_h _s_dae_0(1736)\n#define _s_dae_convex_hull_of _s_dae_0(1738)\n#define _s_dae_control_vertices _s_dae_0(1753)\n#define _s_dae_closed _s_dae_0(1770)\n#define _s_dae_set _s_dae_0(1777)\n#define _s_dae_line _s_dae_0(1781)\n#define _s_dae_circle _s_dae_0(1786)\n#define _s_dae_ellipse _s_dae_0(1793)\n#define _s_dae_parabola _s_dae_0(1801)\n#define _s_dae_hyperbola _s_dae_0(1810)\n#define _s_dae_nurbs _s_dae_0(1820)\n#define _s_dae_orient _s_dae_0(1826)\n#define _s_dae_origin _s_dae_0(1833)\n#define _s_dae_curve _s_dae_0(1840)\n#define _s_dae_direction _s_dae_0(1846)\n#define _s_dae_radius _s_dae_0(1856)\n#define _s_dae_degree _s_dae_0(1863)\n#define _s_dae_degree_u _s_dae_0(1870)\n#define _s_dae_closed_u _s_dae_0(1879)\n#define _s_dae_degree_v _s_dae_0(1888)\n#define _s_dae_closed_v _s_dae_0(1897)\n#define _s_dae_cone _s_dae_0(1906)\n#define _s_dae_plane _s_dae_0(1911)\n#define _s_dae_cylinder _s_dae_0(1917)\n#define _s_dae_nurbs_surface _s_dae_0(1926)\n#define _s_dae_sphere _s_dae_0(1940)\n#define _s_dae_torus _s_dae_0(1947)\n#define _s_dae_swept_surface _s_dae_0(1953)\n#define _s_dae_surface _s_dae_0(1967)\n#define _s_dae_angle _s_dae_0(1975)\n#define _s_dae_axis _s_dae_0(1981)\n#define _s_dae_curves _s_dae_0(1986)\n#define _s_dae_surface_curves _s_dae_0(1993)\n#define _s_dae_surfaces _s_dae_0(2008)\n#define _s_dae_edges _s_dae_0(2017)\n#define _s_dae_wires _s_dae_0(2023)\n#define _s_dae_faces _s_dae_0(2029)\n#define _s_dae_pcurves _s_dae_0(2035)\n\n/* _s_dae_pool_1 */\n#define _s_dae_shells _s_dae_1(0)\n#define _s_dae_solids _s_dae_1(7)\n#define _s_dae_lib_controllers _s_dae_1(14)\n#define _s_dae_controller _s_dae_1(34)\n#define _s_dae_skin _s_dae_1(45)\n#define _s_dae_morph _s_dae_1(50)\n#define _s_dae_bind_shape_matrix _s_dae_1(56)\n#define _s_dae_joints _s_dae_1(74)\n#define _s_dae_vertex_weights _s_dae_1(81)\n#define _s_dae_targets _s_dae_1(96)\n#define _s_dae_v _s_dae_1(104)\n#define _s_dae_method _s_dae_1(106)\n#define _s_dae_lib_visual_scenes _s_dae_1(113)\n#define _s_dae_visual_scene _s_dae_1(135)\n#define _s_dae_node _s_dae_1(148)\n#define _s_dae_evaluate_scene _s_dae_1(153)\n#define _s_dae_layer _s_dae_1(168)\n#define _s_dae_lookat _s_dae_1(174)\n#define _s_dae_matrix _s_dae_1(181)\n#define _s_dae_rotate _s_dae_1(188)\n#define _s_dae_scale _s_dae_1(195)\n#define _s_dae_skew _s_dae_1(201)\n#define _s_dae_translate _s_dae_1(206)\n#define _s_dae_instance_camera _s_dae_1(216)\n#define _s_dae_instance_controller _s_dae_1(232)\n#define _s_dae_skeleton _s_dae_1(252)\n#define _s_dae_bind_material _s_dae_1(261)\n#define _s_dae_instance_material _s_dae_1(275)\n#define _s_dae_bind _s_dae_1(293)\n#define _s_dae_bind_vertex_input _s_dae_1(298)\n#define _s_dae_input_semantic _s_dae_1(316)\n#define _s_dae_input_set _s_dae_1(331)\n#define _s_dae_instance_geometry _s_dae_1(341)\n#define _s_dae_instance_light _s_dae_1(359)\n#define _s_dae_instance_node _s_dae_1(374)\n#define _s_dae_proxy _s_dae_1(388)\n#define _s_dae_render _s_dae_1(394)\n#define _s_dae_camera_node _s_dae_1(401)\n#define _s_dae_technique_override _s_dae_1(413)\n#define _s_dae_enable _s_dae_1(432)\n#define _s_dae_lib_nodes _s_dae_1(439)\n#define _s_dae_instance_visual_scene _s_dae_1(453)\n#define _s_dae_scene _s_dae_1(475)\n#define _s_dae_sampler1d _s_dae_1(481)\n#define _s_dae_sampler2d _s_dae_1(491)\n#define _s_dae_sampler3d _s_dae_1(501)\n#define _s_dae_sampler_rect _s_dae_1(511)\n#define _s_dae_sampler_cube _s_dae_1(523)\n#define _s_dae_sampler_depth _s_dae_1(535)\n#define _s_dae_data _s_dae_1(548)\n#define _s_dae_version _s_dae_1(553)\n#define _s_dae_format_hint _s_dae_1(561)\n#define _s_dae_viewport_ratio _s_dae_1(573)\n#define _s_dae_mip_levels _s_dae_1(588)\n#define _s_dae_mipmap_generate _s_dae_1(599)\n#define _s_dae_option _s_dae_1(615)\n#define _s_dae_init_as_target _s_dae_1(622)\n#define _s_dae_init_cube _s_dae_1(637)\n#define _s_dae_all _s_dae_1(647)\n#define _s_dae_primary _s_dae_1(651)\n#define _s_dae_order _s_dae_1(659)\n#define _s_dae_sketchup _s_dae_1(665)\n#define _s_dae_lib_animations _s_dae_1(674)\n#define _s_dae_animation _s_dae_1(693)\n#define _s_dae_sampler _s_dae_1(703)\n#define _s_dae_channel _s_dae_1(711)\n#define _s_dae_pre_behavior _s_dae_1(719)\n#define _s_dae_post_behavior _s_dae_1(732)\n#define _s_dae_focal _s_dae_1(746)\n#define _s_dae_equation _s_dae_1(752)\n\n#endif /* dae_strpool_h */\n"
  },
  {
    "path": "src/io/dae/strpool.json",
    "content": "{\n  \"space\": \" \",\n  \"collada\": \"COLLADA\",\n  \"asset\": \"asset\",\n  \"contributor\": \"contributor\",\n  \"author\": \"author\",\n  \"author_email\": \"author_email\",\n  \"author_website\":\"author_website\",\n  \"authoring_tool\":\"authoring_tool\",\n  \"comments\": \"comments\",\n  \"copyright\": \"copyright\",\n  \"source_data\": \"source_data\",\n  \"created\": \"created\",\n  \"modified\": \"modified\",\n  \"keywords\": \"keywords\",\n  \"revision\": \"revision\",\n  \"subject\": \"subject\",\n  \"title\": \"title\",\n  \"unit\": \"unit\",\n  \"name\": \"name\",\n  \"meter\": \"meter\",\n  \"up_axis\": \"up_axis\",\n  \"x_up\": \"X_UP\",\n  \"z_up\": \"Z_UP\",\n  \"y_up\": \"Y_UP\",\n  \"extra\": \"extra\",\n  \"lib_cameras\": \"library_cameras\",\n  \"id\": \"id\",\n  \"camera\": \"camera\",\n  \"optics\": \"optics\",\n  \"technique\": \"technique\",\n  \"techniquec\": \"technique_common\",\n  \"profile\": \"profile\",\n  \"xmlns\": \"xmlns\",\n  \"perspective\": \"perspective\",\n  \"xfov\": \"xfov\",\n  \"yfov\": \"yfov\",\n  \"sid\": \"sid\",\n  \"aspect_ratio\": \"aspect_ratio\",\n  \"znear\": \"znear\",\n  \"zfar\": \"zfar\",\n  \"orthographic\": \"orthographic\",\n  \"xmag\": \"xmag\",\n  \"ymag\": \"ymag\",\n  \"ambient\": \"ambient\",\n  \"color\": \"color\",\n  \"directional\": \"directional\",\n  \"point\": \"point\",\n  \"const_attn\": \"constant_attenuation\",\n  \"linear_attn\": \"linear_attenuation\",\n  \"quad_attn\": \"quadratic_attenuation\",\n  \"spot\": \"spot\",\n  \"falloff_angle\": \"falloff_angle\",\n  \"falloff_exp\": \"falloff_exponent\",\n  \"imager\": \"imager\",\n  \"light\": \"light\",\n  \"lib_lights\": \"library_lights\",\n  \"lib_effects\": \"library_effects\",\n  \"effect\": \"effect\",\n  \"newparam\": \"newparam\",\n  \"prfl_common\": \"profile_COMMON\",\n  \"string\": \"string\",\n  \"bool\": \"bool\",\n  \"bool2\": \"bool2\",\n  \"bool3\": \"bool3\",\n  \"bool4\": \"bool4\",\n  \"int\": \"int\",\n  \"int2\": \"int2\",\n  \"int3\": \"int3\",\n  \"int4\": \"int4\",\n  \"float\": \"float\",\n  \"float2\": \"float2\",\n  \"float3\": \"float3\",\n  \"float4\": \"float4\",\n  \"float2x2\": \"float2x2\",\n  \"float3x3\": \"float3x3\",\n  \"float4x4\": \"float4x4\",\n  \"semantic\": \"semantic\",\n  \"ref\": \"ref\",\n  \"type\": \"type\",\n  \"platform\": \"platform\",\n  \"url\": \"url\",\n  \"pass\": \"pass\",\n  \"blinn\": \"blinn\",\n  \"constant\": \"constant\",\n  \"lambert\": \"lambert\",\n  \"phong\": \"phong\",\n  \"emission\": \"emission\",\n  \"diffuse\": \"diffuse\",\n  \"specular\": \"specular\",\n  \"shininess\": \"shininess\",\n  \"reflective\": \"reflective\",\n  \"reflectivity\": \"reflectivity\",\n  \"transparent\": \"transparent\",\n  \"transparency\": \"transparency\",\n  \"index_of_refraction\": \"index_of_refraction\",\n  \"opaque\": \"opaque\",\n  \"texture\": \"texture\",\n  \"texcoord\": \"texcoord\",\n  \"param\": \"param\",\n  \"func\": \"func\",\n  \"value\": \"value\",\n  \"src\": \"src\",\n  \"dest\": \"dest\",\n  \"src_rgb\": \"src_rgb\",\n  \"dest_rgb\": \"dest_rgb\",\n  \"src_alpha\": \"src_alpha\",\n  \"dest_alpha\": \"dest_alpha\",\n  \"rgb\": \"rgb\",\n  \"alpha\": \"alpha\",\n  \"face\": \"face\",\n  \"mode\": \"mode\",\n  \"mask\": \"mask\",\n  \"fail\": \"fail\",\n  \"zfail\": \"zfail\",\n  \"zpass\": \"zpass\",\n  \"front\": \"front\",\n  \"back\": \"back\",\n  \"index\": \"index\",\n  \"instance_image\": \"instance_image\",\n  \"renderable\": \"renderable\",\n  \"share\": \"share\",\n  \"true\": \"true\",\n  \"false\": \"false\",\n  \"init_from\": \"init_from\",\n  \"create_2d\": \"create_2d\",\n  \"create_3d\": \"create_3d\",\n  \"create_cube\": \"create_cube\",\n  \"image\": \"image\",\n  \"mips_generate\": \"mips_generate\",\n  \"array_index\": \"array_index\",\n  \"mip_index\": \"mip_index\",\n  \"depth\": \"depth\",\n  \"hex\": \"hex\",\n  \"format\": \"format\",\n  \"size_exact\": \"size_exact\",\n  \"width\": \"width\",\n  \"height\": \"height\",\n  \"size_ratio\": \"size_ratio\",\n  \"mips\": \"mips\",\n  \"levels\": \"levels\",\n  \"auto_generate\": \"auto_generate\",\n  \"unnormalized\": \"unnormalized\",\n  \"array\": \"array\",\n  \"length\": \"length\",\n  \"hint\": \"hint\",\n  \"channels\": \"channels\",\n  \"range\": \"range\",\n  \"precision\": \"precision\",\n  \"spaceText\": \"space\",\n  \"exact\": \"exact\",\n  \"size\": \"size\",\n  \"wrap_s\": \"wrap_s\",\n  \"wrap_t\": \"wrap_t\",\n  \"wrap_p\": \"wrap_p\",\n  \"minfilter\": \"minfilter\",\n  \"magfilter\": \"magfilter\",\n  \"mipfilter\": \"mipfilter\",\n  \"border_color\": \"border_color\",\n  \"mip_max_level\": \"mip_max_level\",\n  \"mip_min_level\": \"mip_min_level\",\n  \"mip_bias\": \"mip_bias\",\n  \"max_anisotropy\": \"max_anisotropy\",\n  \"target\": \"target\",\n  \"symbol\": \"symbol\",\n  \"slice\": \"slice\",\n  \"mip\": \"mip\",\n  \"lib_images\": \"library_images\",\n  \"lib_materials\": \"library_materials\",\n  \"inst_effect\": \"instance_effect\",\n  \"technique_hint\": \"technique_hint\",\n  \"material\": \"material\",\n  \"lib_geometries\": \"library_geometries\",\n  \"geometry\": \"geometry\",\n  \"mesh\": \"mesh\",\n  \"source\": \"source\",\n  \"count\": \"count\",\n  \"accessor\": \"accessor\",\n  \"stride\": \"stride\",\n  \"X\": \"X\",\n  \"Y\": \"Y\",\n  \"Z\": \"Z\",\n  \"vertices\": \"vertices\",\n  \"input\": \"input\",\n  \"offset\": \"offset\",\n  \"polylist\": \"polylist\",\n  \"vcount\": \"vcount\",\n  \"p\": \"p\",\n  \"convex_mesh\": \"convex_mesh\",\n  \"spline\": \"spline\",\n  \"brep\": \"brep\",\n  \"lines\": \"lines\",\n  \"linestrips\": \"linestrips\",\n  \"polygons\": \"polygons\",\n  \"triangles\": \"triangles\",\n  \"trifans\": \"trifans\",\n  \"tristrips\": \"tristrips\",\n  \"float_array\": \"float_array\",\n  \"bool_array\": \"bool_array\",\n  \"IDREF_array\": \"IDREF_array\",\n  \"int_array\": \"int_array\",\n  \"Name_array\": \"Name_array\",\n  \"SIDREF_array\": \"SIDREF_array\",\n  \"token_array\": \"token_array\",\n  \"ph\": \"ph\",\n  \"h\": \"h\",\n  \"convex_hull_of\": \"convex_hull_of\",\n  \"control_vertices\": \"control_vertices\",\n  \"closed\": \"closed\",\n  \"set\": \"set\",\n  \"line\": \"line\",\n  \"circle\": \"circle\",\n  \"ellipse\": \"ellipse\",\n  \"parabola\": \"parabola\",\n  \"hyperbola\": \"hyperbola\",\n  \"nurbs\": \"nurbs\",\n  \"orient\": \"orient\",\n  \"origin\": \"origin\",\n  \"curve\": \"curve\",\n  \"direction\": \"direction\",\n  \"radius\": \"radius\",\n  \"degree\": \"degree\",\n  \"degree_u\": \"degree_u\",\n  \"closed_u\": \"closed_u\",\n  \"degree_v\": \"degree_v\",\n  \"closed_v\": \"closed_v\",\n  \"cone\": \"cone\",\n  \"plane\": \"plane\",\n  \"cylinder\": \"cylinder\",\n  \"nurbs_surface\": \"nurbs_surface\",\n  \"sphere\": \"sphere\",\n  \"torus\": \"torus\",\n  \"swept_surface\": \"swept_surface\",\n  \"surface\": \"surface\",\n  \"angle\": \"angle\",\n  \"axis\": \"axis\",\n  \"curves\": \"curves\",\n  \"surface_curves\": \"surface_curves\",\n  \"surfaces\": \"surfaces\",\n  \"edges\": \"edges\",\n  \"wires\": \"wires\",\n  \"faces\": \"faces\",\n  \"pcurves\": \"pcurves\",\n  \"shells\": \"shells\",\n  \"solids\": \"solids\",\n  \"lib_controllers\": \"library_controllers\",\n  \"controller\": \"controller\",\n  \"skin\": \"skin\",\n  \"morph\": \"morph\",\n  \"bind_shape_matrix\": \"bind_shape_matrix\",\n  \"joints\": \"joints\",\n  \"vertex_weights\": \"vertex_weights\",\n  \"targets\": \"targets\",\n  \"v\": \"v\",\n  \"method\": \"method\",\n  \"lib_visual_scenes\": \"library_visual_scenes\",\n  \"visual_scene\": \"visual_scene\",\n  \"node\": \"node\",\n  \"evaluate_scene\": \"evaluate_scene\",\n  \"layer\": \"layer\",\n  \"lookat\": \"lookat\",\n  \"matrix\": \"matrix\",\n  \"rotate\": \"rotate\",\n  \"scale\": \"scale\",\n  \"skew\": \"skew\",\n  \"translate\": \"translate\",\n  \"instance_camera\": \"instance_camera\",\n  \"instance_controller\": \"instance_controller\",\n  \"skeleton\": \"skeleton\",\n  \"bind_material\": \"bind_material\",\n  \"instance_material\": \"instance_material\",\n  \"bind\": \"bind\",\n  \"bind_vertex_input\": \"bind_vertex_input\",\n  \"input_semantic\": \"input_semantic\",\n  \"input_set\": \"input_set\",\n  \"instance_geometry\": \"instance_geometry\",\n  \"instance_light\": \"instance_light\",\n  \"instance_node\": \"instance_node\",\n  \"proxy\": \"proxy\",\n  \"render\": \"render\",\n  \"camera_node\": \"camera_node\",\n  \"technique_override\": \"technique_override\",\n  \"enable\": \"enable\",\n  \"lib_nodes\": \"library_nodes\",\n  \"instance_visual_scene\": \"instance_visual_scene\",\n  \"scene\": \"scene\",\n  \"sampler1d\": \"sampler1D\",\n  \"sampler2d\": \"sampler2D\",\n  \"sampler3d\": \"sampler3D\",\n  \"sampler_rect\": \"samplerRECT\",\n  \"sampler_cube\": \"samplerCUBE\",\n  \"sampler_depth\": \"samplerDEPTH\",\n  \"data\": \"data\",\n  \"version\": \"version\",\n  \"format_hint\": \"format_hint\",\n  \"viewport_ratio\": \"viewport_ratio\",\n  \"mip_levels\": \"mip_levels\",\n  \"mipmap_generate\": \"mipmap_generate\",\n  \"option\": \"option\",\n  \"init_as_target\": \"init_as_target\",\n  \"init_cube\": \"init_cube\",\n  \"all\": \"all\",\n  \"primary\": \"primary\",\n  \"order\": \"order\",\n  \"sketchup\": \"sketchup\",\n  \"lib_animations\": \"library_animations\",\n  \"animation\": \"animation\",\n  \"sampler\": \"sampler\",\n  \"channel\": \"channel\",\n  \"pre_behavior\": \"pre_behavior\",\n  \"post_behavior\": \"post_behavior\",\n  \"focal\": \"focal\",\n  \"equation\": \"equation\"\n}\n"
  },
  {
    "path": "src/io/dae/strpool.py",
    "content": "#!/usr/bin/python3\n#\n# Copyright (C) 2020 Recep Aslantas\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport json\nfrom collections import OrderedDict\nfrom os.path     import realpath\nfrom os.path     import dirname\n\nheaderContents = [ ]\ndestdir        = dirname(realpath(__file__))\nspidx          = 0\npos            = 0\n\nfspoolJson = open(destdir + \"/strpool.json\")\nspool      = json.loads(fspoolJson.read(),\n                        object_pairs_hook=OrderedDict)\nfspoolJson.close()\n\nfspool_h = open(destdir + \"/strpool.h\", \"wb\");\nfspool_c = open(destdir + \"/strpool.c\", \"wb\");\n\ncopyright_str = \"\"\"\\\n/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"\"\"\n\nfspool_h.write(copyright_str.encode())\nfspool_c.write(copyright_str.encode())\n\nfspool_h.write(\"\"\"\n#ifndef dae_strpool_h\n#  define dae_strpool_h\n\n#ifndef _DAE_STRPOOL_\n#  define _AK_EXTERN extern\n#else\n#  define _AK_EXTERN\n#endif\n\"\"\".encode())\n\nfspool_c.write(\"\"\"\n#ifndef _DAE_STRPOOL_\n#  define _DAE_STRPOOL_\n#endif\n\n#include \"strpool.h\"\n#include <string.h>\n\nconst char _s_dae_pool_0[] =\n\"\"\".encode())\n\nheaderContents.append(\"\\n/* _s_dae_pool_0 */\\n\")\n\nfor name, val in spool.items():\n  valLen = len(val) + 1\n\n  # string literal size: 2048\n  if pos + valLen > 2048:\n    pos    = 0\n    spidx += 1\n\n    fspool_c.write(\";\\n\\nconst char _s_dae_pool_{0}[] =\\n\"\n                     .format(str(spidx)).encode())\n\n    headerContents.append(\"\\n/* _s_dae_pool_{0} */\\n\"\n                            .format(spidx))\n\n  fspool_c.write(\"\\\"{0}\\\\0\\\"\\n\".format(val).encode())\n\n  headerContents.append(\"#define _s_dae_{0} _s_dae_{1}({2})\\n\"\n                          .format(name, str(spidx), str(pos)))\n\n  pos += valLen\n\n# source file, then close it\nfspool_c.write(\";\\n\\n#undef _DAE_STRPOOL_\\n\".encode())\nfspool_c.close()\n\n# header file\nfor idx in range(spidx + 1):\n  fspool_h.write(\"\\n_AK_EXTERN const char _s_dae_pool_{0}[];\"\n                   .format(str(idx)).encode())\n\nfspool_h.write(\"\\n\\n\".encode())\n\nfor idx in range(spidx + 1):\n  fspool_h.write(\"#define _s_dae_{0}(x) (_s_dae_pool_{0} + x)\\n\"\n                   .format(str(idx)).encode())\n\n# write header contents, then close it\nfspool_h.writelines(map(lambda x: x.encode(), headerContents))\nfspool_h.write(\"\\n#endif /* dae_strpool_h */\\n\".encode())\nfspool_h.close()\n\n# try free array\ndel headerContents[:]\n"
  },
  {
    "path": "src/io/gltf/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n\nadd_subdirectory(imp)"
  },
  {
    "path": "src/io/gltf/README.md",
    "content": "# AssetKit: glTF Status\n\n- [x] Single Interface for glTF and COLLADA\n- [x] glTF 2.0\n- [x] Options to Generate Mesh Normals\n- [x] Acessors, Buffers / BufferViews\n- [x] Geometries (Triangles, Polygons, Lines)\n- [x] Nodes\n- [x] Scenes\n- [x] Cameras\n- [x] Materials\n  - [x] Images\n  - [x] Samplers\n  - [x] Textures\n  - [x] PBR Materials\n      - [x] Metallic Roughness\n      - [x] Specular Glossiness Extension\n  - [x] Other textures\n      - [x] Occlusion map\n      - [x] Emissive map\n      - [x] Normal Map\n      - [ ] other textures?\n  - [x] alphaMode\n  - [x] alphaCutoff\n  - [x] doubleSided\n- [x] Skin\n- [x] Morph\n- [x] Animations\n- Extensions \n  - [x] KHR_materials_pbrSpecularGlossiness\n  - [x] KHR_texture_specular\n  - [x] KHR_texture_transform\n  - [x] KHR_materials_clearcoat\n  - [x] KHR_materials_unlit\n  - [x] KHR_materials_emissive_strength\n  - [x] KHR_materials_ior\n  - [x] KHR_materials_transmission\n  - [ ] Lights (TODO)\n  - [ ] Common materials (TODO)\n- [x] glTF-Separate\n- [x] glTF-Binary \n- [x] glTF-Embedded\n- [ ] Load glTF-Draco (TODO)\n\n### Trademarks\n\nglTF and COLLADA and their logos are trademarks of Khronos Group.\n"
  },
  {
    "path": "src/io/gltf/common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_commoh_h\n#define gltf_commoh_h\n\n#include \"../../../include/ak/assetkit.h\"\n#include \"../../common.h\"\n#include \"../../utils.h\"\n#include \"../../tree.h\"\n#include \"../../json.h\"\n#include \"strpool.h\"\n\n#include <string.h>\n#include <stdlib.h>\n\n/* JSON parser */\n#include <json/json.h>\n\ntypedef struct AkBufferView {\n  AkBuffer   *buffer;\n  const char *name;\n  size_t      byteOffset;\n  size_t      byteLength;\n  size_t      byteStride;\n} AkBufferView;\n\ntypedef struct AkGLTFMeshoptLib AkGLTFMeshoptLib;\ntypedef struct AkGLTFDracoLib   AkGLTFDracoLib;\ntypedef struct AkGLTFSPZLib     AkGLTFSPZLib;\ntypedef struct AkGLTFKTX2Lib    AkGLTFKTX2Lib;\n\ntypedef struct AkGLTFState {\n  AkHeap       *heap;\n  AkDoc        *doc;\n  json_t       *root;\n  void         *tmpParent;\n  FListItem    *buffers;\n  RBTree       *bufferMap;\n  FListItem    *bufferViews;\n  RBTree       *skinBound;\n  RBTree       *meshTargets;\n  void         *bindata;\n  void         *defaultMaterial;\n  AkGLTFMeshoptLib *meshopt;\n  AkGLTFDracoLib   *draco;\n  AkGLTFSPZLib     *spz;   /* Gaussian splat (SPZ) decoder, optional */\n  AkGLTFKTX2Lib    *ktx2;  /* KTX2/BasisU decoder, optional */\n  size_t        bindataLen;\n  bool          stop;\n  bool          isbinary;\n  bool          animPointerRequired;\n  bool          borrowBufferViews;\n} AkGLTFState;\n\n#define GETCHILD(INITIAL, ITEM, INDEX)                                        \\\n  do {                                                                        \\\n    int i;                                                                    \\\n    if (INITIAL && (i = INDEX) >= 0) {                                        \\\n      ITEM = (void *)INITIAL;                                                 \\\n      while (i > 0) {                                                         \\\n        if (!(ITEM = (void *)ITEM->base.next)) {                              \\\n          i     = -1;                                                         \\\n          ITEM  = NULL;                                                       \\\n          break;  /* not foud */                                              \\\n        }                                                                     \\\n        i--;                                                                  \\\n      }                                                                       \\\n    } else {                                                                  \\\n      ITEM = NULL;                                                            \\\n    }                                                                         \\\n  } while (0)\n\n#endif /* gltf_commoh_h */\n"
  },
  {
    "path": "src/io/gltf/imp/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n\nadd_subdirectory(core)\nadd_subdirectory(ext)\n"
  },
  {
    "path": "src/io/gltf/imp/common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_commoh_h\n#define gltf_imp_commoh_h\n\n#include \"../common.h\"\n\n#endif /* gltf_imp_commoh_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/gltf/imp/core/accessor.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"accessor.h\"\n#include \"enum.h\"\n#include \"buffer.h\"\n#include \"../../../../accessor.h\"\n\n#define k_gltf_bufferView    0\n#define k_gltf_byteOffset    1\n#define k_gltf_componentType 2\n#define k_gltf_normalized    3\n#define k_gltf_count         4\n#define k_gltf_type          5\n#define k_gltf_max           6\n#define k_gltf_min           7\n#define k_gltf_sparse        8\n#define k_gltf_name          9\n\nAK_HIDE\nvoid\ngltf_accessors(json_t * __restrict json,\n               void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  AkDoc              *doc;\n  AkHeap             *heap;\n  const json_array_t *jaccessors, *jarr;\n  const json_t       *jitem, *it;\n  AkAccessor         *acc;\n  int                 componentLen, count, bound;\n\n  if (!(jaccessors = json_array(json)))\n    return;\n\n  gst          = userdata;\n  doc          = gst->doc;\n  heap         = gst->heap;\n  json         = jaccessors->base.value;\n  componentLen = 1;\n\n  while (json) {\n    acc = ak_heap_calloc(heap, doc, sizeof(*acc));\n\n    ak_setypeid(acc, AKT_ACCESSOR);\n\n    json_objmap_t accMap[] = {\n      JSON_OBJMAP_OBJ(_s_gltf_bufferView,    I2P k_gltf_bufferView),\n      JSON_OBJMAP_OBJ(_s_gltf_byteOffset,    I2P k_gltf_byteOffset),\n      JSON_OBJMAP_OBJ(_s_gltf_componentType, I2P k_gltf_componentType),\n      JSON_OBJMAP_OBJ(_s_gltf_normalized,    I2P k_gltf_normalized),\n      JSON_OBJMAP_OBJ(_s_gltf_count,         I2P k_gltf_count),\n      JSON_OBJMAP_OBJ(_s_gltf_type,          I2P k_gltf_type),\n      JSON_OBJMAP_OBJ(_s_gltf_max,           I2P k_gltf_max),\n      JSON_OBJMAP_OBJ(_s_gltf_min,           I2P k_gltf_min),\n      JSON_OBJMAP_OBJ(_s_gltf_sparse,        I2P k_gltf_sparse),\n      JSON_OBJMAP_OBJ(_s_gltf_name,          I2P k_gltf_name)\n    };\n\n    json_objmap(json, accMap, JSON_ARR_LEN(accMap));\n  \n    if ((it = accMap[k_gltf_name].object)) {\n      acc->name = json_strdup(it, heap, acc);\n    }\n    \n    /*  merge bufferView with acessor and buffer */\n    if ((it = accMap[k_gltf_bufferView].object)) {\n      AkBuffer     *buff, *tmpbuff;\n      AkBufferView *buffView;\n      int32_t       buffViewIndex;\n      \n      if ((buffViewIndex = json_int32(it, -1)) > -1\n          && (buffView = flist_sp_at(&gst->bufferViews, buffViewIndex))\n          && (tmpbuff = buffView->buffer)\n          /* tmpbuff->data is NULL when the source buffer is supplied via\n             an unsupported extension (EXT_meshopt_compression,\n             KHR_draco_mesh_compression, ...). Skip — accessor will be\n             buffer-less rather than reading from a NULL pointer. */\n          && tmpbuff->data) {\n        if (!(buff = rb_find(gst->bufferMap, buffView))) {\n          buff         = ak_heap_calloc(heap, doc, sizeof(*buff));\n          buff->data   = ak_heap_alloc(heap, buff, buffView->byteLength);\n          buff->length = buffView->byteLength;\n\n          memcpy(buff->data,\n                 (char *)tmpbuff->data + buffView->byteOffset,\n                 buffView->byteLength);\n\n          rb_insert(gst->bufferMap, buffView, buff);\n        }\n\n        acc->byteStride = buffView->byteStride;\n        acc->buffer     = buff;\n\n        flist_sp_insert(&doc->lib.buffers, buff);\n      }\n    }\n\n    if ((it = accMap[k_gltf_byteOffset].object)) {\n      acc->byteOffset = json_uint64(it, 0);\n    }\n    \n    if ((it = accMap[k_gltf_componentType].object)) {\n      int componentType;\n      componentType      = json_int32(it, -1);\n      acc->componentType = gltf_componentType(componentType);\n      componentLen       = gltf_componentLen(componentType);\n    }\n    \n    if ((it = accMap[k_gltf_normalized].object)) {\n      acc->normalized = json_bool(it, false);\n    }\n    \n    if ((it = accMap[k_gltf_count].object)) {\n      acc->count = json_uint32(it, 0);\n    }\n    \n    if ((it = accMap[k_gltf_type].object)) {\n      acc->componentSize = gltf_type(it);\n    }\n\n    /* prepare for min and max */\n    if (acc->componentSize < 5) {\n      bound                  = acc->componentSize;\n      acc->bytesPerComponent = componentLen;\n      acc->componentCount    = bound;\n    } else {\n      bound                  = acc->componentSize >> 3;\n      acc->bytesPerComponent = componentLen;\n      acc->componentCount    = bound;\n    }\n    \n    acc->byteLength   = acc->bytesPerComponent * bound * acc->count;\n    acc->fillByteSize = acc->bytesPerComponent * bound;\n\n    /* glTF spec 3.6.2.4: bufferView.byteStride undefined → tightly packed.\n       Normalize once, up front, so all downstream code (sparse densify,\n       dequantize, consumer reads) can use acc->byteStride uniformly without\n       a special case for \"0 means packed\". */\n    if (acc->byteStride == 0) {\n      acc->byteStride = acc->fillByteSize;\n    }\n\n    /* Snapshot the source-side encoding before any in-place dequantize\n       below mutates componentType/normalized. These fields stay valid\n       for the lifetime of the accessor so callers can reason about how\n       the buffer was originally laid out, even after a widen-to-float\n       pass. With AK_OPT_PRESERVE_QUANTIZED_ATTRS the buffer is left as\n       integers and these mirror componentType/normalized. */\n    acc->originalComponentType = acc->componentType;\n    acc->originallyNormalized  = acc->normalized;\n\n    if (acc->componentSize != AK_COMPONENT_SIZE_UNKNOWN\n        && acc->fillByteSize > 0) {\n      if ((it = accMap[k_gltf_min].object) && it->value) {\n        acc->min = ak_heap_alloc(heap, acc, acc->fillByteSize);\n\n        if ((jarr = json_array(it))) {\n          jitem = jarr->base.value;\n          count = jarr->count;\n\n          while (jitem) {\n            json_array_set(acc->min, acc->componentType, --count, it);\n            jitem = jitem->next;\n          }\n        } else {\n          json_array_set(acc->min, acc->componentType, 0, it);\n        }\n      }\n\n      if ((it = accMap[k_gltf_max].object) && it->value) {\n        acc->max = ak_heap_alloc(heap, acc, acc->fillByteSize);\n\n        if ((jarr = json_array(it))) {\n          jitem = jarr->base.value;\n          count = jarr->count;\n\n          while (jitem) {\n            json_array_set(acc->max, acc->componentType, --count, it);\n            jitem = jitem->next;\n          }\n        } else {\n          json_array_set(acc->max, acc->componentType, 0, it);\n        }\n      }\n    }\n\n    /* Sparse accessor — densify here so consumers always see a flat,\n       tightly-packed buffer.\n       glTF spec 3.6.2.3: when accessor.bufferView is undefined the dense\n       buffer is initialized to zeros; otherwise it starts as a copy of the\n       referenced bufferView slice. Then sparse.values overwrite the entries\n       at sparse.indices. */\n    if ((it = accMap[k_gltf_sparse].object)) {\n      json_t       *jsCount, *jsIndices, *jsValues, *node;\n      AkBufferView *idxBV, *valBV;\n      AkBuffer     *denseBuff;\n      char         *denseData, *idxPtr, *valPtr;\n      size_t        totalSize, idxByteOffset, valByteOffset;\n      uint32_t      sparseCount, sparseIdx, i;\n      int32_t       idxBVIdx, valBVIdx;\n      AkTypeId      idxComponentType;\n\n      jsCount   = json_get(it, _s_gltf_count);\n      jsIndices = json_get(it, _s_gltf_indices);\n      jsValues  = json_get(it, _s_gltf_values);\n\n      if (jsCount && jsIndices && jsValues\n          && (sparseCount = json_uint32(jsCount, 0)) > 0\n          && acc->fillByteSize > 0\n          && acc->count > 0) {\n\n        /* indices descriptor */\n        idxBVIdx         = (node = json_get(jsIndices, _s_gltf_bufferView))\n                              ? json_int32(node, -1) : -1;\n        idxByteOffset    = (node = json_get(jsIndices, _s_gltf_byteOffset))\n                              ? json_uint64(node, 0) : 0;\n        idxComponentType = (node = json_get(jsIndices, _s_gltf_componentType))\n                              ? gltf_componentType(json_int32(node, -1))\n                              : AKT_USHORT;\n\n        /* values descriptor */\n        valBVIdx      = (node = json_get(jsValues, _s_gltf_bufferView))\n                          ? json_int32(node, -1) : -1;\n        valByteOffset = (node = json_get(jsValues, _s_gltf_byteOffset))\n                          ? json_uint64(node, 0) : 0;\n\n        idxBV = (idxBVIdx >= 0)\n                  ? flist_sp_at(&gst->bufferViews, idxBVIdx) : NULL;\n        valBV = (valBVIdx >= 0)\n                  ? flist_sp_at(&gst->bufferViews, valBVIdx) : NULL;\n\n        if (idxBV && valBV && idxBV->buffer && valBV->buffer) {\n          totalSize         = (size_t)acc->fillByteSize * acc->count;\n          denseBuff         = ak_heap_calloc(heap, doc, sizeof(*denseBuff));\n          denseBuff->data   = ak_heap_alloc(heap, denseBuff, totalSize);\n          denseBuff->length = totalSize;\n          denseData         = denseBuff->data;\n\n          /* initialize from main bufferView slice (if any) or zeros.\n             Source may be interleaved (byteStride > fillByteSize), so copy\n             element by element when needed instead of one big memcpy. */\n          if (acc->buffer && acc->buffer->data) {\n            if (acc->byteStride == 0\n                || acc->byteStride == acc->fillByteSize) {\n              memcpy(denseData,\n                     (char *)acc->buffer->data + acc->byteOffset,\n                     totalSize);\n            } else {\n              uint32_t v;\n              char    *baseSrc = (char *)acc->buffer->data + acc->byteOffset;\n              for (v = 0; v < acc->count; v++) {\n                memcpy(denseData + (size_t)v * acc->fillByteSize,\n                       baseSrc   + (size_t)v * acc->byteStride,\n                       acc->fillByteSize);\n              }\n            }\n          } else {\n            memset(denseData, 0, totalSize);\n          }\n\n          /* overlay sparse values at the given indices */\n          idxPtr = (char *)idxBV->buffer->data\n                 + idxBV->byteOffset + idxByteOffset;\n          valPtr = (char *)valBV->buffer->data\n                 + valBV->byteOffset + valByteOffset;\n\n          for (i = 0; i < sparseCount; i++) {\n            switch (idxComponentType) {\n              case AKT_UBYTE:  sparseIdx = ((uint8_t  *)idxPtr)[i]; break;\n              case AKT_USHORT: sparseIdx = ((uint16_t *)idxPtr)[i]; break;\n              case AKT_UINT:   sparseIdx = ((uint32_t *)idxPtr)[i]; break;\n              default:         continue;\n            }\n            if (sparseIdx >= acc->count) continue;\n\n            memcpy(denseData + (size_t)sparseIdx * acc->fillByteSize,\n                   valPtr    + (size_t)i        * acc->fillByteSize,\n                   acc->fillByteSize);\n          }\n\n          /* swap: accessor now points at the dense buffer, tightly packed */\n          acc->buffer     = denseBuff;\n          acc->byteOffset = 0;\n          acc->byteStride = acc->fillByteSize;\n\n          flist_sp_insert(&doc->lib.buffers, denseBuff);\n        }\n      }\n    }\n\n    /* Dequantize normalized integer attributes to packed float buffer.\n       Triggered when accessor.normalized == true on byte/short data — the\n       common case for COLOR_n (UBYTE), TEXCOORD_n (UBYTE/USHORT), and\n       quantized POSITION/NORMAL/TANGENT under KHR_mesh_quantization.\n       Non-normalized integers (e.g. JOINTS_n) are left intact so vertex\n       shaders can index by them. After conversion the accessor owns a\n       tightly-packed float buffer.\n       glTF spec 3.6.1.1: integer-to-float scale factors.\n\n       Opt-out: AK_OPT_PRESERVE_QUANTIZED_ATTRS keeps the integer buffer\n       intact for renderers that decode on the GPU. originalComponentType\n       and originallyNormalized still describe the source encoding, and\n       ak_accessorAsFloat / ak_accessorMakeFloat let consumers dequantize\n       on demand. */\n    if (acc->normalized\n        && acc->buffer && acc->buffer->data\n        && acc->componentType != AKT_FLOAT\n        && acc->fillByteSize > 0\n        && acc->count > 0\n        && !ak_opt_get(AK_OPT_PRESERVE_QUANTIZED_ATTRS)) {\n      AkBuffer *fbuf;\n      char     *src;\n      float    *dst;\n      size_t    floatBufSize, stride, perComp;\n      uint32_t  v, c, comps;\n\n      comps        = acc->componentCount;\n      stride       = acc->byteStride;            /* normalized up front      */\n      perComp      = acc->bytesPerComponent;\n      floatBufSize = (size_t)comps * acc->count * sizeof(float);\n\n      fbuf         = ak_heap_calloc(heap, doc, sizeof(*fbuf));\n      fbuf->data   = ak_heap_alloc(heap, fbuf, floatBufSize);\n      fbuf->length = floatBufSize;\n      dst          = fbuf->data;\n      src          = (char *)acc->buffer->data + acc->byteOffset;\n\n      for (v = 0; v < acc->count; v++) {\n        char *vsrc = src + (size_t)v * stride;\n        for (c = 0; c < comps; c++) {\n          void  *cp = vsrc + (size_t)c * perComp;\n          float  f;\n          switch (acc->componentType) {\n            case AKT_BYTE:   f = (float)(*(int8_t  *)cp) / 127.0f;\n                             if (f < -1.0f) f = -1.0f;\n                             break;\n            case AKT_UBYTE:  f = (float)(*(uint8_t *)cp) / 255.0f;   break;\n            case AKT_SHORT:  f = (float)(*(int16_t *)cp) / 32767.0f;\n                             if (f < -1.0f) f = -1.0f;\n                             break;\n            case AKT_USHORT: f = (float)(*(uint16_t*)cp) / 65535.0f; break;\n            default:         f = 0.0f; break;\n          }\n          dst[(size_t)v * comps + c] = f;\n        }\n      }\n\n      acc->buffer            = fbuf;\n      acc->byteOffset        = 0;\n      acc->bytesPerComponent = sizeof(float);\n      acc->fillByteSize      = (size_t)comps * sizeof(float);\n      acc->byteStride        = acc->fillByteSize;\n      acc->componentType     = AKT_FLOAT;\n      acc->normalized        = false;\n\n      flist_sp_insert(&doc->lib.buffers, fbuf);\n    }\n\n    /* KHR_mesh_quantization: non-normalized integer POSITION (vec3) or\n       TEXCOORD (vec2). Spec allows raw integer values that map linearly to\n       model space via a node-level transform — but most engines expect\n       float vertex attributes, so we widen the integers to float in\n       place. The asset's node transform already encodes the correct\n       scale/offset from quantized space, so the result reaches the GPU\n       at the intended position.\n\n       Heuristic: only widen vec2/vec3 attributes — JOINTS_n is non-normalized\n       integer too but is vec4 (and must stay int for skinning), so it's\n       skipped. TANGENT is vec4 in the quantization spec but is required to\n       be normalized, so it goes through the previous branch instead.\n\n       Opt-out: AK_OPT_PRESERVE_QUANTIZED_ATTRS keeps the integer buffer\n       intact (see notes on the previous block). */\n    if (!acc->normalized\n        && acc->buffer && acc->buffer->data\n        && acc->componentType != AKT_FLOAT\n        && acc->componentType != AKT_UINT\n        && (acc->componentCount == 2 || acc->componentCount == 3)\n        && acc->fillByteSize > 0\n        && acc->count > 0\n        && !ak_opt_get(AK_OPT_PRESERVE_QUANTIZED_ATTRS)) {\n      AkBuffer *fbuf;\n      char     *src;\n      float    *dst;\n      size_t    floatBufSize, stride, perComp;\n      uint32_t  v, c, comps;\n\n      comps        = acc->componentCount;\n      stride       = acc->byteStride;            /* normalized up front      */\n      perComp      = acc->bytesPerComponent;\n      floatBufSize = (size_t)comps * acc->count * sizeof(float);\n\n      fbuf         = ak_heap_calloc(heap, doc, sizeof(*fbuf));\n      fbuf->data   = ak_heap_alloc(heap, fbuf, floatBufSize);\n      fbuf->length = floatBufSize;\n      dst          = fbuf->data;\n      src          = (char *)acc->buffer->data + acc->byteOffset;\n\n      for (v = 0; v < acc->count; v++) {\n        char *vsrc = src + (size_t)v * stride;\n        for (c = 0; c < comps; c++) {\n          void *cp = vsrc + (size_t)c * perComp;\n          float f;\n          switch (acc->componentType) {\n            case AKT_BYTE:   f = (float)(*(int8_t  *)cp); break;\n            case AKT_UBYTE:  f = (float)(*(uint8_t *)cp); break;\n            case AKT_SHORT:  f = (float)(*(int16_t *)cp); break;\n            case AKT_USHORT: f = (float)(*(uint16_t*)cp); break;\n            default:         f = 0.0f; break;\n          }\n          dst[(size_t)v * comps + c] = f;\n        }\n      }\n\n      acc->buffer            = fbuf;\n      acc->byteOffset        = 0;\n      acc->bytesPerComponent = sizeof(float);\n      acc->fillByteSize      = (size_t)comps * sizeof(float);\n      acc->byteStride        = acc->fillByteSize;\n      acc->componentType     = AKT_FLOAT;\n\n      flist_sp_insert(&doc->lib.buffers, fbuf);\n    }\n\n    /* (byteStride normalization done up front, before sparse + dequantize) */\n\n    flist_sp_insert(&gst->doc->lib.accessors, acc);\n\n    json = json->next;\n  }\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/accessor.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_accesor_h\n#define gltf_imp_core_accesor_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_accessors(json_t * __restrict jbuffView,\n               void   * __restrict userdata);\n\n#endif /* gltf_imp_core_accesor_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/anim.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"anim.h\"\n#include \"accessor.h\"\n#include \"enum.h\"\n\n#define k_path 0\n#define k_node 1\n#define k_ext  2\n\n#define k_anim_samplers 0\n#define k_anim_channels 1\n#define k_anim_name     2\n\nstatic\nbool\ngltf_animSegEq(const char * __restrict seg,\n               size_t                   segLen,\n               const char * __restrict name) {\n  return strlen(name) == segLen && strncasecmp(seg, name, segLen) == 0;\n}\n\nstatic\nbool\ngltf_animPtrSeg(const char ** __restrict p,\n                const char  * __restrict end,\n                const char  * __restrict name) {\n  size_t len;\n\n  if (*p >= end || **p != '/')\n    return false;\n\n  (*p)++;\n  len = strlen(name);\n\n  if ((size_t)(end - *p) < len || strncmp(*p, name, len) != 0)\n    return false;\n\n  *p += len;\n  return true;\n}\n\nstatic\nbool\ngltf_animPtrIndex(const char ** __restrict p,\n                  const char  * __restrict end,\n                  uint32_t    * __restrict index) {\n  uint32_t val;\n  bool     found;\n\n  val   = 0;\n  found = false;\n\n  while (*p < end && **p >= '0' && **p <= '9') {\n    found = true;\n    val   = val * 10 + (uint32_t)(**p - '0');\n    (*p)++;\n  }\n\n  *index = val;\n  return found;\n}\n\nstatic\nbool\ngltf_animPtrReadSeg(const char ** __restrict p,\n                    const char  * __restrict end,\n                    const char ** __restrict seg,\n                    size_t      * __restrict segLen) {\n  if (*p >= end || **p != '/')\n    return false;\n\n  (*p)++;\n  *seg = *p;\n  while (*p < end && **p != '/')\n    (*p)++;\n\n  *segLen = (size_t)(*p - *seg);\n  return *segLen > 0;\n}\n\nstatic\nvoid\ngltf_animSetTarget(AkGLTFState         * __restrict gst,\n                   AkChannel           * __restrict ch,\n                   void                * __restrict target,\n                   uint32_t                         off,\n                   bool                             isPartial,\n                   AkTargetPropertyType             targetType) {\n  AkResolvedTarget *rt;\n\n  rt = ak_heap_calloc(gst->heap, ch, sizeof(*rt));\n  ak_setypeid(rt, AKT_RESOLVED_TARGET);\n\n  rt->target         = target;\n  rt->off            = off;\n  rt->isPartial      = isPartial;\n  ch->targetType     = targetType;\n  ch->resolvedTarget = rt;\n}\n\nstatic\nbool\ngltf_animSetFloatArrayTarget(AkGLTFState         * __restrict gst,\n                             AkChannel           * __restrict ch,\n                             float               * __restrict target,\n                             uint32_t                         len,\n                             uint32_t                         index,\n                             bool                             hasIndex,\n                             AkTargetPropertyType             targetType) {\n  if (!target)\n    return false;\n\n  if (hasIndex && index >= len)\n    return false;\n\n  gltf_animSetTarget(gst, ch, target, index, hasIndex, targetType);\n  return true;\n}\n\nstatic\nbool\ngltf_animSetFloatTarget(AkGLTFState         * __restrict gst,\n                        AkChannel           * __restrict ch,\n                        float               * __restrict target) {\n  if (!target)\n    return false;\n\n  gltf_animSetTarget(gst, ch, target, 0, false, AK_TARGET_FLOAT);\n  return true;\n}\n\nstatic\nAkTextureTransform*\ngltf_animEnsureTexTransform(AkGLTFState * __restrict gst,\n                            AkTextureRef * __restrict texref) {\n  AkTextureTransform *texTransf;\n\n  if (!texref)\n    return NULL;\n\n  if (!(texTransf = texref->transform)) {\n    texTransf           = ak_heap_calloc(gst->heap, texref, sizeof(*texTransf));\n    texTransf->slot     = -1;\n    texTransf->scale[0] = 1.0f;\n    texTransf->scale[1] = 1.0f;\n    texref->transform   = texTransf;\n  }\n\n  return texTransf;\n}\n\nstatic\nAkTextureRef*\ngltf_animEnsureTexRef(AkGLTFState     * __restrict gst,\n                      AkTextureRef   ** __restrict texref,\n                      void            * __restrict parent,\n                      AkTextureColorSpace          colorSpace,\n                      AkTextureChannels            channels) {\n  if (!*texref) {\n    *texref       = ak_heap_calloc(gst->heap, parent, sizeof(**texref));\n    (*texref)->slot = -1;\n  }\n\n  ak_texref_usage(*texref, colorSpace, channels);\n\n  return *texref;\n}\n\nstatic\nbool\ngltf_animResolveTexTransform(AkGLTFState     * __restrict gst,\n                             AkChannel       * __restrict ch,\n                             AkTextureRef    * __restrict texref,\n                             const char      *p,\n                             const char      *end) {\n  AkTextureTransform *texTransf;\n  const char         *seg;\n  size_t              segLen;\n  uint32_t            idx;\n  bool                hasIdx;\n\n  if (!gltf_animPtrSeg(&p, end, _s_gltf_extensions)\n      || !gltf_animPtrSeg(&p, end, _s_gltf_KHR_texture_transform)\n      || !gltf_animPtrReadSeg(&p, end, &seg, &segLen))\n    return false;\n\n  hasIdx = false;\n  idx    = 0;\n  if (p < end) {\n    p++;\n    if (!gltf_animPtrIndex(&p, end, &idx) || p != end)\n      return false;\n    hasIdx = true;\n  }\n\n  if (!(texTransf = gltf_animEnsureTexTransform(gst, texref)))\n    return false;\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_offset))\n    return gltf_animSetFloatArrayTarget(gst, ch, texTransf->offset, 2,\n                                        idx, hasIdx, AK_TARGET_VEC2);\n  if (gltf_animSegEq(seg, segLen, _s_gltf_rotation))\n    return gltf_animSetFloatTarget(gst, ch, &texTransf->rotation);\n  if (gltf_animSegEq(seg, segLen, _s_gltf_scale))\n    return gltf_animSetFloatArrayTarget(gst, ch, texTransf->scale, 2,\n                                        idx, hasIdx, AK_TARGET_VEC2);\n\n  return false;\n}\n\nstatic\nbool\ngltf_animResolveNodePointer(AkGLTFState     * __restrict gst,\n                            AkChannel       * __restrict ch,\n                            const char      * __restrict ptr,\n                            size_t                       ptrLen) {\n  const char         *p;\n  const char         *end;\n  const char         *prop;\n  AkNode             *node;\n  AkObject           *xform;\n  AkInstanceGeometry *instGeom;\n  AkInstanceMorph    *morpher;\n  size_t              propLen;\n  uint32_t            nodeIndex;\n  uint32_t            itemIndex;\n  bool                hasItemIndex;\n  char                nodeid[16];\n\n  if (!ptr || ptrLen == 0)\n    return false;\n\n  p   = ptr;\n  end = ptr + ptrLen;\n\n  if (!gltf_animPtrSeg(&p, end, _s_gltf_nodes)\n      || p >= end\n      || *p != '/')\n    return false;\n\n  p++;\n  if (!gltf_animPtrIndex(&p, end, &nodeIndex))\n    return false;\n\n  if (p >= end || *p != '/')\n    return false;\n\n  p++;\n  prop = p;\n  while (p < end && *p != '/')\n    p++;\n\n  propLen      = (size_t)(p - prop);\n  hasItemIndex = false;\n  itemIndex    = 0;\n\n  sprintf(nodeid, \"%s%d\", _s_gltf_node, nodeIndex);\n  if (!(node = ak_getObjectById(gst->doc, nodeid)))\n    return false;\n\n  if (gltf_animSegEq(prop, propLen, _s_gltf_extensions)) {\n    if (!gltf_animPtrSeg(&p, end, _s_gltf_KHR_node_visibility)\n        || !gltf_animPtrSeg(&p, end, _s_gltf_visible)\n        || p != end)\n      return false;\n\n    gltf_animSetTarget(gst, ch, &node->visible, 0, false, AK_TARGET_BOOL);\n    return true;\n  }\n\n  if (p < end) {\n    p++;\n    if (!gltf_animPtrIndex(&p, end, &itemIndex) || p != end)\n      return false;\n    hasItemIndex = true;\n  }\n\n  xform = NULL;\n  if (gltf_animSegEq(prop, propLen, _s_gltf_rotation)) {\n    if (hasItemIndex && itemIndex > 3)\n      return false;\n    xform = ak_getTransformTRS(node, AKT_QUATERNION);\n    if (xform)\n      gltf_animSetTarget(gst, ch, xform, itemIndex, hasItemIndex,\n                         AK_TARGET_QUAT);\n    return xform != NULL;\n  }\n\n  if (gltf_animSegEq(prop, propLen, _s_gltf_translation)) {\n    if (hasItemIndex && itemIndex > 2)\n      return false;\n    xform = ak_getTransformTRS(node, AKT_TRANSLATE);\n    if (xform)\n      gltf_animSetTarget(gst, ch, xform, itemIndex, hasItemIndex,\n                         AK_TARGET_POSITION);\n    return xform != NULL;\n  }\n\n  if (gltf_animSegEq(prop, propLen, _s_gltf_scale)) {\n    if (hasItemIndex && itemIndex > 2)\n      return false;\n    xform = ak_getTransformTRS(node, AKT_SCALE);\n    if (xform)\n      gltf_animSetTarget(gst, ch, xform, itemIndex, hasItemIndex,\n                         AK_TARGET_SCALE);\n    return xform != NULL;\n  }\n\n  if (gltf_animSegEq(prop, propLen, _s_gltf_weights)) {\n    if (!(instGeom = node->geometry) || !(morpher = instGeom->morpher))\n      return false;\n    if (hasItemIndex\n        && morpher->morph\n        && itemIndex >= morpher->morph->targetCount)\n      return false;\n    gltf_animSetTarget(gst, ch, morpher, itemIndex, hasItemIndex,\n                       AK_TARGET_WEIGHTS);\n    return true;\n  }\n\n  return false;\n}\n\nstatic\nAkTechniqueFxCommon*\ngltf_animMaterialCommon(AkMaterial * __restrict mat) {\n  AkEffect *effect;\n\n  if (!mat || !mat->effect)\n    return NULL;\n\n  effect = mat->effect->base.url.ptr;\n  if (!effect)\n    return NULL;\n\n  return ak_getProfileTechniqueCommon(effect);\n}\n\nstatic\nvoid\ngltf_animSetColorDefault(AkColor * __restrict color,\n                         float                 r,\n                         float                 g,\n                         float                 b,\n                         float                 a) {\n  color->vec[0] = r;\n  color->vec[1] = g;\n  color->vec[2] = b;\n  color->vec[3] = a;\n}\n\nstatic\nAkColor*\ngltf_animEnsureColor(AkGLTFState * __restrict gst,\n                     AkColorDesc * __restrict desc,\n                     void        * __restrict parent,\n                     float                    r,\n                     float                    g,\n                     float                    b,\n                     float                    a) {\n  AkColor *color;\n\n  if (!desc)\n    return NULL;\n\n  if (!(color = desc->color)) {\n    color = ak_heap_calloc(gst->heap, parent, sizeof(*color));\n    gltf_animSetColorDefault(color, r, g, b, a);\n    desc->color = color;\n  }\n\n  return color;\n}\n\nstatic\nAkMaterialMetallicProp*\ngltf_animEnsureMetalProp(AkGLTFState              * __restrict gst,\n                         AkTechniqueFxCommon      * __restrict cmn,\n                         AkMaterialMetallicProp  ** __restrict prop) {\n  if (!*prop) {\n    *prop = ak_heap_calloc(gst->heap, cmn, sizeof(**prop));\n    (*prop)->intensity = 1.0f;\n  }\n\n  return *prop;\n}\n\nstatic\nAkMaterialSheen*\ngltf_animEnsureSheen(AkGLTFState         * __restrict gst,\n                     AkTechniqueFxCommon * __restrict cmn) {\n  AkMaterialSheen *sheen;\n\n  if (!(sheen = cmn->sheen)) {\n    sheen        = ak_heap_calloc(gst->heap, cmn, sizeof(*sheen));\n    sheen->color = ak_heap_calloc(gst->heap, sheen, sizeof(*sheen->color));\n    gltf_animEnsureColor(gst, sheen->color, sheen->color,\n                         0.0f, 0.0f, 0.0f, 1.0f);\n    cmn->sheen = sheen;\n  }\n\n  return sheen;\n}\n\nstatic\nAkMaterialIridescence*\ngltf_animEnsureIridescence(AkGLTFState         * __restrict gst,\n                           AkTechniqueFxCommon * __restrict cmn) {\n  AkMaterialIridescence *iri;\n\n  if (!(iri = cmn->iridescence)) {\n    iri                   = ak_heap_calloc(gst->heap, cmn, sizeof(*iri));\n    iri->ior              = 1.3f;\n    iri->thicknessMinimum = 100.0f;\n    iri->thicknessMaximum = 400.0f;\n    cmn->iridescence      = iri;\n  }\n\n  return iri;\n}\n\nstatic\nAkMaterialVolume*\ngltf_animEnsureVolume(AkGLTFState         * __restrict gst,\n                      AkTechniqueFxCommon * __restrict cmn) {\n  AkMaterialVolume *vol;\n\n  if (!(vol = cmn->volume)) {\n    vol = ak_heap_calloc(gst->heap, cmn, sizeof(*vol));\n    vol->attenuationColor.vec[0] = 1.0f;\n    vol->attenuationColor.vec[1] = 1.0f;\n    vol->attenuationColor.vec[2] = 1.0f;\n    vol->attenuationColor.vec[3] = 1.0f;\n    vol->attenuationDistance     = INFINITY;\n    cmn->volume = vol;\n  }\n\n  return vol;\n}\n\nstatic\nAkMaterialAnisotropy*\ngltf_animEnsureAnisotropy(AkGLTFState         * __restrict gst,\n                          AkTechniqueFxCommon * __restrict cmn) {\n  AkMaterialAnisotropy *aniso;\n\n  if (!(aniso = cmn->anisotropy)) {\n    aniso           = ak_heap_calloc(gst->heap, cmn, sizeof(*aniso));\n    cmn->anisotropy = aniso;\n  }\n\n  return aniso;\n}\n\nstatic\nAkMaterialDispersion*\ngltf_animEnsureDispersion(AkGLTFState         * __restrict gst,\n                          AkTechniqueFxCommon * __restrict cmn) {\n  AkMaterialDispersion *disp;\n\n  if (!(disp = cmn->dispersion)) {\n    disp            = ak_heap_calloc(gst->heap, cmn, sizeof(*disp));\n    cmn->dispersion = disp;\n  }\n\n  return disp;\n}\n\nstatic\nAkMaterialDiffuseTransmission*\ngltf_animEnsureDiffuseTransmission(AkGLTFState         * __restrict gst,\n                                   AkTechniqueFxCommon * __restrict cmn) {\n  AkMaterialDiffuseTransmission *dt;\n\n  if (!(dt = cmn->diffuseTransmission)) {\n    dt        = ak_heap_calloc(gst->heap, cmn, sizeof(*dt));\n    dt->color = ak_heap_calloc(gst->heap, dt, sizeof(*dt->color));\n    gltf_animEnsureColor(gst, dt->color, dt->color,\n                         1.0f, 1.0f, 1.0f, 1.0f);\n    cmn->diffuseTransmission = dt;\n  }\n\n  return dt;\n}\n\nstatic\nbool\ngltf_animResolveMaterialPBR(AkGLTFState          * __restrict gst,\n                            AkChannel            * __restrict ch,\n                            AkTechniqueFxCommon  * __restrict cmn,\n                            const char           *p,\n                            const char           *end) {\n  const char             *seg;\n  size_t                  segLen;\n  uint32_t                idx;\n  bool                    hasIdx;\n  AkColor                *color;\n  AkMaterialMetallicProp *prop, *rough;\n\n  if (!gltf_animPtrReadSeg(&p, end, &seg, &segLen))\n    return false;\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_baseColorTex)) {\n    if (!cmn->albedo)\n      cmn->albedo = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->albedo));\n    return gltf_animResolveTexTransform(\n             gst,\n             ch,\n             gltf_animEnsureTexRef(gst,\n                                   &cmn->albedo->texture,\n                                   cmn->albedo,\n                                   AK_TEXTURE_COLORSPACE_SRGB,\n                                   AK_TEXTURE_CHANNEL_RGBA),\n             p,\n             end);\n  }\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_metalRoughTex)) {\n    prop  = gltf_animEnsureMetalProp(gst, cmn, &cmn->metalness);\n    rough = gltf_animEnsureMetalProp(gst, cmn, &cmn->roughness);\n    prop->textureChannels  = AK_TEXTURE_CHANNEL_B;\n    rough->textureChannels = AK_TEXTURE_CHANNEL_G;\n    if (!prop->tex)\n      prop->tex = gltf_animEnsureTexRef(gst,\n                                        &prop->tex,\n                                        prop,\n                                        AK_TEXTURE_COLORSPACE_LINEAR,\n                                        AK_TEXTURE_CHANNEL_GB);\n    if (!rough->tex)\n      rough->tex = prop->tex;\n    return gltf_animResolveTexTransform(gst, ch, prop->tex, p, end);\n  }\n\n  hasIdx = false;\n  idx    = 0;\n  if (p < end) {\n    p++;\n    if (!gltf_animPtrIndex(&p, end, &idx) || p != end)\n      return false;\n    hasIdx = true;\n  }\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_baseColor)) {\n    if (!cmn->albedo)\n      cmn->albedo = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->albedo));\n    color = gltf_animEnsureColor(gst, cmn->albedo, cmn->albedo,\n                                 1.0f, 1.0f, 1.0f, 1.0f);\n    return gltf_animSetFloatArrayTarget(gst, ch, color->vec, 4, idx,\n                                        hasIdx, AK_TARGET_COLOR);\n  }\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_metalFac)) {\n    prop = gltf_animEnsureMetalProp(gst, cmn, &cmn->metalness);\n    return gltf_animSetFloatTarget(gst, ch, &prop->intensity);\n  }\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_roughFac)) {\n    prop = gltf_animEnsureMetalProp(gst, cmn, &cmn->roughness);\n    return gltf_animSetFloatTarget(gst, ch, &prop->intensity);\n  }\n\n  return false;\n}\n\nstatic\nbool\ngltf_animResolveMaterialExt(AkGLTFState          * __restrict gst,\n                            AkChannel            * __restrict ch,\n                            AkTechniqueFxCommon  * __restrict cmn,\n                            const char           *p,\n                            const char           *end) {\n  const char              *ext;\n  const char              *seg;\n  size_t                   extLen;\n  size_t                   segLen;\n  uint32_t                 idx;\n  bool                     hasIdx;\n  AkColor                 *color;\n  AkMaterialSheen         *sheen;\n  AkMaterialIridescence   *iri;\n  AkMaterialVolume        *vol;\n  AkMaterialAnisotropy    *aniso;\n  AkMaterialDispersion    *disp;\n  AkMaterialDiffuseTransmission *dt;\n\n  if (!gltf_animPtrReadSeg(&p, end, &ext, &extLen)\n      || !gltf_animPtrReadSeg(&p, end, &seg, &segLen))\n    return false;\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_clearcoat)) {\n    if (!cmn->clearcoat) {\n      cmn->clearcoat = ak_heap_calloc(gst->heap, cmn,\n                                      sizeof(*cmn->clearcoat));\n      cmn->clearcoat->normalScale = 1.0f;\n    }\n    if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatTexture))\n      cmn->clearcoat->textureChannels = AK_TEXTURE_CHANNEL_R;\n    if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatTexture))\n      return gltf_animResolveTexTransform(\n               gst, ch,\n               gltf_animEnsureTexRef(gst, &cmn->clearcoat->texture,\n                                     cmn->clearcoat,\n                                     AK_TEXTURE_COLORSPACE_LINEAR,\n                                     AK_TEXTURE_CHANNEL_R),\n               p, end);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatRoughnessTexture))\n      cmn->clearcoat->roughnessTextureChannels = AK_TEXTURE_CHANNEL_G;\n    if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatRoughnessTexture))\n      return gltf_animResolveTexTransform(\n               gst, ch,\n               gltf_animEnsureTexRef(gst,\n                                     &cmn->clearcoat->roughnessTexture,\n                                     cmn->clearcoat,\n                                     AK_TEXTURE_COLORSPACE_LINEAR,\n                                     AK_TEXTURE_CHANNEL_G),\n               p, end);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatNormalTexture))\n      return gltf_animResolveTexTransform(\n               gst, ch,\n               gltf_animEnsureTexRef(gst, &cmn->clearcoat->normalTexture,\n                                     cmn->clearcoat,\n                                     AK_TEXTURE_COLORSPACE_LINEAR,\n                                     AK_TEXTURE_CHANNEL_RGB),\n               p, end)\n             || (gltf_animPtrSeg(&p, end, _s_gltf_scale)\n                 && p == end\n                 && gltf_animSetFloatTarget(gst, ch,\n                                            &cmn->clearcoat->normalScale));\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_specular)\n      && cmn->specular) {\n    if (gltf_animSegEq(seg, segLen, _s_gltf_specularTexture))\n      cmn->specular->textureChannels = AK_TEXTURE_CHANNEL_A;\n    if (gltf_animSegEq(seg, segLen, _s_gltf_specularTexture))\n      return gltf_animResolveTexTransform(\n               gst, ch,\n               gltf_animEnsureTexRef(gst, &cmn->specular->specularTex,\n                                     cmn->specular,\n                                     AK_TEXTURE_COLORSPACE_LINEAR,\n                                     AK_TEXTURE_CHANNEL_A),\n               p, end);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_specularColorTexture))\n      return cmn->specular->color\n             && gltf_animResolveTexTransform(\n                  gst, ch,\n                  gltf_animEnsureTexRef(gst,\n                                        &cmn->specular->color->texture,\n                                        cmn->specular->color,\n                                        AK_TEXTURE_COLORSPACE_SRGB,\n                                        AK_TEXTURE_CHANNEL_RGB),\n                  p, end);\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_transmission)\n      && cmn->transmission\n      && gltf_animSegEq(seg, segLen, _s_gltf_transmissionTexture))\n    cmn->transmission->textureChannels = AK_TEXTURE_CHANNEL_R;\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_transmission)\n      && cmn->transmission\n      && gltf_animSegEq(seg, segLen, _s_gltf_transmissionTexture))\n    return gltf_animResolveTexTransform(\n             gst, ch,\n             gltf_animEnsureTexRef(gst, &cmn->transmission->texture,\n                                   cmn->transmission,\n                                   AK_TEXTURE_COLORSPACE_LINEAR,\n                                   AK_TEXTURE_CHANNEL_R),\n             p, end);\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_sheen)) {\n    sheen = gltf_animEnsureSheen(gst, cmn);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_sheenColorTexture))\n      return gltf_animResolveTexTransform(\n               gst, ch,\n               gltf_animEnsureTexRef(gst, &sheen->color->texture,\n                                     sheen->color,\n                                     AK_TEXTURE_COLORSPACE_SRGB,\n                                     AK_TEXTURE_CHANNEL_RGB),\n               p, end);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_sheenRoughnessTexture))\n      sheen->roughnessTextureChannels = AK_TEXTURE_CHANNEL_A;\n    if (gltf_animSegEq(seg, segLen, _s_gltf_sheenRoughnessTexture))\n      return gltf_animResolveTexTransform(\n               gst, ch,\n               gltf_animEnsureTexRef(gst,\n                                     &sheen->roughnessTexture,\n                                     sheen,\n                                     AK_TEXTURE_COLORSPACE_LINEAR,\n                                     AK_TEXTURE_CHANNEL_A),\n               p, end);\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_iridescence)) {\n    iri = gltf_animEnsureIridescence(gst, cmn);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceTexture))\n      iri->textureChannels = AK_TEXTURE_CHANNEL_R;\n    if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceTexture))\n      return gltf_animResolveTexTransform(\n               gst, ch,\n               gltf_animEnsureTexRef(gst,\n                                     &iri->texture,\n                                     iri,\n                                     AK_TEXTURE_COLORSPACE_LINEAR,\n                                     AK_TEXTURE_CHANNEL_R),\n               p, end);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceThicknessTexture))\n      iri->thicknessTextureChannels = AK_TEXTURE_CHANNEL_G;\n    if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceThicknessTexture))\n      return gltf_animResolveTexTransform(\n               gst, ch,\n               gltf_animEnsureTexRef(gst,\n                                     &iri->thicknessTexture,\n                                     iri,\n                                     AK_TEXTURE_COLORSPACE_LINEAR,\n                                     AK_TEXTURE_CHANNEL_G),\n               p, end);\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_volume)) {\n    vol = gltf_animEnsureVolume(gst, cmn);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_thicknessTexture))\n      vol->thicknessTextureChannels = AK_TEXTURE_CHANNEL_G;\n    if (gltf_animSegEq(seg, segLen, _s_gltf_thicknessTexture))\n      return gltf_animResolveTexTransform(\n               gst, ch,\n               gltf_animEnsureTexRef(gst,\n                                     &vol->thicknessTexture,\n                                     vol,\n                                     AK_TEXTURE_COLORSPACE_LINEAR,\n                                     AK_TEXTURE_CHANNEL_G),\n               p, end);\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_anisotropy)) {\n    aniso = gltf_animEnsureAnisotropy(gst, cmn);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_anisotropyTexture))\n      return gltf_animResolveTexTransform(\n               gst, ch,\n               gltf_animEnsureTexRef(gst,\n                                     &aniso->texture,\n                                     aniso,\n                                     AK_TEXTURE_COLORSPACE_LINEAR,\n                                     AK_TEXTURE_CHANNEL_RGB),\n               p, end);\n  }\n\n  if (gltf_animSegEq(ext, extLen,\n                     _s_gltf_KHR_materials_diffuse_transmission)) {\n    dt = gltf_animEnsureDiffuseTransmission(gst, cmn);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_diffuseTransmissionTexture))\n      dt->textureChannels = AK_TEXTURE_CHANNEL_A;\n    if (gltf_animSegEq(seg, segLen, _s_gltf_diffuseTransmissionTexture))\n      return gltf_animResolveTexTransform(\n               gst, ch,\n               gltf_animEnsureTexRef(gst,\n                                     &dt->texture,\n                                     dt,\n                                     AK_TEXTURE_COLORSPACE_LINEAR,\n                                     AK_TEXTURE_CHANNEL_A),\n               p, end);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_diffuseTransmissionColorTexture))\n      return gltf_animResolveTexTransform(\n               gst, ch,\n               gltf_animEnsureTexRef(gst,\n                                     &dt->color->texture,\n                                     dt->color,\n                                     AK_TEXTURE_COLORSPACE_SRGB,\n                                     AK_TEXTURE_CHANNEL_RGB),\n               p, end);\n  }\n\n  hasIdx = false;\n  idx    = 0;\n  if (p < end) {\n    p++;\n    if (!gltf_animPtrIndex(&p, end, &idx) || p != end)\n      return false;\n    hasIdx = true;\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_emissive_strength)\n      && gltf_animSegEq(seg, segLen, _s_gltf_emissiveStrength)) {\n    if (!cmn->emission) {\n      cmn->emission = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->emission));\n      cmn->emission->strength = 1.0f;\n    }\n    return gltf_animSetFloatTarget(gst, ch, &cmn->emission->strength);\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_ior)\n      && gltf_animSegEq(seg, segLen, _s_gltf_ior))\n    return gltf_animSetFloatTarget(gst, ch, &cmn->ior);\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_transmission)\n      && gltf_animSegEq(seg, segLen, _s_gltf_transmissionFactor)) {\n    if (!cmn->transmission)\n      cmn->transmission = ak_heap_calloc(gst->heap, cmn,\n                                         sizeof(*cmn->transmission));\n    return gltf_animSetFloatTarget(gst, ch, &cmn->transmission->factor);\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_clearcoat)) {\n    if (!cmn->clearcoat) {\n      cmn->clearcoat = ak_heap_calloc(gst->heap, cmn,\n                                      sizeof(*cmn->clearcoat));\n      cmn->clearcoat->normalScale = 1.0f;\n    }\n    if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatFactor))\n      return gltf_animSetFloatTarget(gst, ch, &cmn->clearcoat->intensity);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_clearcoatRoughnessFactor))\n      return gltf_animSetFloatTarget(gst, ch, &cmn->clearcoat->roughness);\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_specular)) {\n    if (!cmn->specular)\n      cmn->specular = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->specular));\n    if (gltf_animSegEq(seg, segLen, _s_gltf_specularFactor))\n      return gltf_animSetFloatTarget(gst, ch, &cmn->specular->strength);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_specularColorFactor)) {\n      if (!cmn->specular->color)\n        cmn->specular->color = ak_heap_calloc(gst->heap, cmn->specular,\n                                              sizeof(*cmn->specular->color));\n      color = gltf_animEnsureColor(gst, cmn->specular->color,\n                                   cmn->specular->color,\n                                   1.0f, 1.0f, 1.0f, 1.0f);\n      return gltf_animSetFloatArrayTarget(gst, ch, color->vec, 3, idx,\n                                          hasIdx, AK_TARGET_COLOR);\n    }\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_sheen)) {\n    sheen = gltf_animEnsureSheen(gst, cmn);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_sheenColorFactor)) {\n      color = gltf_animEnsureColor(gst, sheen->color, sheen->color,\n                                   0.0f, 0.0f, 0.0f, 1.0f);\n      return gltf_animSetFloatArrayTarget(gst, ch, color->vec, 3, idx,\n                                          hasIdx, AK_TARGET_COLOR);\n    }\n    if (gltf_animSegEq(seg, segLen, _s_gltf_sheenRoughnessFactor))\n      return gltf_animSetFloatTarget(gst, ch, &sheen->roughness);\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_iridescence)) {\n    iri = gltf_animEnsureIridescence(gst, cmn);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceFactor))\n      return gltf_animSetFloatTarget(gst, ch, &iri->factor);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceIor))\n      return gltf_animSetFloatTarget(gst, ch, &iri->ior);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceThicknessMinimum))\n      return gltf_animSetFloatTarget(gst, ch, &iri->thicknessMinimum);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_iridescenceThicknessMaximum))\n      return gltf_animSetFloatTarget(gst, ch, &iri->thicknessMaximum);\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_volume)) {\n    vol = gltf_animEnsureVolume(gst, cmn);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_thicknessFactor))\n      return gltf_animSetFloatTarget(gst, ch, &vol->thicknessFactor);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_attenuationDistance))\n      return gltf_animSetFloatTarget(gst, ch, &vol->attenuationDistance);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_attenuationColor))\n      return gltf_animSetFloatArrayTarget(gst, ch,\n                                          vol->attenuationColor.vec, 3,\n                                          idx, hasIdx, AK_TARGET_COLOR);\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_anisotropy)) {\n    aniso = gltf_animEnsureAnisotropy(gst, cmn);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_anisotropyStrength))\n      return gltf_animSetFloatTarget(gst, ch, &aniso->strength);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_anisotropyRotation))\n      return gltf_animSetFloatTarget(gst, ch, &aniso->rotation);\n  }\n\n  if (gltf_animSegEq(ext, extLen, _s_gltf_KHR_materials_dispersion)) {\n    disp = gltf_animEnsureDispersion(gst, cmn);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_dispersion))\n      return gltf_animSetFloatTarget(gst, ch, &disp->dispersion);\n  }\n\n  if (gltf_animSegEq(ext, extLen,\n                     _s_gltf_KHR_materials_diffuse_transmission)) {\n    dt = gltf_animEnsureDiffuseTransmission(gst, cmn);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_diffuseTransmissionFactor))\n      return gltf_animSetFloatTarget(gst, ch, &dt->factor);\n    if (gltf_animSegEq(seg, segLen,\n                       _s_gltf_diffuseTransmissionColorFactor)) {\n      color = gltf_animEnsureColor(gst, dt->color, dt->color,\n                                   1.0f, 1.0f, 1.0f, 1.0f);\n      return gltf_animSetFloatArrayTarget(gst, ch, color->vec, 3, idx,\n                                          hasIdx, AK_TARGET_COLOR);\n    }\n  }\n\n  return false;\n}\n\nstatic\nbool\ngltf_animResolveMaterialPointer(AkGLTFState     * __restrict gst,\n                                AkChannel       * __restrict ch,\n                                const char      * __restrict ptr,\n                                size_t                       ptrLen) {\n  const char          *p;\n  const char          *end;\n  const char          *seg;\n  const char          *q;\n  AkMaterial          *mat;\n  AkTechniqueFxCommon *cmn;\n  AkColor             *color;\n  size_t               segLen;\n  uint32_t             matIndex;\n  uint32_t             idx;\n  bool                 hasIdx;\n\n  p   = ptr;\n  end = ptr + ptrLen;\n\n  if (!gltf_animPtrSeg(&p, end, _s_gltf_materials)\n      || p >= end || *p != '/')\n    return false;\n\n  p++;\n  if (!gltf_animPtrIndex(&p, end, &matIndex)\n      || p >= end || *p != '/')\n    return false;\n\n  GETCHILD(gst->doc->lib.materials->chld, mat, matIndex);\n  if (!(cmn = gltf_animMaterialCommon(mat)))\n    return false;\n\n  if (!gltf_animPtrReadSeg(&p, end, &seg, &segLen))\n    return false;\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_pbrMetalRough))\n    return gltf_animResolveMaterialPBR(gst, ch, cmn, p, end);\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_extensions))\n    return gltf_animResolveMaterialExt(gst, ch, cmn, p, end);\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_normalTex)) {\n    if (!cmn->normal) {\n      cmn->normal = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->normal));\n      cmn->normal->scale = 1.0f;\n    }\n    q = p;\n    if (gltf_animPtrSeg(&q, end, _s_gltf_scale) && q == end)\n      return gltf_animSetFloatTarget(gst, ch, &cmn->normal->scale);\n    return gltf_animResolveTexTransform(\n             gst, ch,\n             gltf_animEnsureTexRef(gst,\n                                   &cmn->normal->tex,\n                                   cmn->normal,\n                                   AK_TEXTURE_COLORSPACE_LINEAR,\n                                   AK_TEXTURE_CHANNEL_RGB),\n             p, end);\n  }\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_occlusionTex)) {\n    if (!cmn->occlusion) {\n      cmn->occlusion = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->occlusion));\n      cmn->occlusion->strength = 1.0f;\n    }\n    q = p;\n    if (gltf_animPtrSeg(&q, end, _s_gltf_strength) && q == end)\n      return gltf_animSetFloatTarget(gst, ch, &cmn->occlusion->strength);\n    cmn->occlusion->textureChannels = AK_TEXTURE_CHANNEL_R;\n    return gltf_animResolveTexTransform(\n             gst, ch,\n             gltf_animEnsureTexRef(gst,\n                                   &cmn->occlusion->tex,\n                                   cmn->occlusion,\n                                   AK_TEXTURE_COLORSPACE_LINEAR,\n                                   AK_TEXTURE_CHANNEL_R),\n             p, end);\n  }\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_emissiveTex)) {\n    if (!cmn->emission) {\n      cmn->emission = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->emission));\n      cmn->emission->strength = 1.0f;\n    }\n    return gltf_animResolveTexTransform(\n             gst, ch,\n             gltf_animEnsureTexRef(gst, &cmn->emission->color.texture,\n                                   cmn->emission,\n                                   AK_TEXTURE_COLORSPACE_SRGB,\n                                   AK_TEXTURE_CHANNEL_RGB),\n             p, end);\n  }\n\n  hasIdx = false;\n  idx    = 0;\n  if (p < end) {\n    p++;\n    if (!gltf_animPtrIndex(&p, end, &idx) || p != end)\n      return false;\n    hasIdx = true;\n  }\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_emissiveFac)) {\n    if (!cmn->emission)\n      cmn->emission = ak_heap_calloc(gst->heap, cmn, sizeof(*cmn->emission));\n    color = gltf_animEnsureColor(gst, &cmn->emission->color, cmn->emission,\n                                 0.0f, 0.0f, 0.0f, 1.0f);\n    return gltf_animSetFloatArrayTarget(gst, ch, color->vec, 3, idx,\n                                        hasIdx, AK_TARGET_COLOR);\n  }\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_alphaCutoff)) {\n    if (!cmn->transparent) {\n      cmn->transparent = ak_heap_calloc(gst->heap, cmn,\n                                        sizeof(*cmn->transparent));\n      cmn->transparent->amount = 1.0f;\n      cmn->transparent->cutoff = 0.5f;\n    }\n    return gltf_animSetFloatTarget(gst, ch, &cmn->transparent->cutoff);\n  }\n\n  return false;\n}\n\nstatic\nbool\ngltf_animResolveCameraPointer(AkGLTFState     * __restrict gst,\n                              AkChannel       * __restrict ch,\n                              const char      * __restrict ptr,\n                              size_t                       ptrLen) {\n  const char     *p;\n  const char     *end;\n  const char     *seg;\n  const char     *prop;\n  AkCamera       *cam;\n  AkProjection   *proj;\n  AkPerspective  *persp;\n  AkOrthographic *ortho;\n  size_t          segLen;\n  size_t          propLen;\n  uint32_t        camIndex;\n\n  p   = ptr;\n  end = ptr + ptrLen;\n\n  if (!gltf_animPtrSeg(&p, end, _s_gltf_cameras)\n      || p >= end || *p != '/')\n    return false;\n\n  p++;\n  if (!gltf_animPtrIndex(&p, end, &camIndex))\n    return false;\n\n  GETCHILD(gst->doc->lib.cameras->chld, cam, camIndex);\n  if (!cam || !cam->optics || !(proj = cam->optics->tcommon))\n    return false;\n\n  if (!gltf_animPtrReadSeg(&p, end, &seg, &segLen)\n      || !gltf_animPtrReadSeg(&p, end, &prop, &propLen)\n      || p != end)\n    return false;\n\n  if (proj->type == AK_PROJECTION_PERSPECTIVE\n      && gltf_animSegEq(seg, segLen, _s_gltf_perspective)) {\n    persp = (AkPerspective *)proj;\n    if (gltf_animSegEq(prop, propLen, _s_gltf_xfov))\n      return gltf_animSetFloatTarget(gst, ch, &persp->xfov);\n    if (gltf_animSegEq(prop, propLen, _s_gltf_yfov))\n      return gltf_animSetFloatTarget(gst, ch, &persp->yfov);\n    if (gltf_animSegEq(prop, propLen, _s_gltf_aspectRatio))\n      return gltf_animSetFloatTarget(gst, ch, &persp->aspectRatio);\n    if (gltf_animSegEq(prop, propLen, _s_gltf_znear))\n      return gltf_animSetFloatTarget(gst, ch, &persp->znear);\n    if (gltf_animSegEq(prop, propLen, _s_gltf_zfar))\n      return gltf_animSetFloatTarget(gst, ch, &persp->zfar);\n  }\n\n  if (proj->type == AK_PROJECTION_ORTHOGRAPHIC\n      && gltf_animSegEq(seg, segLen, _s_gltf_orthographic)) {\n    ortho = (AkOrthographic *)proj;\n    if (gltf_animSegEq(prop, propLen, _s_gltf_xmag))\n      return gltf_animSetFloatTarget(gst, ch, &ortho->xmag);\n    if (gltf_animSegEq(prop, propLen, _s_gltf_ymag))\n      return gltf_animSetFloatTarget(gst, ch, &ortho->ymag);\n    if (gltf_animSegEq(prop, propLen, _s_gltf_znear))\n      return gltf_animSetFloatTarget(gst, ch, &ortho->znear);\n    if (gltf_animSegEq(prop, propLen, _s_gltf_zfar))\n      return gltf_animSetFloatTarget(gst, ch, &ortho->zfar);\n  }\n\n  return false;\n}\n\nstatic\nbool\ngltf_animResolveLightPointer(AkGLTFState     * __restrict gst,\n                             AkChannel       * __restrict ch,\n                             const char      * __restrict ptr,\n                             size_t                       ptrLen) {\n  const char  *p;\n  const char  *end;\n  const char  *seg;\n  AkLight     *light;\n  AkLightBase *base;\n  AkSpotLight *spot;\n  size_t       segLen;\n  uint32_t     lightIndex;\n  uint32_t     idx;\n  bool         hasIdx;\n\n  p   = ptr;\n  end = ptr + ptrLen;\n\n  if (!gltf_animPtrSeg(&p, end, _s_gltf_extensions)\n      || !gltf_animPtrSeg(&p, end, _s_gltf_KHR_lights_punctual)\n      || !gltf_animPtrSeg(&p, end, _s_gltf_lights)\n      || p >= end || *p != '/')\n    return false;\n\n  p++;\n  if (!gltf_animPtrIndex(&p, end, &lightIndex))\n    return false;\n\n  light = (void *)gst->doc->lib.lights->chld;\n  while (light && lightIndex > 0) {\n    light = light->next;\n    lightIndex--;\n  }\n\n  if (!light || !(base = light->tcommon))\n    return false;\n\n  if (!gltf_animPtrReadSeg(&p, end, &seg, &segLen))\n    return false;\n\n  if (base->type == AK_LIGHT_TYPE_SPOT\n      && gltf_animSegEq(seg, segLen, _s_gltf_spot)) {\n    spot = (AkSpotLight *)base;\n    if (!gltf_animPtrReadSeg(&p, end, &seg, &segLen) || p != end)\n      return false;\n    if (gltf_animSegEq(seg, segLen, _s_gltf_innerConeAngle))\n      return gltf_animSetFloatTarget(gst, ch, &spot->innerConeAngle);\n    if (gltf_animSegEq(seg, segLen, _s_gltf_outerConeAngle))\n      return gltf_animSetFloatTarget(gst, ch, &spot->outerConeAngle);\n    return false;\n  }\n\n  hasIdx = false;\n  idx    = 0;\n  if (p < end) {\n    p++;\n    if (!gltf_animPtrIndex(&p, end, &idx) || p != end)\n      return false;\n    hasIdx = true;\n  }\n\n  if (gltf_animSegEq(seg, segLen, _s_gltf_color))\n    return gltf_animSetFloatArrayTarget(gst, ch, base->color.vec, 3, idx,\n                                        hasIdx, AK_TARGET_COLOR);\n  if (gltf_animSegEq(seg, segLen, _s_gltf_intensity))\n    return gltf_animSetFloatTarget(gst, ch, &base->intensity);\n  if (gltf_animSegEq(seg, segLen, _s_gltf_range))\n    return gltf_animSetFloatTarget(gst, ch, &base->range);\n\n  return false;\n}\n\nstatic\nbool\ngltf_animResolvePointer(AkGLTFState * __restrict gst,\n                        AkChannel   * __restrict ch,\n                        const json_t * __restrict jext) {\n  const json_t *jptrExt;\n  const json_t *jptr;\n  const char   *ptr;\n\n  if (!(jptrExt = json_get(jext, _s_gltf_KHR_animation_pointer))\n      || !(jptr = json_get(jptrExt, _s_gltf_pointer))\n      || !(ptr = json_string(jptr)))\n    return false;\n\n  if (gltf_animResolveNodePointer(gst, ch, ptr, jptr->valsize))\n    return true;\n  if (gltf_animResolveMaterialPointer(gst, ch, ptr, jptr->valsize))\n    return true;\n  if (gltf_animResolveCameraPointer(gst, ch, ptr, jptr->valsize))\n    return true;\n  if (gltf_animResolveLightPointer(gst, ch, ptr, jptr->valsize))\n    return true;\n\n  return false;\n}\n\nAK_HIDE\nvoid\ngltf_animations(json_t * __restrict janim,\n                void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  AkHeap             *heap;\n  AkDoc              *doc;\n  const json_array_t *janims;\n  AkLibrary          *lib;\n  AkAnimation        *anim;\n\n  if (!(janims = json_array(janim)))\n    return;\n\n  gst   = userdata;\n  heap  = gst->heap;\n  doc   = gst->doc;\n  janim = janims->base.value;\n  lib   = ak_heap_calloc(heap, doc, sizeof(*lib));\n  \n  while (janim) {\n    json_t *anim_it;\n    \n    json_objmap_t animMap[] = {\n      JSON_OBJMAP_OBJ(_s_gltf_samplers, I2P k_anim_samplers),\n      JSON_OBJMAP_OBJ(_s_gltf_channels, I2P k_anim_channels),\n      JSON_OBJMAP_OBJ(_s_gltf_name,     I2P k_anim_name),\n    };\n    \n    json_objmap(janim, animMap, JSON_ARR_LEN(animMap));\n    \n    anim = ak_heap_calloc(heap, lib,  sizeof(*anim));\n\n    if ((anim_it = animMap[k_anim_name].object)) {\n      anim->name = json_strdup(anim_it, heap, anim);\n    }\n    \n    if ((anim_it = animMap[k_anim_samplers].object)) {\n      AkAnimSampler *sampler;\n      json_array_t  *jsamplers;\n      json_t        *jsampler;\n      \n      if (!(jsamplers = json_array(anim_it)))\n        goto anm_nxt;\n      \n      jsampler = jsamplers->base.value;\n      \n      /* samplers */\n      while (jsampler) {\n        json_t *jsampVal;\n        \n        jsampVal = jsampler->value;\n        sampler     = ak_heap_calloc(heap, anim, sizeof(*sampler));\n        \n        while (jsampVal) {\n          if (json_key_eq(jsampVal, _s_gltf_input)) {\n            AkInput *inp;\n            \n            inp              = ak_heap_calloc(heap, sampler, sizeof(*inp));\n            inp->semanticRaw = ak_heap_strdup(gst->heap, anim, _s_gltf_input);\n            inp->semantic    = AK_INPUT_INPUT;\n            inp->accessor    = flist_sp_at(&doc->lib.accessors,\n                                           json_int32(jsampVal, -1));\n            \n            ak_retain(inp->accessor);\n\n            inp->next      = sampler->input;\n            sampler->input = inp;\n          } else if (json_key_eq(jsampVal, _s_gltf_interpolation)) {\n            sampler->uniInterpolation = gltf_interp(jsampVal);\n          } else if (json_key_eq(jsampVal, _s_gltf_output)) {\n            AkInput *inp;\n            \n            inp              = ak_heap_calloc(heap, sampler, sizeof(*inp));\n            inp->semanticRaw = ak_heap_strdup(gst->heap, anim, _s_gltf_output);\n            inp->semantic    = AK_INPUT_OUTPUT;\n            inp->accessor    = flist_sp_at(&doc->lib.accessors,\n                                           json_int32(jsampVal, -1));\n            \n            ak_retain(inp->accessor);\n\n            inp->next      = sampler->input;\n            sampler->input = inp;\n          }\n          \n          /* Default is LINEAR */\n          if (sampler->uniInterpolation == AK_INTERPOLATION_UNKNOWN) {\n            sampler->uniInterpolation = AK_INTERPOLATION_LINEAR;\n          }\n\n          jsampVal = jsampVal->next;\n        }\n\n        sampler->base.next = (void *)anim->sampler;\n        anim->sampler      = sampler;\n\n        jsampler = jsampler->next;\n      }\n    }\n    \n    if ((anim_it = animMap[k_anim_channels].object)) {\n      AkChannel    *ch;\n      json_array_t *jchannels;\n      json_t       *jchannel;\n      \n      if (!(jchannels = json_array(anim_it)))\n        goto anm_nxt;\n      \n      jchannel = jchannels->base.value;\n      \n      while (jchannel) {\n        json_t *jchVal;\n        \n        ch     = ak_heap_calloc(heap, anim, sizeof(*ch));\n        jchVal = jchannel->value;\n        \n        while (jchVal) {\n          if (json_key_eq(jchVal, _s_gltf_sampler)) {\n            AkAnimSampler *sampler;\n            int32_t        samplerIndex;\n            \n            samplerIndex = json_int32(jchVal, -1);\n            GETCHILD(anim->sampler, sampler, samplerIndex);\n            ch->source.ptr = sampler;\n          } else if (json_key_eq(jchVal, _s_gltf_target)) {\n            const char *path;\n            AkNode     *node;\n            json_t     *it;\n            uint32_t    pathLen;\n            \n            json_objmap_t targetMap[] = {\n              JSON_OBJMAP_OBJ(_s_gltf_path,       I2P k_path),\n              JSON_OBJMAP_OBJ(_s_gltf_node,       I2P k_node),\n              JSON_OBJMAP_OBJ(_s_gltf_extensions, I2P k_ext)\n            };\n\n            json_objmap(jchVal, targetMap, JSON_ARR_LEN(targetMap));\n\n            path    = NULL;\n            pathLen = 0;\n\n            if ((it = targetMap[k_path].object)) {\n              path    = json_string(it);\n              pathLen = it->valsize;\n            }\n\n            if ((it = targetMap[k_ext].object)\n                && json_get(it, _s_gltf_KHR_animation_pointer)) {\n              if (!gltf_animResolvePointer(gst, ch, it))\n                gst->stop = gst->animPointerRequired;\n            } else if (path && (it = targetMap[k_node].object)) {\n              char    nodeid[16];\n              int32_t nodeIndex;\n              \n              if ((nodeIndex = json_int32(it, -1)) > -1) {\n                sprintf(nodeid, \"%s%d\", _s_gltf_node, nodeIndex);\n                \n                if ((node = ak_getObjectById(doc, nodeid))) {\n                  AkObject *xform = NULL;\n\n                  /* glTF always animates whole vec/quat (no partial component),\n                     so isPartial = false and off = 0 for all paths below. */\n                  if (strncasecmp(path, _s_gltf_rotation, pathLen) == 0) {\n                    ch->targetType = AK_TARGET_QUAT;\n                    xform          = ak_getTransformTRS(node, AKT_QUATERNION);\n                  } else if (strncasecmp(path, _s_gltf_translation, pathLen) == 0) {\n                    ch->targetType = AK_TARGET_POSITION;\n                    xform          = ak_getTransformTRS(node, AKT_TRANSLATE);\n                  } else if (strncasecmp(path, _s_gltf_scale, pathLen) == 0) {\n                    ch->targetType = AK_TARGET_SCALE;\n                    xform          = ak_getTransformTRS(node, AKT_SCALE);\n                  } else if (strncasecmp(path, _s_gltf_weights, pathLen) == 0) {\n                    AkInstanceGeometry *instGeom;\n                    AkInstanceMorph    *morpher;\n\n                    ch->targetType = AK_TARGET_WEIGHTS;\n\n                    if ((instGeom = node->geometry)\n                        && (morpher = instGeom->morpher)) {\n                      AkResolvedTarget *rt;\n\n                      rt = ak_heap_calloc(heap, ch, sizeof(*rt));\n                      ak_setypeid(rt, AKT_RESOLVED_TARGET);\n\n                      rt->target         = morpher;\n                      rt->off            = 0;\n                      rt->isPartial      = false;\n                      ch->resolvedTarget = rt;\n                    }\n                    /* else: morpher not yet set, channel left un-resolved */\n                  }\n\n                  /* common: wrap transform component in AkResolvedTarget */\n                  if (xform) {\n                    AkResolvedTarget *rt;\n\n                    rt = ak_heap_calloc(heap, ch, sizeof(*rt));\n                    ak_setypeid(rt, AKT_RESOLVED_TARGET);\n\n                    rt->target         = xform;\n                    rt->off            = 0;\n                    rt->isPartial      = false;\n                    ch->resolvedTarget = rt;\n                  }\n                }\n              } /* if nodeIndex */\n            } /* if k_node */\n          } /* if _s_gltf_target */\n          \n          jchVal = jchVal->next;\n        }\n        ch->next      = anim->channel;\n        anim->channel = ch;\n        \n        jchannel = jchannel->next;\n      }\n    }\n    \n  anm_nxt:\n\n    anim->base.next = (void *)lib->chld;\n    lib->chld       = (void *)anim;\n    lib->count++;\n\n    janim = janim->next;\n  }\n\n  doc->lib.animations = lib;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/anim.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_anim_h\n#define gltf_imp_core_anim_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_animations(json_t * __restrict janim,\n                void   * __restrict userdata);\n\n#endif /* gltf_imp_core_anim_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/asset.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"asset.h\"\n\nvoid\ngltf_asset(json_t * __restrict json,\n           void   * __restrict userdata) {\n  AkGLTFState   *gst;\n  AkAssetInf    *inf;\n  AkDoc         *doc;\n  AkHeap        *heap;\n  AkContributor *contrib;\n\n  gst  = userdata;\n  heap = gst->heap;\n  doc  = gst->doc;\n  inf  = &doc->inf->base;\n\n  contrib          = ak_heap_calloc(heap, inf, sizeof(*contrib));\n  inf->contributor = contrib;\n\n  json = json->value;\n  while (json) {\n    if (json_key_eq(json, _s_gltf_version)) {\n      if (json_float(json, 0.0f) < 2.0f) {\n        gst->stop = true;\n        return; /* unsupported version */\n      }\n    } else if (json_key_eq(json, _s_gltf_copyright)) {\n      contrib->copyright = json_strdup(json, heap, contrib);\n    } else if (json_key_eq(json, _s_gltf_generator)) {\n      contrib->authoringTool = json_strdup(json, heap, contrib);\n    }\n\n    json = json->next;\n  }\n  \n  /* glTF default definitions */\n\n  /* CoordSys is Y_UP */\n  inf->coordSys = AK_YUP;\n\n  /* Unit is meter */\n  inf->unit       = ak_heap_calloc(heap, inf, sizeof(*inf->unit));\n  inf->unit->dist = 1.0;\n  inf->unit->name = ak_heap_strdup(heap, inf->unit, _s_gltf_meter);\n\n  *(AkAssetInf **)ak_heap_ext_add(heap,\n                                  ak__alignof(doc),\n                                  AK_HEAP_NODE_FLAGS_INF) = inf;\n\n  doc->coordSys = inf->coordSys;\n  doc->unit     = inf->unit;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/asset.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_asset_h\n#define gltf_imp_core_asset_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_asset(json_t * __restrict json,\n           void   * __restrict userdata);\n\n#endif /* gltf_imp_core_asset_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/buffer.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"buffer.h\"\n#include \"ext.h\"\n#include \"../../../../utils.h\"\n#include \"../../../../base64.h\"\n\nvoid\ngltf_bufferViews(json_t * __restrict jbuffView,\n                 void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  const json_array_t *jbuffers;\n  const json_t       *jbuffVal;\n  const json_t       *jext;\n  AkBufferView       *buffView;\n  int32_t             buffIndex;\n\n  if (!(jbuffers = json_array(jbuffView)))\n    return;\n\n  gst = userdata;\n\n  jbuffView = jbuffers->base.value;\n  while (jbuffView) {\n    buffView = ak_heap_calloc(gst->heap, gst->tmpParent, sizeof(*buffView));\n    jbuffVal = jbuffView->value;\n    jext     = NULL;\n\n    while (jbuffVal) {\n      if (json_key_eq(jbuffVal, _s_gltf_buffer)) {\n        if ((buffIndex = json_int32(jbuffVal, -1)) > -1)\n          buffView->buffer = flist_sp_at(&gst->buffers, buffIndex);\n      } else if (json_key_eq(jbuffVal, _s_gltf_byteLength)) {\n        buffView->byteLength = (size_t)json_uint64(jbuffVal, 0);\n      } else if (json_key_eq(jbuffVal, _s_gltf_byteOffset)) {\n        buffView->byteOffset = (size_t)json_uint64(jbuffVal, 0);\n      } else if (json_key_eq(jbuffVal, _s_gltf_byteStride)) {\n        buffView->byteStride = (size_t)json_uint64(jbuffVal, 0);\n      } else if (json_key_eq(jbuffVal, _s_gltf_name)) {\n        buffView->name = json_strdup(jbuffVal, gst->heap, buffView);\n      } else if (json_key_eq(jbuffVal, _s_gltf_extensions)) {\n        jext = jbuffVal;\n      }\n\n      jbuffVal = jbuffVal->next;\n    }\n\n    if (!gltf_ext_bufferView(gst, buffView, jext)) {\n      gst->stop = true;\n      return;\n    }\n\n    flist_sp_insert(&gst->bufferViews, buffView);\n    jbuffView = jbuffView->next;\n  }\n}\n\nvoid\ngltf_buffers(json_t * __restrict jbuff,\n             void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  AkHeap             *heap;\n  const json_array_t *jbuffers;\n  const json_t       *jbuffVal;\n  char               *localurl;\n  char               *uri;\n  AkBuffer           *buff;\n    \n  if (!(jbuffers = json_array(jbuff)))\n    return;\n\n  gst   = userdata;\n  heap  = gst->heap;\n  jbuff = jbuffers->base.value;\n\n  while (jbuff) {\n    bool foundUri;\n\n    buff     = ak_heap_calloc(heap, gst->tmpParent, sizeof(*buff));\n    jbuffVal = jbuff->value;\n    foundUri = false;\n\n    while (jbuffVal) {\n      if (json_key_eq(jbuffVal, _s_gltf_uri)) {\n        uri = json_string_dup(jbuffVal);\n\n        if (strncmp(uri, _s_gltf_b64d, strlen(_s_gltf_b64d)) == 0) {\n          base64_buff(uri, jbuffVal->valsize, buff);\n        } else {\n          localurl = ak_getFileFrom(gst->doc, uri);\n          ak_readfile(localurl, buff, &buff->data, &buff->length);\n          ak_free(localurl);\n        }\n\n        if (uri)\n          free(uri);\n\n        foundUri = true;\n\n        /* TODO: log if logging enabled (or by log level) */\n      } else if (json_key_eq(jbuffVal, _s_gltf_name)) {\n        buff->name = json_strdup(jbuffVal, heap, buff);\n      }\n\n      jbuffVal = jbuffVal->next;\n    }\n    \n    if (!foundUri && gst->bindata) {\n      buff->data   = gst->bindata;\n      buff->length = gst->bindataLen;\n    }\n\n    flist_sp_insert(&gst->buffers, buff);\n    jbuff = jbuff->next;\n  }\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/buffer.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_buffer_h\n#define gltf_imp_core_buffer_h\n\n#include \"../common.h\"\n\nvoid\ngltf_buffers(json_t * __restrict json,\n             void   * __restrict userdata);\n\nvoid\ngltf_bufferViews(json_t * __restrict json,\n                 void   * __restrict userdata);\n\n#endif /* gltf_imp_core_buffer_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/camera.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"camera.h\"\n#include \"../extra.h\"\n\n#define k_name         0\n#define k_type         1\n#define k_perspective  2\n#define k_orthographic 3\n\nAK_HIDE\nvoid\ngltf_cameras(json_t * __restrict jcam,\n             void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  AkHeap             *heap;\n  AkDoc              *doc;\n  const json_array_t *jcams;\n  AkLibrary          *lib;\n  json_t             *it;\n\n  if (!(jcams = json_array(jcam)))\n    return;\n\n  gst       = userdata;\n  heap      = gst->heap;\n  doc       = gst->doc;\n  jcam      = jcams->base.value;\n  lib       = ak_heap_calloc(heap, doc, sizeof(*lib));\n\n  while (jcam) {\n    AkCamera   *cam;\n    AkOptics   *optics;\n    json_t     *jtechn;\n\n    cam         = ak_heap_calloc(heap, lib, sizeof(*cam));\n    optics      = ak_heap_calloc(heap, cam, sizeof(*optics));\n    cam->optics = optics;\n\n    gltf_extra(gst,\n               cam,\n               json_get(jcam, _s_gltf_extras),\n               json_get(jcam, _s_gltf_extensions));\n\n    json_objmap_t camMap[] = {\n      JSON_OBJMAP_OBJ(_s_gltf_name,         I2P k_name),\n      JSON_OBJMAP_OBJ(_s_gltf_type,         I2P k_type),\n      JSON_OBJMAP_OBJ(_s_gltf_perspective,  I2P k_perspective),\n      JSON_OBJMAP_OBJ(_s_gltf_orthographic, I2P k_orthographic)\n    };\n\n    json_objmap(jcam, camMap, JSON_ARR_LEN(camMap));\n\n    if ((it = camMap[k_name].object)) {\n      cam->name = json_strdup(it, heap, cam);\n    }\n  \n    if (!(it = camMap[k_type].object)) {\n      ak_free(cam);\n      continue;\n    }\n    \n    if (json_val_eqsz(it, _s_gltf_perspective, it->valsize)) {\n      AkPerspective *persp;\n      \n      persp            = ak_heap_calloc(heap, optics, sizeof(*persp));\n      persp->base.type = AK_PROJECTION_PERSPECTIVE;\n\n      if ((it = camMap[k_perspective].object) && (jtechn = json_json(it))) {\n        while (jtechn) {\n          if (json_key_eq(jtechn, _s_gltf_xfov)) {\n            persp->xfov = json_float(jtechn, 0.0f);\n          } else if (json_key_eq(jtechn, _s_gltf_yfov)) {\n            persp->yfov = json_float(jtechn, 0.0f);\n          } else if (json_key_eq(jtechn, _s_gltf_znear)) {\n            persp->znear = json_float(jtechn, 0.0f);\n          } else if (json_key_eq(jtechn, _s_gltf_zfar)) {\n            persp->zfar = json_float(jtechn, 0.0f);\n          } else if (json_key_eq(jtechn, _s_gltf_aspectRatio)) {\n            persp->aspectRatio = json_float(jtechn, 0.0f);\n          }\n          jtechn = jtechn->next;\n        }\n      }\n\n      if (!persp->aspectRatio && persp->yfov && persp->xfov) {\n        persp->aspectRatio = persp->xfov / persp->yfov;\n      } else if (!persp->yfov && persp->aspectRatio && persp->xfov) {\n        persp->yfov = persp->xfov / persp->aspectRatio;\n      } else if (!persp->xfov && persp->aspectRatio && persp->yfov) {\n        persp->xfov = persp->yfov * persp->aspectRatio;\n      }\n\n      optics->tcommon = &persp->base;\n    } else if (json_val_eqsz(it, _s_gltf_orthographic, it->valsize)) {\n      AkOrthographic *ortho;\n\n      ortho            = ak_heap_calloc(heap, optics, sizeof(*ortho));\n      ortho->base.type = AK_PROJECTION_ORTHOGRAPHIC;\n\n      if ((it = camMap[k_orthographic].object) && (jtechn = json_json(it))) {\n        while (jtechn) {\n          if (json_key_eq(jtechn, _s_gltf_xmag)) {\n            ortho->xmag = json_float(jtechn, 0.0f);\n          } else if (json_key_eq(jtechn, _s_gltf_ymag)) {\n            ortho->ymag = json_float(jtechn, 0.0f);\n          } else if (json_key_eq(jtechn, _s_gltf_znear)) {\n            ortho->znear = json_float(jtechn, 0.0f);\n          } else if (json_key_eq(jtechn, _s_gltf_zfar)) {\n            ortho->zfar = json_float(jtechn, 0.0f);\n          }\n\n          jtechn = jtechn->next;\n        }\n      }\n\n      if (ortho->ymag && ortho->xmag)\n        ortho->aspectRatio = ortho->xmag / ortho->ymag;\n\n      optics->tcommon = &ortho->base;\n    }\n    \n    cam->base.next = lib->chld;\n    lib->chld      = (void *)cam;\n    \n    lib->count++;\n\n    jcam = jcam->next;\n  }\n\n  gst->doc->lib.cameras = lib;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/camera.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_camera_h\n#define gltf_imp_core_camera_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_cameras(json_t * __restrict jcam,\n             void   * __restrict userdata);\n\n#endif /* gltf_imp_core_camera_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/enum.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"enum.h\"\n\nAK_HIDE AkEnum\ngltf_enumInputSemantic(const char * name) {\n  AkEnum val;\n  long   glenums_len;\n  long   i;\n\n  dae_enum glenums[] = {\n    {_s_gltf_COLOR,     AK_INPUT_COLOR},\n    {_s_gltf_JOINTS,    AK_INPUT_JOINT},\n    {_s_gltf_NORMAL,    AK_INPUT_NORMAL},\n    {_s_gltf_POSITION,  AK_INPUT_POSITION},\n    {_s_gltf_TANGENT,   AK_INPUT_TANGENT},\n    {_s_gltf_TEXCOORD,  AK_INPUT_TEXCOORD},\n    {_s_gltf_WEIGHTS,   AK_INPUT_WEIGHT},\n  };\n\n  val = AK_INPUT_OTHER;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (strcasecmp(name, glenums[i].name) == 0) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ngltf_componentType(int type) {\n  switch (type) {\n    case 5120:  return AKT_BYTE;   break;\n    case 5121:  return AKT_UBYTE;  break;\n    case 5122:  return AKT_SHORT;  break;\n    case 5123:  return AKT_USHORT; break;\n    case 5125:  return AKT_UINT;   break;\n    case 5126:  return AKT_FLOAT;  break;\n    default: break;\n  }\n  return AKT_NONE;\n}\n\nAK_HIDE int\ngltf_componentLen(int type) {\n  switch (type) {\n    case 5120:            /* AKT_BYTE   */\n    case 5121:  return 1; /* AKT_UBYTE  */\n    case 5122:            /* AKT_SHORT  */\n    case 5123:  return 2; /* AKT_USHORT */\n    case 5125:            /* AKT_UINT   */\n    case 5126:  return 4; /* AKT_FLOAT  */\n    default: return 1;\n  }\n}\n\nAK_HIDE AkComponentSize\ngltf_type(const json_t * __restrict json) {\n  AkComponentSize val;\n  long            glenums_len;\n  long            i;\n\n  dae_enum glenums[] = {\n    {_s_gltf_SCALAR, AK_COMPONENT_SIZE_SCALAR},\n    {_s_gltf_VEC2,   AK_COMPONENT_SIZE_VEC2},\n    {_s_gltf_VEC3,   AK_COMPONENT_SIZE_VEC3},\n    {_s_gltf_VEC4,   AK_COMPONENT_SIZE_VEC4},\n    {_s_gltf_MAT2,   AK_COMPONENT_SIZE_MAT2},\n    {_s_gltf_MAT3,   AK_COMPONENT_SIZE_MAT3},\n    {_s_gltf_MAT4,   AK_COMPONENT_SIZE_MAT4},\n  };\n\n  val         = AK_COMPONENT_SIZE_UNKNOWN;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (json_val_eq(json, glenums[i].name)) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkEnum\ngltf_minFilter(int type) {\n  switch (type) {\n    case 9728:  return AK_MAGFILTER_NEAREST;       break;\n    case 9729:  return AK_MAGFILTER_LINEAR;        break;\n\n    case 9984:  return AK_NEAREST_MIPMAP_NEAREST;  break;\n    case 9985:  return AK_LINEAR_MIPMAP_NEAREST;   break;\n    case 9986:  return AK_NEAREST_MIPMAP_LINEAR;   break;\n    case 9987:  return AK_LINEAR_MIPMAP_LINEAR;    break;\n    default: break;\n  }\n  return 0;\n}\n\nAK_HIDE AkEnum\ngltf_magFilter(int type) {\n  switch (type) {\n    case 9728:  return AK_MINFILTER_NEAREST;   break;\n    case 9729:  return AK_MINFILTER_LINEAR;    break;\n    default: break;\n  }\n  return 0;\n}\n\nAK_HIDE AkEnum\ngltf_wrapMode(int type) {\n  switch (type) {\n    case 33071:  return AK_WRAP_MODE_CLAMP;       break;\n    case 33648:  return AK_WRAP_MODE_MIRROR;      break;\n    case 10497:  return AK_WRAP_MODE_WRAP;        break;\n    default: break;\n  }\n  return AK_WRAP_MODE_WRAP;\n}\n\nAK_HIDE AkOpaque\ngltf_alphaMode(const json_t * __restrict json) {\n  AkEnum val;\n  long   glenums_len;\n  long   i;\n\n  dae_enum glenums[] = {\n    {_s_gltf_OPAQUE, AK_OPAQUE_OPAQUE},\n    {_s_gltf_MASK,   AK_OPAQUE_MASK},\n    {_s_gltf_BLEND,  AK_OPAQUE_BLEND}\n  };\n\n  val         = AK_OPAQUE_OPAQUE;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (json_val_eq(json, glenums[i].name)) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n\nAK_HIDE AkInterpolationType\ngltf_interp(const json_t * __restrict json) {\n  AkEnum val;\n  long   glenums_len;\n  long   i;\n\n  dae_enum glenums[] = {\n    {_s_gltf_LINEAR,       AK_INTERPOLATION_LINEAR},\n    {_s_gltf_STEP,         AK_INTERPOLATION_STEP},\n    {_s_gltf_CUBICSPLINE,  AK_INTERPOLATION_HERMITE}\n  };\n\n  val         = AK_INTERPOLATION_LINEAR;\n  glenums_len = AK_ARRAY_LEN(glenums);\n\n  for (i = 0; i < glenums_len; i++) {\n    if (json_val_eq(json, glenums[i].name)) {\n      val = glenums[i].val;\n      break;\n    }\n  }\n\n  return val;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/enum.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_enums_h\n#define gltf_imp_core_enums_h\n\n#include \"../common.h\"\n\nAK_HIDE AkEnum\ngltf_enumInputSemantic(const char *name);\n\nAK_HIDE AkEnum\ngltf_componentType(int type);\n\nAK_HIDE int\ngltf_componentLen(int type) ;\n\nAK_HIDE AkComponentSize\ngltf_type(const json_t * __restrict json);\n\nAK_HIDE AkEnum\ngltf_minFilter(int type);\n\nAK_HIDE AkEnum\ngltf_magFilter(int type);\n\nAK_HIDE AkEnum\ngltf_wrapMode(int type);\n\nAK_HIDE AkOpaque\ngltf_alphaMode(const json_t * __restrict json);\n\nAK_HIDE AkInterpolationType\ngltf_interp(const json_t * __restrict json);\n\n#endif /* gltf_imp_core_enums_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/ext.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ext.h\"\n#include \"../ext/decoder.h\"\n#include \"../ext/instancing.h\"\n#include \"../ext/lights.h\"\n#include \"../ext/variants.h\"\n\ntypedef struct AkGLTFExtName {\n  const char *name;\n  size_t      nameSize;\n} AkGLTFExtName;\n\n#define GLTF_EXT_NAME(NAME) { _s_gltf_ ## NAME, sizeof(#NAME) - 1 }\n\nstatic\nint\ngltf_extNameCmp(const void * __restrict a,\n                const void * __restrict b) {\n  const AkGLTFExtName *extname;\n  const char *val;\n  size_t      valSize;\n  size_t      minSize;\n  int         res;\n\n  extname = b;\n  if (!(val = json_string(a)))\n    return -1;\n\n  valSize = (size_t)((const json_t *)a)->valsize;\n  minSize = valSize < extname->nameSize ? valSize : extname->nameSize;\n  res     = strncmp(val, extname->name, minSize);\n\n  if (res != 0)                    return res;\n  if (valSize < extname->nameSize) return -1;\n  if (valSize > extname->nameSize) return 1;\n\n  return 0;\n}\n\nstatic inline\nbool\ngltf_extNameSearch(const json_t        * __restrict ext,\n                   const AkGLTFExtName * __restrict names,\n                   size_t                           count) {\n  return ext && bsearch(ext,\n                        names,\n                        count,\n                        sizeof(names[0]),\n                        gltf_extNameCmp) != NULL;\n}\n\nstatic\nbool\ngltf_ext_preserved_supported(const json_t * __restrict ext) {\n  static const AkGLTFExtName names[] = {\n    GLTF_EXT_NAME(ADOBE_materials_clearcoat_specular),\n    GLTF_EXT_NAME(ADOBE_materials_clearcoat_tint),\n    GLTF_EXT_NAME(ADOBE_materials_thin_transparency),\n    GLTF_EXT_NAME(AGI_articulations),\n    GLTF_EXT_NAME(AGI_stk_metadata),\n    GLTF_EXT_NAME(CESIUM_primitive_outline),\n    GLTF_EXT_NAME(EXT_gsplat_compression_spz),\n    GLTF_EXT_NAME(EXT_lights_ies),\n    GLTF_EXT_NAME(EXT_lights_image_based),\n    GLTF_EXT_NAME(EXT_mesh_manifold),\n    GLTF_EXT_NAME(EXT_mesh_primitive_restart),\n    GLTF_EXT_NAME(EXT_texture_astc),\n    GLTF_EXT_NAME(FB_geometry_metadata),\n    GLTF_EXT_NAME(GODOT_single_root),\n    GLTF_EXT_NAME(GRIFFEL_bim_data),\n    GLTF_EXT_NAME(KHR_techniques_webgl),\n    GLTF_EXT_NAME(KHR_xmp),\n    GLTF_EXT_NAME(MPEG_accessor_timed),\n    GLTF_EXT_NAME(MPEG_animation_timing),\n    GLTF_EXT_NAME(MPEG_audio_spatial),\n    GLTF_EXT_NAME(MPEG_buffer_circular),\n    GLTF_EXT_NAME(MPEG_media),\n    GLTF_EXT_NAME(MPEG_mesh_linking),\n    GLTF_EXT_NAME(MPEG_scene_dynamic),\n    GLTF_EXT_NAME(MPEG_texture_video),\n    GLTF_EXT_NAME(MPEG_viewport_recommended),\n    GLTF_EXT_NAME(MSFT_lod),\n    GLTF_EXT_NAME(MSFT_packing_normalRoughnessMetallic),\n    GLTF_EXT_NAME(MSFT_packing_occlusionRoughnessMetallic),\n    GLTF_EXT_NAME(MSFT_texture_dds),\n    GLTF_EXT_NAME(NV_materials_mdl)\n  };\n\n  return gltf_extNameSearch(ext, names, AK_ARRAY_LEN(names));\n}\n\nstatic\nbool\ngltf_ext_supported(AkGLTFState      * __restrict gst,\n                   const json_t     * __restrict ext) {\n  static const AkGLTFExtName names[] = {\n    GLTF_EXT_NAME(EXT_mesh_gpu_instancing),\n    GLTF_EXT_NAME(EXT_texture_webp),\n    GLTF_EXT_NAME(KHR_animation_pointer),\n    GLTF_EXT_NAME(KHR_gaussian_splatting),\n    GLTF_EXT_NAME(KHR_lights_punctual),\n    GLTF_EXT_NAME(KHR_materials_anisotropy),\n    GLTF_EXT_NAME(KHR_materials_clearcoat),\n    GLTF_EXT_NAME(KHR_materials_diffuse_transmission),\n    GLTF_EXT_NAME(KHR_materials_dispersion),\n    GLTF_EXT_NAME(KHR_materials_emissive_strength),\n    GLTF_EXT_NAME(KHR_materials_ior),\n    GLTF_EXT_NAME(KHR_materials_iridescence),\n    GLTF_EXT_NAME(KHR_materials_pbrSpecularGlossiness),\n    GLTF_EXT_NAME(KHR_materials_sheen),\n    GLTF_EXT_NAME(KHR_materials_specular),\n    GLTF_EXT_NAME(KHR_materials_transmission),\n    GLTF_EXT_NAME(KHR_materials_unlit),\n    GLTF_EXT_NAME(KHR_materials_variants),\n    GLTF_EXT_NAME(KHR_materials_volume),\n    GLTF_EXT_NAME(KHR_mesh_quantization),\n    GLTF_EXT_NAME(KHR_node_visibility),\n    GLTF_EXT_NAME(KHR_texture_transform),\n    GLTF_EXT_NAME(KHR_xmp_json_ld)\n  };\n\n  if (!ext)\n    return false;\n\n  if (gltf_extNameSearch(ext, names, AK_ARRAY_LEN(names)))\n    return true;\n\n  if (json_val_eq(ext, _s_gltf_EXT_meshopt_compression)\n      || json_val_eq(ext, _s_gltf_KHR_meshopt_compression))\n    return gltf_ext_meshopt(gst);\n  if (json_val_eq(ext, _s_gltf_KHR_draco_mesh_compression))\n    return gltf_ext_draco(gst);\n  if (json_val_eq(ext, _s_gltf_KHR_texture_basisu))\n    return gltf_ext_ktx2(gst);\n  if (gltf_ext_preserved_supported(ext))\n    return true;\n\n  return false;\n}\n\nAK_HIDE\nvoid\ngltf_exts(json_t * __restrict jext,\n          void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  const json_array_t *jexts;\n  json_t             *it;\n\n  gst = userdata;\n\n  if (!(jexts = json_array(jext)))\n    return;\n\n  for (it = (void *)jexts->base.value; it; it = it->next) {\n    if (json_val_eq(it, _s_gltf_KHR_animation_pointer))\n      gst->animPointerRequired = true;\n\n    if (!gltf_ext_supported(gst, it)) {\n      gst->stop = true;\n      return;\n    }\n  }\n}\n\nAK_HIDE\nvoid\ngltf_ext_root(json_t * __restrict jext,\n              void   * __restrict userdata) {\n  AkGLTFState *gst;\n  json_t      *jpunctual;\n  json_t      *jlights;\n  json_t      *jvariantsExt;\n  json_t      *jvariants;\n\n  gst = userdata;\n  if (!jext)\n    return;\n\n  if ((jpunctual = json_get(jext, _s_gltf_KHR_lights_punctual))\n      && (jlights = json_get(jpunctual, _s_gltf_lights))) {\n    gltf_ext_lights(gst, jlights);\n  }\n\n  if ((jvariantsExt = json_get(jext, _s_gltf_KHR_materials_variants))\n      && (jvariants = json_get(jvariantsExt, _s_gltf_variants))) {\n    gltf_ext_materialVariants(gst, jvariants);\n  }\n}\n\nAK_HIDE\nbool\ngltf_ext_node(AkGLTFState * __restrict gst,\n              AkNode      * __restrict node,\n              const json_t * __restrict jext) {\n  json_t  *jvis;\n  json_t  *jvisible;\n  json_t  *jinstancing;\n\n  if (!gst || !node || !jext)\n    return true;\n\n  jvis     = json_get(jext, _s_gltf_KHR_node_visibility);\n  jvisible = jvis ? json_get(jvis, _s_gltf_visible) : NULL;\n\n  if (jvisible)\n    node->visible = json_bool(jvisible, true);\n\n  if ((jinstancing = json_get(jext, _s_gltf_EXT_mesh_gpu_instancing))) {\n    node->instancing = gltf_ext_meshGPUInstancing(gst, node, jinstancing);\n    if (gst->stop)\n      return false;\n  }\n\n  return gltf_ext_nodeLight(gst, node, jext);\n}\n\nAK_HIDE\nvoid\ngltf_ext_close(AkGLTFState * __restrict gst) {\n  gltf_ext_decoderClose(gst);\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/ext.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_ext_h\n#define gltf_imp_core_ext_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_exts(json_t * __restrict jext,\n          void   * __restrict userdata);\n\nAK_HIDE\nvoid\ngltf_ext_root(json_t * __restrict jext,\n              void   * __restrict userdata);\n\nAK_HIDE\nbool\ngltf_ext_node(AkGLTFState * __restrict gst,\n              AkNode      * __restrict node,\n              const json_t * __restrict jext);\n\nAK_HIDE\nvoid\ngltf_ext_close(AkGLTFState * __restrict gst);\n\nAK_HIDE\nbool\ngltf_ext_bufferView(AkGLTFState  * __restrict gst,\n                    AkBufferView * __restrict buffView,\n                    const json_t * __restrict jext);\n\nAK_HIDE\nbool\ngltf_ext_textureBasisu(AkGLTFState * __restrict gst);\n\nAK_HIDE\nbool\ngltf_ext_dracoPrimitive(AkGLTFState     * __restrict gst,\n                        AkMeshPrimitive * __restrict prim,\n                        const json_t    * __restrict jprim);\n\n/*!\n * @brief Parse `KHR_materials_variants` mappings on a mesh primitive.\n *\n * Reads `primitive.extensions.KHR_materials_variants.mappings[]` from\n * `jprim` and populates `prim->variantMappings`. The doc-level variant\n * list must already be resolved (gltf_ext_root). Returns true on\n * success or absence; false only on irrecoverable error.\n */\nAK_HIDE\nbool\ngltf_ext_primitiveVariants(AkGLTFState     * __restrict gst,\n                           AkMeshPrimitive * __restrict prim,\n                           const json_t    * __restrict jprim);\n\n/*!\n * @brief Parse `KHR_gaussian_splatting` on a mesh primitive.\n *\n * Reads `primitive.extensions.KHR_gaussian_splatting.{kernel,colorSpace,\n * projection,sortingMethod}` and populates `prim->gsplat`. If the\n * extension carries a compression sub-extension (future spec) and an\n * external Gaussian splat decoder is loaded (see\n * AK_OPT_GLTF_GSPLAT_DECODER_PATH), the decoder is invoked to populate\n * `gsplat->decodedData`. Without compression and without a decoder the\n * primitive's standard accessor chain stays authoritative — renderers\n * that don't recognize the extension fall back to point-cloud display.\n */\nAK_HIDE\nbool\ngltf_ext_primitiveGaussianSplat(AkGLTFState     * __restrict gst,\n                                AkMeshPrimitive * __restrict prim,\n                                const json_t    * __restrict jprim);\n\n#endif /* gltf_imp_core_ext_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/image.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"image.h\"\n#include \"../extra.h\"\n#include \"../../../../base64.h\"\n\n#define k_name          0\n#define k_bufferView    1\n#define k_uri           2\n#define k_mimeType      3\n\nstatic\nchar*\ngltf_imageDataUriMime(AkHeap      * __restrict heap,\n                      void        * __restrict parent,\n                      const char  * __restrict uri,\n                      int                      len) {\n  const char *start;\n  const char *end;\n  int         prefixLen;\n\n  if (!uri || len <= 0)\n    return NULL;\n\n  prefixLen = (int)strlen(_s_gltf_b64d);\n  if (len <= prefixLen || strncmp(uri, _s_gltf_b64d, (size_t)prefixLen) != 0)\n    return NULL;\n\n  start = uri + prefixLen;\n  end   = memchr(start, ';', (size_t)(len - prefixLen));\n  if (!end || end <= start)\n    return NULL;\n\n  return ak_heap_strndup(heap, parent, start, (size_t)(end - start));\n}\n\nAK_HIDE\nvoid\ngltf_images(json_t * __restrict jimage,\n            void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  AkHeap             *heap;\n  const json_array_t *jimages;\n  AkImage            *image;\n  json_t             *it;\n\n  if (!(jimages = json_array(jimage)))\n    return;\n\n  gst    = userdata;\n  heap   = gst->heap;\n  jimage = jimages->base.value;\n\n  while (jimage) {\n    AkInitFrom *initFrom;\n\n    image    = ak_heap_calloc(gst->heap, gst->doc, sizeof(*image));\n    initFrom = NULL;\n    gltf_extra(gst,\n               image,\n               json_get(jimage, _s_gltf_extras),\n               json_get(jimage, _s_gltf_extensions));\n    \n    json_objmap_t imgMap[] = {\n      JSON_OBJMAP_OBJ(_s_gltf_name,       I2P k_name),\n      JSON_OBJMAP_OBJ(_s_gltf_bufferView, I2P k_bufferView),\n      JSON_OBJMAP_OBJ(_s_gltf_uri,        I2P k_uri),\n      JSON_OBJMAP_OBJ(\"mimeType\",         I2P k_mimeType)\n    };\n\n    json_objmap(jimage, imgMap, JSON_ARR_LEN(imgMap));\n\n    if ((it = imgMap[k_name].object)) {\n      image->name = json_strdup(it, gst->heap, image);\n    }\n    \n    if ((it = imgMap[k_bufferView].object)) {\n      AkBuffer     *tmpbuff;\n      AkBufferView *buffView;\n      int32_t       buffViewIndex;\n      \n      if ((buffViewIndex = json_int32(it, -1)) > -1\n          && (buffView = flist_sp_at(&gst->bufferViews, buffViewIndex))\n          && (tmpbuff = buffView->buffer)) {\n        initFrom             = ak_heap_calloc(heap, image, sizeof(*initFrom));\n        initFrom->buff       = ak_heap_calloc(heap,\n                                              gst->doc,\n                                              sizeof(*initFrom->buff));\n        initFrom->buff->length = buffView->byteLength;\n\n        if (gst->borrowBufferViews) {\n          initFrom->buff->data = (char *)tmpbuff->data + buffView->byteOffset;\n          initFrom->buff->name = \"assetkit:gltf-buffer-view-slice\";\n        } else {\n          initFrom->buff->data = ak_heap_alloc(heap,\n                                               initFrom->buff,\n                                               buffView->byteLength);\n          memcpy(initFrom->buff->data,\n                 (char *)tmpbuff->data + buffView->byteOffset,\n                 buffView->byteLength);\n        }\n        if ((it = imgMap[k_mimeType].object))\n          initFrom->buffMime = json_strdup(it, heap, initFrom);\n        image->initFrom = initFrom;\n      }\n    }\n    \n    if (!initFrom && (it = imgMap[k_uri].object)) {\n      initFrom = ak_heap_calloc(heap, image, sizeof(*initFrom));\n      \n      if (!strncmp(it->value, _s_gltf_b64d, strlen(_s_gltf_b64d))) {\n        char *uri;\n        uri = it->value;\n\n        initFrom->buff = ak_heap_calloc(heap, gst->doc, sizeof(*initFrom->buff));\n        base64_buff(uri, it->valsize, initFrom->buff);\n        if (imgMap[k_mimeType].object)\n          initFrom->buffMime = json_strdup(imgMap[k_mimeType].object, heap, initFrom);\n        else\n          initFrom->buffMime = gltf_imageDataUriMime(heap, initFrom, uri, it->valsize);\n      } else {\n        initFrom->ref = json_strdup(it, gst->heap, initFrom);\n      }\n      image->initFrom = initFrom;\n    }\n\n    flist_sp_insert(&gst->doc->lib.images, image);\n    jimage = jimage->next;\n  }\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/image.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_image_h\n#define gltf_imp_core_image_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_images(json_t * __restrict jimage,\n            void   * __restrict userdata);\n\n#endif /* gltf_imp_core_image_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/material.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"material.h\"\n#include \"profile.h\"\n#include \"sampler.h\"\n#include \"texture.h\"\n#include \"enum.h\"\n#include \"../extra.h\"\n#include \"../../../../default/material.h\"\n\nAK_HIDE\nAkMaterial*\ngltf_default_mat(AkGLTFState *gst, AkLibrary *libmat) {\n  AkHeap                 *heap;\n  AkInstanceEffect       *ieff;\n  AkEffect               *effect;\n  AkProfileCommon        *pcommon;\n  AkTechniqueFx          *technfx;\n  AkTechniqueFxCommon    *cmnTechn;\n  AkMaterial             *mat;\n  AkMaterialMetallicProp *metalness, *roughness;\n  AkColorDesc            *colorDesc;\n  AkTransparent          *transp;\n\n  heap               = gst->heap;\n  pcommon            = gltf_cmnEffect(gst);\n  effect             = ak_mem_parent(pcommon);\n  technfx            = ak_heap_calloc(heap, pcommon, sizeof(*technfx));\n  mat                = ak_heap_calloc(heap, libmat,  sizeof(*mat));\n  cmnTechn           = ak_heap_calloc(heap, technfx, sizeof(*cmnTechn));;\n  pcommon->technique = technfx;\n\n  ak_setypeid(technfx, AKT_TECHNIQUE_FX);\n\n  cmnTechn->type = AK_MATERIAL_PBR;\n\n  metalness               = ak_heap_calloc(heap, cmnTechn, sizeof(*metalness));\n  roughness               = ak_heap_calloc(heap, cmnTechn, sizeof(*roughness));\n\n  metalness->intensity    = 1.0f;\n  roughness->intensity    = 1.0f;\n\n  cmnTechn->metalness     = metalness;\n  cmnTechn->roughness     = roughness;\n\n  cmnTechn->albedo        = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->albedo));\n  cmnTechn->albedo->color = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->albedo->color));\n\n  /* DEFAULT value by spec */\n  glm_vec4_copy(GLM_VEC4_ONE, cmnTechn->albedo->color->vec);\n\n  /* emissive */\n  cmnTechn->emission           = ak_heap_calloc(heap, technfx, sizeof(*cmnTechn->emission));\n  colorDesc                    = &cmnTechn->emission->color;\n  colorDesc->color             = ak_heap_calloc(heap, colorDesc, sizeof(*colorDesc->color));\n  colorDesc->color->vec[3]     = 1.0f;\n  cmnTechn->emission->strength = 1.0f;\n\n  /* transparent */\n  transp                = ak_heap_calloc(heap, cmnTechn, sizeof(*transp));\n  transp->amount        = 1.0f;\n  transp->opaque        = AK_OPAQUE_OPAQUE;\n  transp->cutoff        = 0.5f;\n  cmnTechn->transparent = transp;\n\n  technfx->common    = cmnTechn;\n  ieff               = ak_heap_calloc(heap, mat, sizeof(*ieff));\n  ieff->base.type    = AK_INSTANCE_EFFECT;\n  ieff->base.url.ptr = effect;\n  mat->effect        = ieff;\n\n  return mat;\n}\n\nstatic\nAkColorDesc*\ngltf_materialColorDesc(AkGLTFState * __restrict gst,\n                       void        * __restrict parent,\n                       float                    r,\n                       float                    g,\n                       float                    b,\n                       float                    a) {\n  AkColorDesc *desc;\n\n  desc        = ak_heap_calloc(gst->heap, parent, sizeof(*desc));\n  desc->color = ak_heap_calloc(gst->heap, desc, sizeof(*desc->color));\n\n  desc->color->vec[0] = r;\n  desc->color->vec[1] = g;\n  desc->color->vec[2] = b;\n  desc->color->vec[3] = a;\n\n  return desc;\n}\n\nstatic\nAkTextureRef*\ngltf_materialTexRef(AkGLTFState        * __restrict gst,\n                    void               * __restrict parent,\n                    json_t             * __restrict jtexinfo,\n                    AkTextureColorSpace             colorSpace,\n                    AkTextureChannels               channels) {\n  return ak_texref_usage(gltf_texref(gst, parent, jtexinfo),\n                         colorSpace,\n                         channels);\n}\n\nstatic\nvoid\ngltf_materialParseSpecular(AkGLTFState         * __restrict gst,\n                           AkTechniqueFxCommon * __restrict cmnTechn,\n                           json_t              * __restrict jspec) {\n  AkMaterialSpecularProp *specularProp;\n  json_t                 *jval;\n\n  if (!(specularProp = cmnTechn->specular)) {\n    specularProp           = ak_heap_calloc(gst->heap, cmnTechn,\n                                            sizeof(*specularProp));\n    specularProp->strength = 1.0f;\n    specularProp->color    = gltf_materialColorDesc(gst, specularProp,\n                                                    1.0f, 1.0f, 1.0f, 1.0f);\n    cmnTechn->specular     = specularProp;\n  }\n\n  jval = jspec->value;\n  while (jval) {\n    if (json_key_eq(jval, _s_gltf_specularFactor)) {\n      specularProp->strength = json_float(jval, 1.0f);\n    } else if (json_key_eq(jval, _s_gltf_specularTexture)) {\n      specularProp->specularTex = gltf_materialTexRef(gst,\n                                                       cmnTechn,\n                                                       jval,\n                                                       AK_TEXTURE_COLORSPACE_LINEAR,\n                                                       AK_TEXTURE_CHANNEL_A);\n      specularProp->textureChannels = AK_TEXTURE_CHANNEL_A;\n    } else if (json_key_eq(jval, _s_gltf_specularColorFactor)) {\n      json_array_float(specularProp->color->color->vec, jval, 0.0f, 3, true);\n      specularProp->color->color->vec[3] = 1.0f;\n    } else if (json_key_eq(jval, _s_gltf_specularColorTexture)) {\n      specularProp->color->texture = gltf_materialTexRef(gst,\n                                                          cmnTechn,\n                                                          jval,\n                                                          AK_TEXTURE_COLORSPACE_SRGB,\n                                                          AK_TEXTURE_CHANNEL_RGB);\n    }\n    jval = jval->next;\n  }\n}\n\nstatic\nvoid\ngltf_materialParseClearcoat(AkGLTFState         * __restrict gst,\n                            AkTechniqueFxCommon * __restrict cmnTechn,\n                            json_t              * __restrict jspec) {\n  AkMaterialClearcoat *clearcoat;\n  json_t              *jval;\n\n  if (!(clearcoat = cmnTechn->clearcoat)) {\n    clearcoat           = ak_heap_calloc(gst->heap, cmnTechn,\n                                         sizeof(*clearcoat));\n    clearcoat->normalScale = 1.0f;\n    cmnTechn->clearcoat = clearcoat;\n  }\n\n  jval = jspec->value;\n  while (jval) {\n    if (json_key_eq(jval, _s_gltf_clearcoatFactor)) {\n      clearcoat->intensity = json_float(jval, 0.0f);\n    } else if (json_key_eq(jval, _s_gltf_clearcoatTexture)) {\n      clearcoat->texture = gltf_materialTexRef(gst,\n                                                cmnTechn,\n                                                jval,\n                                                AK_TEXTURE_COLORSPACE_LINEAR,\n                                                AK_TEXTURE_CHANNEL_R);\n      clearcoat->textureChannels = AK_TEXTURE_CHANNEL_R;\n    } else if (json_key_eq(jval, _s_gltf_clearcoatRoughnessFactor)) {\n      clearcoat->roughness = json_float(jval, 0.0f);\n    } else if (json_key_eq(jval, _s_gltf_clearcoatRoughnessTexture)) {\n      clearcoat->roughnessTexture = gltf_materialTexRef(gst,\n                                                         cmnTechn,\n                                                         jval,\n                                                         AK_TEXTURE_COLORSPACE_LINEAR,\n                                                         AK_TEXTURE_CHANNEL_G);\n      clearcoat->roughnessTextureChannels = AK_TEXTURE_CHANNEL_G;\n    } else if (json_key_eq(jval, _s_gltf_clearcoatNormalTexture)) {\n      clearcoat->normalTexture = gltf_materialTexRef(gst,\n                                                      cmnTechn,\n                                                      jval,\n                                                      AK_TEXTURE_COLORSPACE_LINEAR,\n                                                      AK_TEXTURE_CHANNEL_RGB);\n      clearcoat->normalScale   = json_float(json_get(jval, _s_gltf_scale),\n                                            1.0f);\n    }\n    jval = jval->next;\n  }\n}\n\nstatic\nvoid\ngltf_materialParseTransmission(AkGLTFState         * __restrict gst,\n                               AkTechniqueFxCommon * __restrict cmnTechn,\n                               json_t              * __restrict jspec) {\n  AkMaterialTransmissionProp *transmissionProp;\n  json_t                     *jval;\n\n  if (!(transmissionProp = cmnTechn->transmission)) {\n    transmissionProp       = ak_heap_calloc(gst->heap, cmnTechn,\n                                            sizeof(*transmissionProp));\n    cmnTechn->transmission = transmissionProp;\n  }\n\n  jval = jspec->value;\n  while (jval) {\n    if (json_key_eq(jval, _s_gltf_transmissionFactor)) {\n      transmissionProp->factor = json_float(jval, 0.0f);\n    } else if (json_key_eq(jval, _s_gltf_transmissionTexture)) {\n      transmissionProp->texture = gltf_materialTexRef(gst,\n                                                       cmnTechn,\n                                                       jval,\n                                                       AK_TEXTURE_COLORSPACE_LINEAR,\n                                                       AK_TEXTURE_CHANNEL_R);\n      transmissionProp->textureChannels = AK_TEXTURE_CHANNEL_R;\n    }\n    jval = jval->next;\n  }\n}\n\nstatic\nvoid\ngltf_materialParseSheen(AkGLTFState         * __restrict gst,\n                        AkTechniqueFxCommon * __restrict cmnTechn,\n                        json_t              * __restrict jspec) {\n  AkMaterialSheen *sheen;\n  json_t          *jval;\n\n  if (!(sheen = cmnTechn->sheen)) {\n    sheen          = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*sheen));\n    sheen->color   = gltf_materialColorDesc(gst, sheen,\n                                            0.0f, 0.0f, 0.0f, 1.0f);\n    cmnTechn->sheen = sheen;\n  }\n\n  jval = jspec->value;\n  while (jval) {\n    if (json_key_eq(jval, _s_gltf_sheenColorFactor)) {\n      json_array_float(sheen->color->color->vec, jval, 0.0f, 3, true);\n      sheen->color->color->vec[3] = 1.0f;\n    } else if (json_key_eq(jval, _s_gltf_sheenColorTexture)) {\n      sheen->color->texture = gltf_materialTexRef(gst,\n                                                   sheen,\n                                                   jval,\n                                                   AK_TEXTURE_COLORSPACE_SRGB,\n                                                   AK_TEXTURE_CHANNEL_RGB);\n    } else if (json_key_eq(jval, _s_gltf_sheenRoughnessFactor)) {\n      sheen->roughness = json_float(jval, 0.0f);\n    } else if (json_key_eq(jval, _s_gltf_sheenRoughnessTexture)) {\n      sheen->roughnessTexture = gltf_materialTexRef(gst,\n                                                     sheen,\n                                                     jval,\n                                                     AK_TEXTURE_COLORSPACE_LINEAR,\n                                                     AK_TEXTURE_CHANNEL_A);\n      sheen->roughnessTextureChannels = AK_TEXTURE_CHANNEL_A;\n    }\n    jval = jval->next;\n  }\n}\n\nstatic\nvoid\ngltf_materialParseIridescence(AkGLTFState         * __restrict gst,\n                              AkTechniqueFxCommon * __restrict cmnTechn,\n                              json_t              * __restrict jspec) {\n  AkMaterialIridescence *iri;\n  json_t                *jval;\n\n  if (!(iri = cmnTechn->iridescence)) {\n    iri                   = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*iri));\n    iri->ior              = 1.3f;\n    iri->thicknessMinimum = 100.0f;\n    iri->thicknessMaximum = 400.0f;\n    cmnTechn->iridescence = iri;\n  }\n\n  jval = jspec->value;\n  while (jval) {\n    if (json_key_eq(jval, _s_gltf_iridescenceFactor)) {\n      iri->factor = json_float(jval, 0.0f);\n    } else if (json_key_eq(jval, _s_gltf_iridescenceTexture)) {\n      iri->texture = gltf_materialTexRef(gst,\n                                          iri,\n                                          jval,\n                                          AK_TEXTURE_COLORSPACE_LINEAR,\n                                          AK_TEXTURE_CHANNEL_R);\n      iri->textureChannels = AK_TEXTURE_CHANNEL_R;\n    } else if (json_key_eq(jval, _s_gltf_iridescenceIor)) {\n      iri->ior = json_float(jval, 1.3f);\n    } else if (json_key_eq(jval, _s_gltf_iridescenceThicknessMinimum)) {\n      iri->thicknessMinimum = json_float(jval, 100.0f);\n    } else if (json_key_eq(jval, _s_gltf_iridescenceThicknessMaximum)) {\n      iri->thicknessMaximum = json_float(jval, 400.0f);\n    } else if (json_key_eq(jval, _s_gltf_iridescenceThicknessTexture)) {\n      iri->thicknessTexture = gltf_materialTexRef(gst,\n                                                   iri,\n                                                   jval,\n                                                   AK_TEXTURE_COLORSPACE_LINEAR,\n                                                   AK_TEXTURE_CHANNEL_G);\n      iri->thicknessTextureChannels = AK_TEXTURE_CHANNEL_G;\n    }\n    jval = jval->next;\n  }\n}\n\nstatic\nvoid\ngltf_materialParseVolume(AkGLTFState         * __restrict gst,\n                         AkTechniqueFxCommon * __restrict cmnTechn,\n                         json_t              * __restrict jspec) {\n  AkMaterialVolume *vol;\n  json_t           *jval;\n\n  if (!(vol = cmnTechn->volume)) {\n    vol = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*vol));\n    vol->attenuationColor.vec[0] = 1.0f;\n    vol->attenuationColor.vec[1] = 1.0f;\n    vol->attenuationColor.vec[2] = 1.0f;\n    vol->attenuationColor.vec[3] = 1.0f;\n    vol->attenuationDistance = INFINITY;\n    cmnTechn->volume = vol;\n  }\n\n  jval = jspec->value;\n  while (jval) {\n    if (json_key_eq(jval, _s_gltf_thicknessFactor)) {\n      vol->thicknessFactor = json_float(jval, 0.0f);\n    } else if (json_key_eq(jval, _s_gltf_thicknessTexture)) {\n      vol->thicknessTexture = gltf_materialTexRef(gst,\n                                                  vol,\n                                                  jval,\n                                                  AK_TEXTURE_COLORSPACE_LINEAR,\n                                                  AK_TEXTURE_CHANNEL_G);\n      vol->thicknessTextureChannels = AK_TEXTURE_CHANNEL_G;\n    } else if (json_key_eq(jval, _s_gltf_attenuationDistance)) {\n      vol->attenuationDistance = json_float(jval, INFINITY);\n    } else if (json_key_eq(jval, _s_gltf_attenuationColor)) {\n      json_array_float(vol->attenuationColor.vec, jval, 1.0f, 3, true);\n      vol->attenuationColor.vec[3] = 1.0f;\n    }\n    jval = jval->next;\n  }\n}\n\nstatic\nvoid\ngltf_materialParseAnisotropy(AkGLTFState         * __restrict gst,\n                             AkTechniqueFxCommon * __restrict cmnTechn,\n                             json_t              * __restrict jspec) {\n  AkMaterialAnisotropy *aniso;\n  json_t               *jval;\n\n  if (!(aniso = cmnTechn->anisotropy)) {\n    aniso                 = ak_heap_calloc(gst->heap, cmnTechn,\n                                           sizeof(*aniso));\n    cmnTechn->anisotropy  = aniso;\n  }\n\n  jval = jspec->value;\n  while (jval) {\n    if (json_key_eq(jval, _s_gltf_anisotropyStrength)) {\n      aniso->strength = json_float(jval, 0.0f);\n    } else if (json_key_eq(jval, _s_gltf_anisotropyRotation)) {\n      aniso->rotation = json_float(jval, 0.0f);\n    } else if (json_key_eq(jval, _s_gltf_anisotropyTexture)) {\n      aniso->texture = gltf_materialTexRef(gst,\n                                            aniso,\n                                            jval,\n                                            AK_TEXTURE_COLORSPACE_LINEAR,\n                                            AK_TEXTURE_CHANNEL_RGB);\n    }\n    jval = jval->next;\n  }\n}\n\nstatic\nvoid\ngltf_materialParseDispersion(AkGLTFState         * __restrict gst,\n                             AkTechniqueFxCommon * __restrict cmnTechn,\n                             json_t              * __restrict jspec) {\n  AkMaterialDispersion *disp;\n\n  if (!(disp = cmnTechn->dispersion)) {\n    disp                 = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*disp));\n    cmnTechn->dispersion = disp;\n  }\n\n  disp->dispersion = json_float(json_get(jspec, _s_gltf_dispersion), 0.0f);\n}\n\nstatic\nvoid\ngltf_materialParseDiffuseTransmission(AkGLTFState         * __restrict gst,\n                                      AkTechniqueFxCommon * __restrict cmnTechn,\n                                      json_t              * __restrict jspec) {\n  AkMaterialDiffuseTransmission *dt;\n  json_t                        *jval;\n\n  if (!(dt = cmnTechn->diffuseTransmission)) {\n    dt        = ak_heap_calloc(gst->heap, cmnTechn, sizeof(*dt));\n    dt->color = gltf_materialColorDesc(gst, dt, 1.0f, 1.0f, 1.0f, 1.0f);\n    cmnTechn->diffuseTransmission = dt;\n  }\n\n  jval = jspec->value;\n  while (jval) {\n    if (json_key_eq(jval, _s_gltf_diffuseTransmissionFactor)) {\n      dt->factor = json_float(jval, 0.0f);\n    } else if (json_key_eq(jval, _s_gltf_diffuseTransmissionTexture)) {\n      dt->texture = gltf_materialTexRef(gst,\n                                         dt,\n                                         jval,\n                                         AK_TEXTURE_COLORSPACE_LINEAR,\n                                         AK_TEXTURE_CHANNEL_A);\n      dt->textureChannels = AK_TEXTURE_CHANNEL_A;\n    } else if (json_key_eq(jval, _s_gltf_diffuseTransmissionColorFactor)) {\n      json_array_float(dt->color->color->vec, jval, 1.0f, 3, true);\n      dt->color->color->vec[3] = 1.0f;\n    } else if (json_key_eq(jval, _s_gltf_diffuseTransmissionColorTexture)) {\n      dt->color->texture = gltf_materialTexRef(gst,\n                                                dt,\n                                                jval,\n                                                AK_TEXTURE_COLORSPACE_SRGB,\n                                                AK_TEXTURE_CHANNEL_RGB);\n    }\n    jval = jval->next;\n  }\n}\n\nAK_HIDE\nvoid\ngltf_materials(json_t * __restrict jmaterial,\n               void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  AkHeap             *heap;\n  AkDoc              *doc;\n  const json_array_t *jmaterials;\n  AkLibrary          *libmat;\n\n  gst          = userdata;\n  heap         = gst->heap;\n  doc          = gst->doc;\n  libmat       = ak_heap_calloc(heap, doc, sizeof(*libmat));\n  doc->lib.materials = libmat;\n\n  gst->defaultMaterial = gltf_default_mat(gst, libmat);\n\n  if (!(jmaterials = json_array(jmaterial)))\n    return;\n\n  jmaterial = jmaterials->base.value;\n  while (jmaterial) {\n    json_t                 *jmatVal, *jext;\n    AkProfileCommon        *pcommon;\n    AkTechniqueFx          *technfx;\n    AkTechniqueFxCommon    *cmnTechn;\n    AkMaterial             *mat;\n    AkEffect               *effect;\n    AkInstanceEffect       *ieff;\n\n    pcommon            = gltf_cmnEffect(gst);\n    effect             = ak_mem_parent(pcommon);\n    technfx            = ak_heap_calloc(heap, pcommon, sizeof(*technfx));\n    mat                = ak_heap_calloc(heap, libmat,  sizeof(*mat));\n    cmnTechn           = ak_heap_calloc(heap, technfx, sizeof(*cmnTechn));;\n    pcommon->technique = technfx;\n\n    ak_setypeid(technfx, AKT_TECHNIQUE_FX);\n\n    cmnTechn->type = AK_MATERIAL_PBR;\n    cmnTechn->ior  = 1.5f;\n\n    jmatVal = jmaterial->value;\n    gltf_extra(gst,\n               mat,\n               json_get(jmaterial, _s_gltf_extras),\n               json_get(jmaterial, _s_gltf_extensions));\n\n    if ((jext = json_get(jmaterial, _s_gltf_extensions))) {\n      json_t *jspec, *jval;\n\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_specular)))\n        gltf_materialParseSpecular(gst, cmnTechn, jspec);\n\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_clearcoat)))\n        gltf_materialParseClearcoat(gst, cmnTechn, jspec);\n\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_unlit)))\n        cmnTechn->type = AK_MATERIAL_CONSTANT;\n\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_emissive_strength))) {\n        AkMaterialEmissionProp *emission;\n\n        if (!(emission = cmnTechn->emission)) {\n          emission           = ak_heap_calloc(heap, cmnTechn, sizeof(*emission));\n          cmnTechn->emission = emission;\n        }\n\n        emission->strength = json_float(json_get(jspec, _s_gltf_emissiveStrength), 1.0f);\n      }\n\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_ior)))\n        cmnTechn->ior = json_float(json_get(jspec, _s_gltf_ior), 1.5f);\n\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_transmission)))\n        gltf_materialParseTransmission(gst, cmnTechn, jspec);\n\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_sheen)))\n        gltf_materialParseSheen(gst, cmnTechn, jspec);\n\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_iridescence)))\n        gltf_materialParseIridescence(gst, cmnTechn, jspec);\n\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_volume)))\n        gltf_materialParseVolume(gst, cmnTechn, jspec);\n\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_anisotropy)))\n        gltf_materialParseAnisotropy(gst, cmnTechn, jspec);\n\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_dispersion)))\n        gltf_materialParseDispersion(gst, cmnTechn, jspec);\n\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_diffuse_transmission)))\n        gltf_materialParseDiffuseTransmission(gst, cmnTechn, jspec);\n\n      /* ARCHIVED: Superseded by KHR_materials_specular */\n      if ((jspec = json_get(jext, _s_gltf_KHR_materials_pbrSpecularGlossiness))) {\n        AkMaterialSpecularProp *specularProp;\n        AkColorDesc            *specularColor;\n\n        specularProp           = ak_heap_calloc(heap, cmnTechn, sizeof(*specularProp));\n        specularProp->strength = 1.0f;\n        cmnTechn->specular     = specularProp;\n        cmnTechn->type         = AK_MATERIAL_SPECULAR_GLOSSINES;\n        specularColor        = ak_heap_calloc(heap, specularProp, sizeof(*specularColor));\n        specularColor->color = ak_heap_calloc(heap, specularColor, sizeof(*specularColor->color));\n        specularProp->color  = specularColor;\n\n        if (!cmnTechn->albedo) {\n          cmnTechn->diffuse = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->diffuse));\n        }\n        cmnTechn->diffuse->color = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->diffuse->color));\n\n        glm_vec4_copy(GLM_VEC4_ONE, cmnTechn->diffuse->color->vec);\n        glm_vec4_copy(GLM_VEC4_ONE, specularColor->color->vec);\n\n        jval = jspec->value;\n        while (jval) {\n          if (json_key_eq(jval, _s_gltf_diffuseFactor)) {\n            json_array_float(cmnTechn->diffuse->color->vec, jval, 0.0f, 4, true);\n          } else if (json_key_eq(jval, _s_gltf_specFactor)) {\n            json_array_float(specularColor->color->vec, jval, 0.0f, 3, true);\n            specularColor->color->vec[3] = 1.0f;\n          } else if (json_key_eq(jval, _s_gltf_glossFactor)) {\n            specularProp->strength = json_float(jval, 1.0f);\n          } else if (json_key_eq(jval, _s_gltf_diffuseTexture)) {\n            cmnTechn->diffuse->texture = gltf_materialTexRef(gst,\n                                                              cmnTechn,\n                                                              jval,\n                                                              AK_TEXTURE_COLORSPACE_SRGB,\n                                                              AK_TEXTURE_CHANNEL_RGBA);\n          } else if (json_key_eq(jval, _s_gltf_specGlossTex)) {\n            specularProp->specularTex = gltf_materialTexRef(gst,\n                                                             cmnTechn,\n                                                             jval,\n                                                             AK_TEXTURE_COLORSPACE_SRGB,\n                                                             AK_TEXTURE_CHANNEL_RGBA);\n          }\n          jval = jval->next;\n        }\n      }\n    } /* _s_gltf_extensions */\n\n    while (jmatVal) {\n      /* Metallic Roughness */\n      if (json_key_eq(jmatVal, _s_gltf_name)) {\n        mat->name = json_strdup(jmatVal, heap, mat);\n      } else if (json_key_eq(jmatVal, _s_gltf_pbrMetalRough)) {\n        AkMaterialMetallicProp *metalness, *roughness;\n        json_t *jmrVal;\n\n        metalness               = ak_heap_calloc(heap, cmnTechn, sizeof(*metalness));\n        roughness               = ak_heap_calloc(heap, cmnTechn, sizeof(*roughness));\n\n        metalness->intensity    = 1.0f;\n        roughness->intensity    = 1.0f;\n\n        cmnTechn->metalness     = metalness;\n        cmnTechn->roughness     = roughness;\n\n        if (!cmnTechn->albedo) {\n          cmnTechn->albedo = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->albedo));\n        }\n\n        cmnTechn->albedo->color = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->albedo->color));\n\n        /* DEFAULT value by spec */\n        glm_vec4_copy(GLM_VEC4_ONE, cmnTechn->albedo->color->vec);\n\n        jmrVal = jmatVal->value;\n        while (jmrVal) {\n          if (json_key_eq(jmrVal, _s_gltf_baseColor)) {\n            json_array_float(cmnTechn->albedo->color->vec, jmrVal,  0.0f, 4, true);\n          } else if (json_key_eq(jmrVal, _s_gltf_metalFac)) {\n            metalness->intensity = json_float(jmrVal, 0.0f);\n          } else if (json_key_eq(jmrVal, _s_gltf_roughFac)) {\n            roughness->intensity = json_float(jmrVal, 0.0f);\n          } else if (json_key_eq(jmrVal, _s_gltf_metalRoughTex)) {\n            metalness->tex = gltf_materialTexRef(gst,\n                                                  metalness,\n                                                  jmrVal,\n                                                  AK_TEXTURE_COLORSPACE_LINEAR,\n                                                  AK_TEXTURE_CHANNEL_GB);\n            metalness->textureChannels = AK_TEXTURE_CHANNEL_B;\n            roughness->tex = metalness->tex;\n            roughness->textureChannels = AK_TEXTURE_CHANNEL_G;\n          } else if (json_key_eq(jmrVal, _s_gltf_baseColorTex)) {\n            cmnTechn->albedo->texture = gltf_materialTexRef(gst,\n                                                             cmnTechn->albedo,\n                                                             jmrVal,\n                                                             AK_TEXTURE_COLORSPACE_SRGB,\n                                                             AK_TEXTURE_CHANNEL_RGBA);\n          }\n\n          jmrVal = jmrVal->next;\n        }\n      } else if (json_key_eq(jmatVal, _s_gltf_emissiveFac)) {\n        AkMaterialEmissionProp *emission;\n        AkColor                *color;\n\n        if (!(emission = cmnTechn->emission)) {\n          emission           = ak_heap_calloc(heap, technfx, sizeof(*emission));\n          emission->strength = 1.0f;\n          cmnTechn->emission = emission;\n        }\n\n        if (!(color = emission->color.color)) {\n          emission->color.color = color = ak_heap_calloc(heap, emission, sizeof(*color));\n        }\n\n        json_array_float(color->vec, jmatVal, 0.0f, 3, true);\n        color->vec[3] = 1.0f;\n      } else if (json_key_eq(jmatVal, _s_gltf_emissiveTex)) {\n        AkMaterialEmissionProp *emission;\n\n        if (!(emission = cmnTechn->emission)) {\n          emission           = ak_heap_calloc(heap, technfx, sizeof(*emission));\n          emission->strength = 1.0f;\n          cmnTechn->emission = emission;\n        }\n\n        emission->color.texture = gltf_materialTexRef(gst,\n                                                       emission,\n                                                       jmatVal,\n                                                       AK_TEXTURE_COLORSPACE_SRGB,\n                                                       AK_TEXTURE_CHANNEL_RGB);\n      } else if (json_key_eq(jmatVal, _s_gltf_occlusionTex)) {\n        /* Occlusion Map */\n        AkOcclusion *occl;\n\n        occl           = ak_heap_calloc(heap, technfx, sizeof(*occl));\n        occl->tex      = gltf_materialTexRef(gst,\n                                              occl,\n                                              jmatVal,\n                                              AK_TEXTURE_COLORSPACE_LINEAR,\n                                              AK_TEXTURE_CHANNEL_R);\n        occl->strength = json_float(json_get(jmatVal, _s_gltf_strength), 1.0f);\n        occl->textureChannels = AK_TEXTURE_CHANNEL_R;\n\n        cmnTechn->occlusion = occl;\n\n      } else if (json_key_eq(jmatVal, _s_gltf_normalTex)) {\n        /* Normap Map */\n        AkNormalMap *normal;\n\n        normal        = ak_heap_calloc(heap, technfx, sizeof(*normal));\n        normal->tex   = gltf_materialTexRef(gst,\n                                             normal,\n                                             jmatVal,\n                                             AK_TEXTURE_COLORSPACE_LINEAR,\n                                             AK_TEXTURE_CHANNEL_RGB);\n        normal->scale = json_float(json_get(jmatVal, _s_gltf_scale), 1.0f);\n\n        cmnTechn->normal = normal;\n\n      } else if (json_key_eq(jmatVal, _s_gltf_doubleSided)) {\n        /* doubleSided */\n        cmnTechn->doubleSided = json_bool(jmatVal, 0);\n      } else if (json_key_eq(jmatVal, _s_gltf_alphaMode)) {\n        AkTransparent *transp;\n\n        if (!(transp = cmnTechn->transparent)) {\n          transp                = ak_heap_calloc(heap, cmnTechn, sizeof(*transp));\n          transp->amount        = 1.0f;\n          transp->cutoff        = 0.5f;\n          transp->opaque        = AK_OPAQUE_OPAQUE;\n          cmnTechn->transparent = transp;\n        }\n\n        transp->opaque = gltf_alphaMode(jmatVal);\n      } else if (json_key_eq(jmatVal, _s_gltf_alphaCutoff)) {\n        AkTransparent *transp;\n\n        if (!(transp = cmnTechn->transparent)) {\n          transp                = ak_heap_calloc(heap, cmnTechn, sizeof(*transp));\n          transp->amount        = 1.0f;\n          transp->cutoff        = 0.5f;\n          transp->opaque        = AK_OPAQUE_OPAQUE;\n          cmnTechn->transparent = transp;\n        }\n\n        transp->cutoff = json_float(jmatVal, 0.5f);\n      }\n\n      jmatVal = jmatVal->next;\n    }\n\n    technfx->common    = cmnTechn;\n    ieff               = ak_heap_calloc(heap, mat, sizeof(*ieff));\n    ieff->base.type    = AK_INSTANCE_EFFECT;\n    ieff->base.url.ptr = effect;\n    mat->effect        = ieff;\n\n    mat->base.next     = libmat->chld;\n    libmat->chld       = (void *)mat;\n    libmat->count++;\n\n    jmaterial = jmaterial->next;\n  }\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/material.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_material_h\n#define gltf_imp_core_material_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_materials(json_t * __restrict json,\n               void   * __restrict userdata);\n\n#endif /* gltf_imp_core_material_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/mesh.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"mesh.h\"\n#include \"enum.h\"\n#include \"accessor.h\"\n#include \"buffer.h\"\n#include \"ext.h\"\n#include \"../extra.h\"\n#include \"../../../../accessor.h\"\n#include \"../../../common/util.h\"\n\n#include <ds/rb.h>\n\n/*\n  glTF meshes      -> AkGeometry > AkMesh\n  glTF primitives  -> AkMeshPrimitive\n */\n\nstatic\nbool\ngltf_attrIndexedSemantic(const char * __restrict key,\n                         size_t                  keysize,\n                         const char ** __restrict semantic) {\n  const char *sep, *end, *c;\n\n  if (!key || keysize == 0)\n    return false;\n\n  sep = memchr(key, '_', keysize);\n  end = key + keysize;\n  if (!sep || sep == key || sep + 1 >= end)\n    return false;\n\n  for (c = sep + 1; c < end; c++) {\n    if (*c < '0' || *c > '9')\n      return false;\n  }\n\n  if (semantic)\n    *semantic = sep;\n  return true;\n}\n\nstatic\nvoid\ngltf_inputSemantic(AkHeap * __restrict heap,\n                   AkInput * __restrict inp,\n                   json_t  * __restrict jattrib) {\n  const char *semantic;\n\n  if (gltf_attrIndexedSemantic(jattrib->key, jattrib->keysize, &semantic)) {\n    inp->semanticRaw = ak_heap_strndup(heap,\n                                       inp,\n                                       jattrib->key,\n                                       semantic - jattrib->key);\n    inp->set = (uint32_t)strtol(semantic + 1, NULL, 10);\n  } else {\n    inp->semanticRaw = ak_heap_strndup(heap,\n                                       inp,\n                                       jattrib->key,\n                                       jattrib->keysize);\n  }\n}\n\nstatic\nAkMorphPreset*\ngltf_meshMorphPresets(AkGLTFState * __restrict gst,\n                      void        * __restrict parent,\n                      json_t      * __restrict jpresets,\n                      uint32_t    * __restrict count) {\n  json_array_t *jarr;\n  json_t       *jpreset;\n  json_t       *jname;\n  json_t       *jweights;\n  json_array_t *jwarr;\n  AkMorphPreset *presets;\n  AkMorphPreset *preset;\n  AkFloatArray *weights;\n  AkHeap       *heap;\n  uint32_t      n, i;\n\n  *count = 0;\n\n  if (!(jarr = json_array(jpresets)) || jarr->count == 0)\n    return NULL;\n\n  heap    = gst->heap;\n  n       = (uint32_t)jarr->count;\n  presets = ak_heap_calloc(heap, parent, sizeof(*presets) * n);\n  jpreset = jarr->base.value;\n  i       = 0;\n\n  while (jpreset && i < n) {\n    preset = &presets[n - 1 - i];\n\n    if ((jname = json_get(jpreset, _s_gltf_name)))\n      preset->name = json_strdup(jname, heap, parent);\n\n    if ((jweights = json_get(jpreset, _s_gltf_weights))\n        && (jwarr = json_array(jweights))\n        && jwarr->count > 0) {\n      weights = ak_heap_alloc(heap,\n                              parent,\n                              sizeof(*weights) + sizeof(float) * jwarr->count);\n      json_array_float(weights->items,\n                       jweights,\n                       0.0f,\n                       jwarr->count,\n                       true);\n      weights->count   = jwarr->count;\n      preset->weights  = weights;\n    }\n\n    i++;\n    jpreset = jpreset->next;\n  }\n\n  *count = n;\n  return presets;\n}\n\nAK_HIDE\nvoid\ngltf_meshes(json_t * __restrict jmesh,\n            void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  AkHeap             *heap;\n  AkDoc              *doc;\n  AkLibrary          *lib;\n  const json_array_t *jmeshes;\n  const json_t       *jmeshVal;\n\n  if (!(jmeshes = json_array(jmesh)))\n    return;\n\n  gst        = userdata;\n  heap       = gst->heap;\n  doc        = gst->doc;\n  lib        = ak_heap_calloc(heap, doc, sizeof(*lib));\n\n  jmesh = jmeshes->base.value;\n  while (jmesh) {\n    AkGeometry      *geom;\n    AkMesh          *mesh;\n    AkObject        *meshObj;\n    uint32_t         mode;\n\n    /* mesh-level morph state — built lazily on first primitive that has\n       targets, then re-used by subsequent primitives so that all primitives\n       of the mesh share one AkMorph (per glTF spec, weights are mesh-level). */\n    AkMorph         *meshMorph        = NULL;\n    AkMorphTarget  **meshTargetArr    = NULL;\n    uint32_t         meshTargetCnt    = 0;\n\n    /* mesh.extras.targetNames (blend shape names) — parsed independently of\n       primitive order; attached to meshMorph after all keys are processed. */\n    const char     **morphTargetNames = NULL;\n    uint32_t         morphTargetNamesN = 0;\n    AkMorphPreset   *morphPresets      = NULL;\n    uint32_t         morphPresetCount  = 0;\n    uint32_t         morphPresetIdx;\n    uint32_t         morphPresetWrite;\n\n    mesh                 = ak_allocMesh(heap, lib, &geom);\n    meshObj              = ak_objFrom(mesh);\n    mesh->primitiveCount = 0;\n\n    gltf_extra(gst,\n               meshObj,\n               json_get(jmesh, _s_gltf_extras),\n               json_get(jmesh, _s_gltf_extensions));\n    mesh->extra = ak_extra(meshObj);\n\n    jmeshVal = jmesh->value;\n    while (jmeshVal) {\n      if (json_key_eq(jmeshVal, _s_gltf_primitives)\n          && json_is_array(jmeshVal)) {\n        json_t *jprim;\n\n        jprim = jmeshVal->value;\n        while (jprim) {\n          AkMeshPrimitive *prim;\n          json_t          *jprimVal;\n\n          mode = json_int32(json_get(jprim, _s_gltf_mode), 4);\n          prim = gltf_allocPrim(heap, meshObj, mode);\n\n          prim->input      = NULL;\n          prim->inputCount = 0;\n          prim->mesh       = mesh;\n\n          gltf_extra(gst,\n                     prim,\n                     json_get(jprim, _s_gltf_extras),\n                     json_get(jprim, _s_gltf_extensions));\n          prim->extra = ak_extra(prim);\n\n          jprimVal = jprim->value;\n\n          while (jprimVal) {\n            if (json_key_eq(jprimVal, _s_gltf_attributes)) {\n              json_t *jattrib;\n\n              /* attributes */\n              jattrib = jprimVal->value;\n              while (jattrib) {\n                AkInput    *inp;\n\n                inp      = ak_heap_calloc(heap, prim, sizeof(*inp));\n                gltf_inputSemantic(heap, inp, jattrib);\n\n                inp->semantic = gltf_enumInputSemantic(inp->semanticRaw);\n                inp->accessor = flist_sp_at(&doc->lib.accessors,\n                                            json_int32(jattrib, -1));\n                if (!inp->accessor) {\n                  jattrib = jattrib->next;\n                  continue;\n                }\n\n                if (inp->semantic == AK_INPUT_POSITION)\n                  prim->pos = inp;\n\n                inp->next   = prim->input;\n                prim->input = inp;\n                prim->inputCount++;\n\n                jattrib = jattrib->next;\n              } /* jprimAttrib */\n            } else if (json_key_eq(jprimVal, _s_gltf_indices)) {\n              AkAccessor   *acc;\n              AkBuffer     *indicesBuff;\n              AkUIntArray  *indices;\n              AkUInt       *it1;\n              char         *it2;\n              size_t        count, k, itemSize;\n\n              if (!(acc = flist_sp_at(&doc->lib.accessors,\n                                      json_int32(jprimVal, -1)))\n                  || !(indicesBuff = acc->buffer))\n                goto prim_next;\n\n              itemSize = acc->bytesPerComponent;\n              count    = acc->count;\n              indices  = ak_heap_calloc(heap,\n                                        prim,\n                                        sizeof(*indices)\n                                        + sizeof(AkUInt) * count);\n              indices->count = count;\n              it1            = indices->items;\n              it2            = ((char *)indicesBuff->data) + acc->byteOffset;\n\n              /* we cannot use memcpy here, because we will promote short, byte\n                 type to int32 (for now)\n               */\n              for (k = 0; k < count; k++) {\n                memcpy(&it1[k], it2 + itemSize * k, itemSize);\n              }\n\n              prim->indices     = indices;\n              prim->indexStride = 1;\n            } else if (json_key_eq(jprimVal, _s_gltf_material)) {\n              AkMaterial *mat;\n              int32_t     matIndex;\n\n              matIndex = json_int32(jprimVal, -1);\n              GETCHILD(gst->doc->lib.materials->chld, mat, matIndex);\n       \n              if (mat) { prim->material = mat;                  }\n              else     { prim->material = gst->defaultMaterial; }\n            } else if (json_key_eq(jprimVal, _s_gltf_targets)) {\n              json_array_t  *jtargets;\n              json_t        *jtarget, *jattrib;\n              AkMorphTarget *target;\n              AkMorphable   *morphable;\n              uint32_t       targetIdx;\n\n              if (!(jtargets = json_array(jprimVal)))\n                goto prmv_nxt;\n\n              /* Lazy-init the mesh-level morph and pre-allocate one\n                 AkMorphTarget per blend shape. All primitives of the mesh\n                 share these targets — each adds one AkMorphable to each\n                 target's chain. (glTF spec: every primitive in the mesh\n                 has the same number of morph targets in the same order.) */\n              if (!meshMorph) {\n                AkMorphTarget *prev;\n                uint32_t       i;\n\n                meshMorph         = ak_heap_calloc(heap, doc, sizeof(*meshMorph));\n                meshMorph->method = AK_MORPH_METHOD_ADDITIVE;\n                meshTargetCnt     = (uint32_t)jtargets->count;\n                meshTargetArr     = ak_heap_calloc(heap, meshMorph,\n                                                   sizeof(*meshTargetArr) * meshTargetCnt);\n\n                prev = NULL;\n                for (i = 0; i < meshTargetCnt; i++) {\n                  target           = ak_heap_calloc(heap, meshMorph, sizeof(*target));\n                  meshTargetArr[i] = target;\n\n                  if (prev) prev->next      = target;\n                  else      meshMorph->target = target;\n                  prev = target;\n                  meshMorph->targetCount++;\n                }\n              }\n\n              /* For this primitive, prepend one AkMorphable to each blend\n                 shape's chain (one per target). The first primitive's\n                 morphable is wrapped in an AkObject (carries the type tag\n                 used by intr.c switch dispatch); subsequent primitives'\n                 morphables are plain calloc'd and linked via .next.\n\n                 The JSON parser walks the targets array in reverse source\n                 order (an artifact of the reader's reverse-prepend\n                 strategy). The pre-allocated meshTargetArr[] is in chain\n                 (forward) order, so we mirror the index when filling: the\n                 first jtarget we visit (source LAST) populates the last\n                 chain slot, and the last jtarget visited (source FIRST)\n                 populates chain[0] — i.e. meshMorph->target ends up =\n                 source targets[0], matching glTF semantics where weight[i]\n                 drives mesh.primitives[].targets[i]. Without this swap,\n                 weight[0] animates the wrong blend shape and the morph\n                 visually plays in reverse target order.\n\n                 Primitive nodes below are linked with head-prepend. Prepending\n                 morphables here keeps each target's morphable chain in the\n                 same order as mesh->primitive, so inspect can walk both chains\n                 lock-step for multi-primitive morphs. */\n              jtarget   = jtargets->base.value;\n              targetIdx = 0;\n\n              while (jtarget && targetIdx < meshTargetCnt) {\n                target = meshTargetArr[meshTargetCnt - 1 - targetIdx];\n\n                if (!target->target) {\n                  AkObject *targetObj;\n                  targetObj      = ak_objAlloc(heap, target, sizeof(*morphable),\n                                               AK_MORPHABLE_MORPHABLE, true);\n                  morphable      = ak_objGet(targetObj);\n                  target->target = targetObj;\n                } else {\n                  AkMorphable *head, *oldHead;\n\n                  head    = ak_objGet(target->target);\n                  oldHead = ak_heap_calloc(heap, target, sizeof(*oldHead));\n                  memcpy(oldHead, head, sizeof(*oldHead));\n\n                  memset(head, 0, sizeof(*head));\n                  head->next = oldHead;\n                  morphable  = head;\n                }\n                target->primitiveCount++;\n\n                /* fill morphable->input from the target's attribute deltas.\n                   IMPORTANT: do NOT touch prim->pos here — that's the base\n                   primitive's POSITION binding; target POSITION is a delta. */\n                jattrib = jtarget->value;\n                while (jattrib) {\n                  AkInput    *inp;\n\n                  inp      = ak_heap_calloc(heap, prim, sizeof(*inp));\n                  gltf_inputSemantic(heap, inp, jattrib);\n\n                  inp->semantic = gltf_enumInputSemantic(inp->semanticRaw);\n                  inp->accessor = flist_sp_at(&doc->lib.accessors,\n                                              json_int32(jattrib, -1));\n                  if (!inp->accessor) {\n                    jattrib = jattrib->next;\n                    continue;\n                  }\n\n                  inp->next        = morphable->input;\n                  morphable->input = inp;\n                  morphable->inputCount++;\n\n                  jattrib = jattrib->next;\n                } /* jattrib */\n\n                jtarget = jtarget->next;\n                targetIdx++;\n              } /* jtarget */\n            } else if (json_key_eq(jprimVal, _s_gltf_extensions)) {\n              if (!gltf_ext_dracoPrimitive(gst, prim, jprim)) {\n                gst->stop = true;\n                return;\n              }\n              if (!gltf_ext_primitiveVariants(gst, prim, jprim)\n                  || !gltf_ext_primitiveGaussianSplat(gst, prim, jprim)) {\n                gst->stop = true;\n                return;\n              }\n            }\n\n          prmv_nxt:\n            jprimVal = jprimVal->next;\n          }\n\n          prim->next      = mesh->primitive;\n          mesh->primitive = prim;\n          mesh->primitiveCount++;\n\n          prim->nPolygons = gltf_polyCount(prim, mode);\n\n          if (!prim->material) {\n            prim->material = gst->defaultMaterial;\n          }\n        prim_next:\n          jprim = jprim->next;\n        }\n      } else if (json_key_eq(jmeshVal, _s_gltf_weights)) {\n        AkFloatArray *weights;\n        json_array_t *jarr;\n\n        if ((jarr = json_array(jmeshVal))) {\n          weights = ak_heap_alloc(heap,\n                                  meshObj,\n                                  sizeof(*weights)\n                                   + sizeof(float) * jarr->count);\n          /* Pass jmeshVal (the array NODE), not jmeshVal->value (its first\n             child). json_array_float internally casts arg via json_array(). */\n          json_array_float(weights->items,\n                           jmeshVal,\n                           0.0f,\n                           jarr->count,\n                           true);\n\n          weights->count = jarr->count;\n          mesh->weights  = weights;\n        }\n      } else if (json_key_eq(jmeshVal, _s_gltf_name)) {\n        mesh->name = json_strdup(jmeshVal, heap, meshObj);\n      } else if (json_key_eq(jmeshVal, _s_gltf_extras)) {\n        json_t       *jnames;\n        json_t       *jpresets;\n        json_array_t *jarr;\n\n        if ((jnames = json_get(jmeshVal, _s_gltf_targetNames))\n            && (jarr = json_array(jnames))\n            && jarr->count > 0) {\n          json_t   *jname;\n          uint32_t  i;\n\n          morphTargetNamesN = (uint32_t)jarr->count;\n          morphTargetNames  = ak_heap_calloc(heap, meshObj,\n                                             sizeof(const char *) * morphTargetNamesN);\n\n          jname = jarr->base.value;\n          i     = 0;\n          while (jname && i < morphTargetNamesN) {\n            morphTargetNames[morphTargetNamesN - 1 - i] =\n              json_strdup(jname, heap, meshObj);\n            i++;\n            jname = jname->next;\n          }\n        }\n\n        if ((jpresets = json_get(jmeshVal, _s_gltf_morphPresets))) {\n          morphPresets = gltf_meshMorphPresets(gst,\n                                               meshObj,\n                                               jpresets,\n                                               &morphPresetCount);\n        }\n      }\n\n      jmeshVal = jmeshVal->next;\n    }\n\n    /* Register the mesh-level morph once all keys are processed, so that\n       extras.targetNames (which can come after primitives in JSON order) is\n       attached as well. Keyed by geometry so node.c finds it via meshTargets. */\n    if (meshMorph) {\n      if (morphTargetNames && morphTargetNamesN > 0) {\n        meshMorph->targetNames = morphTargetNames;\n      }\n      if (morphPresets && morphPresetCount > 0) {\n        morphPresetWrite = 0;\n        for (morphPresetIdx = 0;\n             morphPresetIdx < morphPresetCount;\n             morphPresetIdx++) {\n          if (!morphPresets[morphPresetIdx].weights\n              || morphPresets[morphPresetIdx].weights->count != meshMorph->targetCount)\n            continue;\n          if (morphPresetWrite != morphPresetIdx)\n            morphPresets[morphPresetWrite] = morphPresets[morphPresetIdx];\n          morphPresetWrite++;\n        }\n\n        if (morphPresetWrite > 0) {\n          meshMorph->presets    = morphPresets;\n          meshMorph->presetCount = morphPresetWrite;\n        }\n      }\n      if (doc->lib.morphs)\n        meshMorph->base.next = &doc->lib.morphs->base;\n      doc->lib.morphs = meshMorph;\n      rb_insert(gst->meshTargets, geom, meshMorph);\n    }\n\n    /* Reversed */\n    geom->base.next = lib->chld;\n    lib->chld       = (void *)geom;\n\n    lib->count++;\n\n    jmesh = jmesh->next;\n  }\n\n  doc->lib.geometries = lib;\n}\n\nAK_HIDE\nAkMeshPrimitive*\ngltf_allocPrim(AkHeap * __restrict heap,\n               void   * __restrict memParent,\n               int                 mode) {\n  switch (mode) {\n    case 0: {\n      AkMeshPrimitive *prim;\n      prim       = ak_heap_calloc(heap, memParent, sizeof(*prim));\n      prim->type = AK_PRIMITIVE_POINTS;\n      return prim;\n    }\n    case 1: {\n      AkLines *lines;\n      lines            = ak_heap_calloc(heap, memParent, sizeof(*lines));\n      lines->base.type = AK_PRIMITIVE_LINES;\n      lines->mode      = AK_LINES;\n      return &lines->base;\n    }\n    case 2: {\n      AkLines *lines;\n      lines            = ak_heap_calloc(heap, memParent, sizeof(*lines));\n      lines->base.type = AK_PRIMITIVE_LINES;\n      lines->mode      = AK_LINE_LOOP;\n      return &lines->base;\n    }\n    case 3: {\n      AkLines *lines;\n      lines            = ak_heap_calloc(heap, memParent, sizeof(*lines));\n      lines->base.type = AK_PRIMITIVE_LINES;\n      lines->mode      = AK_LINE_STRIP;\n      return &lines->base;\n    }\n    case 4: {\n      AkTriangles *tri;\n      tri            = ak_heap_calloc(heap, memParent, sizeof(*tri));\n      tri->base.type = AK_PRIMITIVE_TRIANGLES;\n      tri->mode      = AK_TRIANGLES;\n      return &tri->base;\n    }\n    case 5: {\n      AkTriangles *tri;\n      tri            = ak_heap_calloc(heap, memParent, sizeof(*tri));\n      tri->base.type = AK_PRIMITIVE_TRIANGLES;\n      tri->mode      = AK_TRIANGLE_STRIP;\n      return &tri->base;\n    }\n    case 6: {\n      AkTriangles *tri;\n      tri            = ak_heap_calloc(heap, memParent, sizeof(*tri));\n      tri->base.type = AK_PRIMITIVE_TRIANGLES;\n      tri->mode      = AK_TRIANGLE_FAN;\n      return &tri->base;\n    }\n  }\n\n  return NULL;\n}\n\nAK_HIDE\nuint32_t\ngltf_polyCount(AkMeshPrimitive *prim, uint32_t mode) {\n  AkInput    *pos;\n  AkAccessor *acc;\n  uint32_t    n;\n\n  if (prim->indices) {\n    n = (uint32_t)prim->indices->count;\n  } else if ((pos = prim->pos) && (acc = pos->accessor)) {\n    n = (uint32_t)acc->count;\n  } else {\n    goto err;\n  }\n\n  switch (mode) {\n    /* 0: points, 2: line loops */\n    case 0:\n    case 2: return n;\n    /* 1: lines */\n    case 1: return n * 0.5;\n    /* 3: line strip */\n    case 3: return n - 1;\n    /* 4: triangles */\n    case 4: return n / 3;\n    /* 5: triangle strip, 6: triangle fan */\n    case 5:\n    case 6: return n - 2;\n  }\n\nerr:\n  return 0;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/mesh.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_mesh_h\n#define gltf_imp_core_mesh_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_meshes(json_t * __restrict json,\n            void   * __restrict userdata);\n\nAK_HIDE\nAkMeshPrimitive*\ngltf_allocPrim(AkHeap * __restrict heap,\n               void   * __restrict memParent,\n               int                 mode);\n\nAK_HIDE\nuint32_t\ngltf_polyCount(AkMeshPrimitive *prim, uint32_t mode);\n\n#endif /* gltf_imp_core_mesh_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/node.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"node.h\"\n#include \"ext.h\"\n#include \"../extra.h\"\n#include \"../../../../id.h\"\n\n#include <ds/hash.h>\n#include <string.h>\n\n#define k_name          0\n#define k_camera        1\n#define k_mesh          2\n#define k_skin          3\n#define k_children      4\n#define k_matrix        5\n#define k_translation   6\n#define k_rotation      7\n#define k_scale         8\n#define k_weights       9\n#define k_extensions    10\n#define k_extras        11\n\nAK_HIDE\nvoid\ngltf_nodes(json_t * __restrict jnode,\n           void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  AkHeap             *heap;\n  AkDoc              *doc;\n  AkLibrary          *lib;\n  AkNode             *node;\n  const json_array_t *jnodes;\n  FListItem          *nodes;\n  AkNode            **nodechld, *parentNode;\n  char                nodeid[32];\n  int                 i, jnodeCount2;\n\n  if (!(jnodes = json_array(jnode)))\n    return;\n\n  gst       = userdata;\n  heap      = gst->heap;\n  doc       = gst->doc;\n  lib       = ak_heap_calloc(heap, doc, sizeof(*lib));\n  nodechld  = ak_calloc(NULL, sizeof(*nodechld) * jnodes->count * 2);\n  nodes     = NULL;\n\n  jnode       = jnodes->base.value;\n  i           = jnodes->count - 1;\n  jnodeCount2 = jnodes->count * 2;\n  \n  while (jnode) {\n    nodechld[i * 2] = node = gltf_node(gst, lib, jnode, nodechld);\n  \n    /* JSON parse is reverse */\n    sprintf(nodeid, \"%s%d\", _s_gltf_node, i);\n    ak_heap_setId(heap, ak__alignof(node), ak_heap_strdup(heap, node, nodeid));\n\n    i--;\n    jnode = jnode->next;\n  }\n  \n  for (i = jnodeCount2 - 2; i >= 0; i -= 2) {\n    node = nodechld[i];\n    \n    /* this node has parent node, move this into parent children link. */\n    if ((parentNode = nodechld[i + 1])) {\n      AkNode *chld;\n      chld = parentNode->chld;\n      if (chld) {\n        chld->prev = node;\n        node->next = chld;\n      }\n      \n      parentNode->chld = node;\n      node->parent     = parentNode;\n      \n      /* Keep heap ownership in the node library; parent is logical scene graph. */\n    }\n    \n    /* it is root node, add to library_nodes */\n    else {\n      node->next = (void *)lib->chld;\n      lib->chld  = (void *)node;\n\n      lib->count++;\n    }\n  }\n\n  flist_sp_destroy(&nodes);\n  ak_free(nodechld);\n\n  doc->lib.nodes = lib;\n}\n\nAK_HIDE\nAkNode*\ngltf_node(AkGLTFState * __restrict gst,\n          void        * __restrict memParent,\n          json_t      * __restrict jnode,\n          AkNode     ** __restrict nodechld) {\n  AkHeap             *heap;\n  AkNode             *node;\n  AkGeometry         *geomIter;\n  AkInstanceGeometry *instGeom;\n  void               *it;\n  AkMorph            *morph;\n  int32_t             i32val;\n\n  heap     = gst->heap;\n  geomIter = NULL;\n  instGeom = NULL;\n\n  node = ak_heap_calloc(heap, memParent, sizeof(*node));\n  ak_setypeid(node, AKT_NODE);\n  node->visible = true;\n\n  json_objmap_t nodeMap[] = {\n    JSON_OBJMAP_OBJ(_s_gltf_name,        I2P k_name),\n    JSON_OBJMAP_OBJ(_s_gltf_camera,      I2P k_camera),\n    JSON_OBJMAP_OBJ(_s_gltf_mesh,        I2P k_mesh),\n    JSON_OBJMAP_OBJ(_s_gltf_skin,        I2P k_skin),\n    JSON_OBJMAP_OBJ(_s_gltf_children,    I2P k_children),\n    JSON_OBJMAP_OBJ(_s_gltf_matrix,      I2P k_matrix),\n    JSON_OBJMAP_OBJ(_s_gltf_translation, I2P k_translation),\n    JSON_OBJMAP_OBJ(_s_gltf_rotation,    I2P k_rotation),\n    JSON_OBJMAP_OBJ(_s_gltf_scale,       I2P k_scale),\n    JSON_OBJMAP_OBJ(_s_gltf_weights,     I2P k_weights),\n    JSON_OBJMAP_OBJ(_s_gltf_extensions,  I2P k_extensions),\n    JSON_OBJMAP_OBJ(_s_gltf_extras,      I2P k_extras)\n  };\n\n  json_objmap(jnode, nodeMap, JSON_ARR_LEN(nodeMap));\n\n  if ((it = nodeMap[k_name].object)) {\n    node->name = json_strdup(it, heap, node);\n  }\n\n  gltf_extra(gst,\n             node,\n             nodeMap[k_extras].object,\n             nodeMap[k_extensions].object);\n\n  if ((it = nodeMap[k_extensions].object)\n      && !gltf_ext_node(gst, node, it)) {\n    gst->stop = true;\n    return node;\n  }\n\n  if (gst->doc->lib.cameras\n      && (i32val = json_int32(nodeMap[k_camera].object, -1)) > -1) {\n    AkCamera *camIter;\n\n    GETCHILD(gst->doc->lib.cameras->chld, camIter, i32val);\n\n    if (camIter) {\n      AkInstanceBase *instCamera;\n      instCamera          = ak_heap_calloc(heap, node, sizeof(*instCamera));\n      instCamera->node    = node;\n      instCamera->type    = AK_INSTANCE_CAMERA;\n      instCamera->url.ptr = camIter;\n\n      node->camera = instCamera;\n    }\n  }\n\n  /* instance geometries */\n  if ((i32val = json_int32(nodeMap[k_mesh].object, -1)) > -1) {\n    GETCHILD(gst->doc->lib.geometries->chld, geomIter, i32val);\n\n    /* instance geometry */\n    if (geomIter) {\n      instGeom               = ak_heap_calloc(heap, node, sizeof(*instGeom));\n      instGeom->base.node    = node;\n      instGeom->base.type    = AK_INSTANCE_GEOMETRY;\n      instGeom->base.url.ptr = geomIter;\n\n      node->geometry         = instGeom;\n    } /* if (geomIter) */\n  }\n\n  /* children */\n  if ((it = nodeMap[k_children].object)) {\n    json_array_t *jchildren;\n    json_t       *jchld;\n    int           chldIndex;\n\n    if ((jchildren = json_array(it))) {\n      jchld = jchildren->base.value;\n\n      while (jchld) {\n        if ((chldIndex = json_int32(jchld, -1)) > -1) {\n          chldIndex = chldIndex * 2 + 1;\n\n          if (!nodechld[chldIndex]) {\n            nodechld[chldIndex] = node;\n          }\n\n          /* else:\n              this node is already child of another,\n              it cannot be child of two node at same time\n           */\n        }\n\n        jchld = jchld->next;\n      }\n    } /* if children */\n  }\n\n  /* first parsed is added to the end so TRS. */\n\n  /* matrix */\n  if ((it = nodeMap[k_matrix].object)) {\n    AkObject *obj;\n    AkMatrix *matrix;\n\n    obj    = ak_objAlloc(heap, node, sizeof(*matrix), AKT_MATRIX, true);\n    matrix = ak_objGet(obj);\n\n    json_array_float(matrix->val[0], it, 0.0f, 16, true);\n\n    if (!node->transform) {\n      node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform));\n    }\n    \n    obj->next             = node->transform->item;\n    node->transform->item = obj;\n  }\n  \n  /* scale */\n  if ((it = nodeMap[k_scale].object)) {\n    AkObject *obj;\n    AkScale  *scale;\n\n    obj   = ak_objAlloc(heap, node, sizeof(*obj), AKT_SCALE, true);\n    scale = ak_objGet(obj);\n\n    json_array_float(scale->val, it, 0.0f, 3, true);\n\n    if (!node->transform) {\n      node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform));\n    }\n    \n    obj->next             = node->transform->item;\n    node->transform->item = obj;\n  }\n\n  /* rotation */\n  if ((it = nodeMap[k_rotation].object)) {\n    AkObject     *obj;\n    AkQuaternion *rot;\n\n    obj = ak_objAlloc(heap, node, sizeof(*obj), AKT_QUATERNION, true);\n    rot = ak_objGet(obj);\n\n    json_array_float(rot->val, it, 0.0f, 4, true);\n\n    if (!node->transform) {\n      node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform));\n    }\n    \n    obj->next             = node->transform->item;\n    node->transform->item = obj;\n  }\n\n  /* translation */\n  if ((it = nodeMap[k_translation].object)) {\n    AkObject    *obj;\n    AkTranslate *translate;\n\n    obj = ak_objAlloc(heap, node, sizeof(*translate), AKT_TRANSLATE, true);\n    translate = ak_objGet(obj);\n\n    json_array_float(translate->val, it, 0.0f, 3, true);\n\n    if (!node->transform) {\n      node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform));\n    }\n    \n    obj->next             = node->transform->item;\n    node->transform->item = obj;\n  }\n  \n  /* morph instance + (optional) node-level weight override */\n  if (geomIter && instGeom && (morph = rb_find(gst->meshTargets, geomIter))) {\n    AkInstanceMorph *morpher;\n\n    morpher = ak_heap_calloc(heap, node, sizeof(*morpher));\n    ak_setypeid(morpher, AKT_MORPH_INST);\n\n    morpher->morph    = morph;\n    instGeom->morpher = morpher;\n\n    /* overrideWeights is set ONLY when the glTF node carries an explicit\n       \"weights\" property. Otherwise it stays NULL so that:\n         - ak_morphHasOverride()     returns false\n         - defaults flow through:    morph.defaultWeights → mesh.weights → 0\n       Allocating a zero-filled array unconditionally would silently override\n       any defaults — see ak_morphInspect_initialWeight precedence. */\n    if ((it = json_array(nodeMap[k_weights].object))) {\n      AkFloatArray *weights;\n      json_array_t *jsonArr;\n\n      jsonArr = it;\n      weights = ak_heap_calloc(heap,\n                               morpher,\n                               sizeof(*weights)\n                               + sizeof(weights->items[0]) * morph->targetCount);\n      json_array_float(weights->items, it, 0.0f, jsonArr->count, true);\n      weights->count           = morph->targetCount;\n      morpher->overrideWeights = weights;\n    }\n  }\n  \n  /* bind skinnerr after skin is loaded */\n  if (instGeom && (i32val = json_int32(nodeMap[k_skin].object, -1)) > -1) {\n\n    rb_insert(gst->skinBound, node, I2P i32val);\n\n    /* TODO: what if there is no Geomerty? */\n  }\n\n  return node;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/node.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_node_h\n#define gltf_imp_core_node_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_nodes(json_t * __restrict jnode,\n           void   * __restrict userdata);\n\nAK_HIDE\nAkNode*\ngltf_node(AkGLTFState * __restrict gst,\n          void        * __restrict memParent,\n          json_t      * __restrict jnode,\n          AkNode     ** __restrict nodechld);\n\nAK_HIDE\nvoid\ngltf_bindMaterials(AkGLTFState        * __restrict gst,\n                   AkInstanceGeometry * __restrict instGeom,\n                   int32_t                         meshIndex);\n\n#endif /* gltf_imp_core_node_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/profile.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"profile.h\"\n\nAK_HIDE\nAkProfileCommon*\ngltf_cmnEffect(AkGLTFState * __restrict gst) {\n  AkLibrary       *lib;\n  AkEffect        *effect;\n  AkProfileCommon *profile;\n\n  if (!(lib = gst->doc->lib.effects)) {\n    lib = ak_heap_calloc(gst->heap, gst->doc, sizeof(*lib));\n    gst->doc->lib.effects = lib;\n  }\n\n  effect  = ak_heap_calloc(gst->heap, lib,      sizeof(*effect));\n  profile = ak_heap_calloc(gst->heap, effect,   sizeof(*profile));\n  profile->type = AK_PROFILE_TYPE_COMMON;\n\n  lib->count++;\n\n  effect->profile = profile;\n  effect->next    = (void *)lib->chld;\n  lib->chld       = (void *)effect;\n\n  ak_setypeid(profile, AKT_PROFILE);\n  ak_setypeid(effect,  AKT_EFFECT);\n\n  return effect->profile;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/profile.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_profile_h\n#define gltf_imp_core_profile_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkProfileCommon*\ngltf_cmnEffect(AkGLTFState * __restrict gst);\n\n#endif /* gltf_imp_core_profile_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/sampler.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"sampler.h\"\n#include \"profile.h\"\n#include \"enum.h\"\n#include \"../extra.h\"\n\nAK_HIDE\nvoid\ngltf_samplers(json_t * __restrict jsampler,\n              void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  const json_array_t *jsamplers;\n  const json_t       *jsamplerVal;\n  AkSampler          *sampler;\n\n  if (!(jsamplers = json_array(jsampler)))\n    return;\n\n  gst = userdata;\n\n  jsampler = jsamplers->base.value;\n  while (jsampler) {\n    jsamplerVal    = jsampler->value;\n    sampler        = ak_heap_calloc(gst->heap, gst->doc, sizeof(*sampler));\n    sampler->wrapS = AK_WRAP_MODE_WRAP;\n    sampler->wrapT = AK_WRAP_MODE_WRAP;\n\n    ak_setypeid(sampler, AKT_SAMPLER2D);\n    gltf_extra(gst,\n               sampler,\n               json_get(jsampler, _s_gltf_extras),\n               json_get(jsampler, _s_gltf_extensions));\n    \n    while (jsamplerVal) {\n      if (json_key_eq(jsamplerVal, _s_gltf_wrapS)) {\n        sampler->wrapS = gltf_wrapMode(json_int32(jsamplerVal,\n                                                  AK_WRAP_MODE_WRAP));\n      } else if (json_key_eq(jsamplerVal, _s_gltf_wrapT)) {\n        sampler->wrapT = gltf_wrapMode(json_int32(jsamplerVal,\n                                                  AK_WRAP_MODE_WRAP));\n      } else if (json_key_eq(jsamplerVal, _s_gltf_minFilter)) {\n        sampler->minfilter = gltf_minFilter(json_int32(jsamplerVal, 0));\n      } else if (json_key_eq(jsamplerVal, _s_gltf_magFilter)) {\n        sampler->magfilter = gltf_magFilter(json_int32(jsamplerVal, 0));\n      } else if (json_key_eq(jsamplerVal, _s_gltf_name)) {\n        sampler->name = json_strdup(jsamplerVal, gst->heap, sampler);\n      }\n\n      jsamplerVal = jsamplerVal->next;\n    }\n\n    flist_sp_insert(&gst->doc->lib.samplers, sampler);\n    jsampler = jsampler->next;\n  }\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/sampler.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_sampler_h\n#define gltf_imp_core_sampler_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_samplers(json_t * __restrict jsampler,\n              void   * __restrict userdata);\n\n#endif /* gltf_imp_core_sampler_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/scene.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"scene.h\"\n#include \"../extra.h\"\n\nstatic\nvoid\ngltf_setFirstCamera(AkVisualScene *scene, AkNode *node);\n\nstatic AkNode *\ngltf_findNodeById(AkNode *node, const char *nodeid);\n\nstatic AkNode *\ngltf_nodeByIndex(AkDoc *doc, int32_t nodeIndex);\n\nAK_HIDE\nvoid\ngltf_scenes(json_t * __restrict jscene,\n            void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  AkHeap             *heap;\n  AkDoc              *doc;\n  const json_array_t *jscenes;\n  AkLibrary          *lib;\n\n  if (!(jscenes = json_array(jscene)))\n    return;\n\n  gst    = userdata;\n  heap   = gst->heap;\n  doc    = gst->doc;\n  jscene = jscenes->base.value;\n  lib    = ak_heap_calloc(heap, doc, sizeof(*lib));\n  \n  while (jscene) {\n    AkVisualScene *scene;\n    json_t        *jsceneVal;\n    \n    jsceneVal = jscene->value;\n    scene     = ak_heap_calloc(heap, lib, sizeof(*scene));\n    ak_setypeid(scene, AKT_SCENE);\n    gltf_extra(gst,\n               scene,\n               json_get(jscene, _s_gltf_extras),\n               json_get(jscene, _s_gltf_extensions));\n    \n    scene->cameras = ak_heap_calloc(heap, scene, sizeof(*scene->cameras));\n    \n    /* root node: to store node instances */\n    scene->node = ak_heap_calloc(heap, scene, sizeof(*scene->node));\n    scene->node->visible = true;\n    \n    while (jsceneVal) {\n      if (json_key_eq(jsceneVal, _s_gltf_name)) {\n        scene->name = json_strdup(jsceneVal, heap, scene);\n      } else if (json_key_eq(jsceneVal, _s_gltf_nodes)) {\n        json_array_t *jnodes;\n        json_t       *jnode;\n        int32_t       nodeIndex;\n        \n        if (!(jnodes = json_array(jsceneVal)))\n          goto scn_nxt;\n        \n        /* create instanceNode for each node */\n        jnode = jnodes->base.value;\n        \n        while (jnode) {\n          AkNode         *node;\n          AkInstanceNode *instNode;\n          \n          instNode  = ak_heap_calloc(heap, scene, sizeof(*instNode));\n          if ((nodeIndex = json_int32(jnode, -1)) < 0)\n            goto jnode_nxt;\n          \n          if (!(node = gltf_nodeByIndex(doc, nodeIndex)))\n            goto jnode_nxt;\n          \n          instNode->base.node    = node;\n          instNode->base.url.ptr = node;\n          instNode->base.type    = AK_INSTANCE_NODE;\n          \n          if (scene->node) {\n            if (scene->node->node)\n              instNode->base.next = &scene->node->node->base;\n            scene->node->node   = instNode;\n          }\n          \n          if (!scene->firstCamNode)\n            gltf_setFirstCamera(scene, node);\n          \n        jnode_nxt:\n          jnode = jnode->next;\n        }\n      }\n      jsceneVal = jsceneVal->next;\n    }\n\n  scn_nxt:\n\n    scene->base.next = lib->chld;\n    lib->chld        = (void *)scene;\n    lib->count++;\n    \n    jscene = jscene->next;\n  }\n  \n   doc->lib.visualScenes = lib;\n}\n\nAK_HIDE\nvoid\ngltf_scene(json_t * __restrict jscene,\n           void   * __restrict userdata) {\n  AkGLTFState   *gst;\n  AkHeap        *heap;\n  AkDoc         *doc;\n  AkVisualScene *scene;\n  int32_t        sceneIndex;\n\n  gst  = userdata;\n  heap = gst->heap;\n  doc  = gst->doc;\n  \n  /* set default scene */\n  sceneIndex = json_int32(jscene, -1);\n  GETCHILD(doc->lib.visualScenes->chld, scene, sceneIndex);\n\n  /* set first scene as default scene if not specified  */\n  if (scene) {\n    AkInstanceBase *instScene;\n    instScene = ak_heap_calloc(heap, doc, sizeof(*instScene));\n    \n    instScene->url.ptr     = scene;\n    doc->scene.visualScene = instScene;\n  }\n}\n\nstatic AkNode *\ngltf_findNodeById(AkNode *node, const char *nodeid) {\n  const char *id;\n  AkNode *found;\n\n  while (node) {\n    id = (const char *)ak_mem_getId(node);\n    if (id && strcmp(id, nodeid) == 0)\n      return node;\n\n    if (node->chld && (found = gltf_findNodeById(node->chld, nodeid)))\n      return found;\n\n    node = node->next;\n  }\n\n  return NULL;\n}\n\nstatic AkNode *\ngltf_nodeByIndex(AkDoc *doc, int32_t nodeIndex) {\n  char nodeid[16];\n\n  if (!doc || !doc->lib.nodes || nodeIndex < 0)\n    return NULL;\n\n  sprintf(nodeid, \"%s%d\", _s_gltf_node, nodeIndex);\n  return gltf_findNodeById((AkNode *)doc->lib.nodes->chld, nodeid);\n}\n\nstatic\nvoid\ngltf_setFirstCamera(AkVisualScene *scene, AkNode *node) {\n  if (node->camera) {\n    if (!scene->firstCamNode)\n      scene->firstCamNode = node;\n\n    ak_instanceListAdd(scene->cameras, node->camera);\n    return;\n  }\n\n  if (node->chld) {\n    AkNode *nodei;\n    nodei = node->chld;\n    while (nodei) {\n      gltf_setFirstCamera(scene, nodei);\n      nodei = nodei->next;\n    }\n  }\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/scene.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_scene_h\n#define gltf_imp_core_scene_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_scenes(json_t * __restrict jscene,\n            void   * __restrict userdata);\n\nAK_HIDE\nvoid\ngltf_scene(json_t * __restrict jscene,\n           void   * __restrict userdata);\n\n#endif /* gltf_imp_core_scene_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/skin.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"skin.h\"\n#include \"accessor.h\"\n\nAK_HIDE\nvoid\ngltf_skin(json_t * __restrict jskin,\n          void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  AkHeap             *heap;\n  AkDoc              *doc;\n  const json_array_t *jskins;\n  uint32_t            skinIndex;\n\n  if (!(jskins = json_array(jskin)))\n    return;\n\n  gst       = userdata;\n  heap      = gst->heap;\n  doc       = gst->doc;\n  jskin     = jskins->base.value;\n  skinIndex = jskins->count - 1;\n\n  while (jskin) {\n    AkSkin *skin;\n    json_t *jskinVal;\n    char    skinid[16];\n    \n    jskinVal = jskin->value;\n    skin     = ak_heap_calloc(heap, doc, sizeof(*skin));\n\n    sprintf(skinid, \"%s%d\", _s_gltf_skin, skinIndex);\n    ak_heap_setId(heap,\n                  ak__alignof(skin),\n                  ak_heap_strdup(heap, skin, (void *)skinid));\n\n    glm_mat4_identity(skin->bindShapeMatrix);\n\n    while (jskinVal) {\n      if (json_key_eq(jskinVal, _s_gltf_inverseBindMatrices)) {\n        AkAccessor *acc;\n        AkBuffer   *buff;\n        float      *pbuff;\n        \n        if ((acc = flist_sp_at(&doc->lib.accessors, json_int32(jskinVal, -1)))\n            && (buff = acc->buffer)) {\n          pbuff = (void *)((char *)buff->data + acc->byteOffset);\n          skin->invBindPoses = ak_heap_alloc(heap, skin,\n                                             acc->count * sizeof(mat4));\n          \n          for (size_t k = 0; k < acc->count * 16; k++) {\n            *((float *)skin->invBindPoses + k) = *(pbuff + k);\n          }\n        }\n      } else if (json_key_eq(jskinVal, _s_gltf_joints)) {\n        json_array_t *jjoints;\n        json_t       *jjoint;\n        uint32_t      j;\n        \n        if ((jjoints = json_array(jskinVal))) {\n          skin->joints = ak_heap_alloc(heap,\n                                       skin,\n                                       sizeof(void **) * jjoints->count);\n\n          jjoint = jjoints->base.value;\n          j      = jjoints->count - 1; /* json parser is reverse */\n          while (jjoint) {\n            char     nodeid[16];\n            AkNode  *node;\n            int32_t  nodeIndex;\n\n            if ((nodeIndex = json_int32(jjoint, -1)) > -1) {\n              sprintf(nodeid, \"%s%d\", _s_gltf_node, nodeIndex);\n\n              if ((node = ak_getObjectById(doc, nodeid))) {\n                skin->joints[j] = node;\n              } else {\n                skin->joints[j] = NULL;\n              }\n            } else {\n              skin->joints[j] = NULL;\n            }\n\n            j--;\n            skin->nJoints++;\n            jjoint = jjoint->next;\n          } /* while (jjoint) */\n        }\n      } else if (json_key_eq(jskinVal, _s_gltf_skeleton)) {\n        /* glTF spec: optional hint to the closest common ancestor of the\n           joints. Engines (Apple SCNSkinner.skeleton, Three.js Skeleton)\n           use it as the coordinate-space reference. We resolve the node\n           id to AkNode* — fall back to NULL if missing or unresolved\n           (callers default to joints[0]). */\n        char    skelid[16];\n        int32_t skelIndex;\n\n        if ((skelIndex = json_int32(jskinVal, -1)) > -1) {\n          sprintf(skelid, \"%s%d\", _s_gltf_node, skelIndex);\n          skin->skeleton = ak_getObjectById(doc, skelid);\n        }\n      } /* if _s_gltf_joints */\n\n      jskinVal = jskinVal->next;\n    }\n    \n    if (doc->lib.skins)\n      skin->base.next = &doc->lib.skins->base;\n    \n    doc->lib.skins = skin;\n\n    skinIndex--;\n    jskin = jskin->next;\n  }\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/skin.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_skin_h\n#define gltf_imp_core_skin_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_skin(json_t * __restrict jskin,\n          void   * __restrict userdata);\n\n#endif /* gltf_imp_core_skin_h */\n"
  },
  {
    "path": "src/io/gltf/imp/core/texture.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"texture.h\"\n#include \"ext.h\"\n#include \"profile.h\"\n#include \"sampler.h\"\n#include \"../extra.h\"\n\nAK_INLINE\nchar*\ncoordInputName(AkHeap * __restrict heap,\n               void   * __restrict parent,\n               int                 set) {\n  char  *coordInputName;\n  size_t len;\n\n  if (set == 0) {\n    coordInputName = ak_heap_strdup(heap, parent, _s_gltf_texcoordPrefix);\n  } else {\n    len                 = strlen(_s_gltf_texcoordPrefix) + ak_digitsize(set);\n    coordInputName      = ak_heap_alloc(heap, parent, len + 1);\n    coordInputName[len] = '\\0';\n    /* sprintf(coordInputName, \"%s%d\", _s_gltf_texcoordPrefix, set); */\n    snprintf(coordInputName, len + 1, \"%s%d\", _s_gltf_texcoordPrefix, set);\n  }\n\n  return coordInputName;\n}\n\nAK_HIDE\nAkTextureRef*\ngltf_texref(AkGLTFState * __restrict gst,\n            void        * __restrict parent,\n            json_t      * __restrict jtexinfo) {\n  AkHeap       *heap;\n  AkDoc        *doc;\n  AkTextureRef *texref;\n  AkTexture    *tex;\n  json_t       *jext;\n  int32_t       texindex, set;\n\n  heap     = gst->heap;\n  doc      = gst->doc;\n  texindex = json_int32(json_get(jtexinfo, _s_gltf_index), 0);\n  set      = json_int32(json_get(jtexinfo, _s_gltf_texCoord), 0);\n  tex      = flist_sp_at(&doc->lib.textures, texindex);\n  \n  texref = ak_heap_calloc(heap, parent, sizeof(*texref));\n  ak_setypeid(texref, AKT_TEXTURE_REF);\n  gltf_extra(gst,\n             texref,\n             json_get(jtexinfo, _s_gltf_extras),\n             json_get(jtexinfo, _s_gltf_extensions));\n\n  texref->coordInputName = coordInputName(heap, texref, set);\n\n  if ((jext = json_get(jtexinfo, _s_gltf_extensions))) {\n    json_t *jval;\n    if ((jval = json_get(jext, _s_gltf_KHR_texture_transform))) {\n      AkTextureTransform *texTransf;\n\n      texTransf               = ak_heap_calloc(heap, texref, sizeof(*texTransf));\n      texref->transform       = texTransf;\n      texTransf->slot = -1;\n      texTransf->scale[0]     = 1.0;\n      texTransf->scale[1]     = 1.0;\n\n      jval = jval->value;\n      while (jval) {\n        if (json_key_eq(jval, _s_gltf_offset)) {\n          json_array_float(texTransf->offset, jval, 0.0f, 2, true);\n        } else if (json_key_eq(jval, _s_gltf_rotation)) {\n          texTransf->rotation = json_float(jval, 0.0f);\n        } else if (json_key_eq(jval, _s_gltf_scale)) {\n          json_array_float(texTransf->scale, jval, 0.0f, 2, true);\n        } else if (json_key_eq(jval, _s_gltf_texCoord)) {\n          texTransf->slot           = json_int32(jval, -1);\n          texTransf->coordInputName = coordInputName(heap, texTransf, texTransf->slot);\n        }\n        jval = jval->next;\n      }\n    }\n  }\n\n  texref->texture = tex;\n  texref->slot    = set;\n\n  return texref;\n}\n\nAK_HIDE\nvoid\ngltf_textures(json_t * __restrict jtex,\n              void   * __restrict userdata) {\n  AkGLTFState        *gst;\n  AkDoc              *doc;\n  const json_array_t *jtextures;\n  const json_t       *jtexVal;\n  AkTexture          *tex;\n\n  if (!(jtextures = json_array(jtex)))\n    return;\n\n  gst  = userdata;\n  doc  = gst->doc;\n\n  jtex = jtextures->base.value;\n  while (jtex) {\n    AkSampler *sampler;\n\n    jtexVal   = jtex->value;\n    tex       = ak_heap_calloc(gst->heap, gst->doc, sizeof(*tex));\n    tex->type = AKT_SAMPLER2D;\n    sampler   = NULL;\n    gltf_extra(gst,\n               tex,\n               json_get(jtex, _s_gltf_extras),\n               json_get(jtex, _s_gltf_extensions));\n\n    while (jtexVal) {\n      if (json_key_eq(jtexVal, _s_gltf_sampler)) {\n        sampler = flist_sp_at(&doc->lib.samplers, json_int32(jtexVal, -1));\n      } else if (json_key_eq(jtexVal, _s_gltf_source)) {\n        tex->image = flist_sp_at(&doc->lib.images, json_int32(jtexVal, -1));\n      } else if (json_key_eq(jtexVal, _s_gltf_name)) {\n        tex->name = json_strdup(jtexVal, gst->heap, tex);\n      } else if (json_key_eq(jtexVal, _s_gltf_extensions)) {\n        /* Texture-source extensions. WebP can go through the normal image\n           loader. KTX2/BasisU is selected only when the optional decoder\n           shim is available, so a PNG/JPEG fallback remains intact. */\n        json_t *jwebp;\n        json_t *jktx2;\n        json_t *jaltSrc;\n\n        jwebp = json_get(jtexVal, _s_gltf_EXT_texture_webp);\n        jktx2 = json_get(jtexVal, _s_gltf_KHR_texture_basisu);\n\n        if (jktx2\n            && gltf_ext_textureBasisu(gst)\n            && (jaltSrc = json_get(jktx2, _s_gltf_source))) {\n          AkImage *altImage = flist_sp_at(&doc->lib.images,\n                                          json_int32(jaltSrc, -1));\n          if (altImage)\n            tex->image = altImage;\n        }\n        if (jwebp && (jaltSrc = json_get(jwebp, _s_gltf_source))) {\n          AkImage *altImage = flist_sp_at(&doc->lib.images,\n                                          json_int32(jaltSrc, -1));\n          if (altImage)\n            tex->image = altImage;\n        }\n      }\n\n      jtexVal = jtexVal->next;\n    }\n\n    /* TODO: add option for this */\n    /* create default sampler */\n    if (!sampler) {\n      sampler        = ak_heap_calloc(gst->heap, gst->doc, sizeof(*sampler));\n      sampler->wrapS = AK_WRAP_MODE_WRAP;\n      sampler->wrapT = AK_WRAP_MODE_WRAP;\n      \n      ak_setypeid(sampler, AKT_SAMPLER2D);\n    }\n\n    tex->sampler = sampler;\n\n    flist_sp_insert(&gst->doc->lib.textures, tex);\n    jtex = jtex->next;\n  }\n}\n"
  },
  {
    "path": "src/io/gltf/imp/core/texture.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_core_texture_h\n#define gltf_imp_core_texture_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkTextureRef*\ngltf_texref(AkGLTFState * __restrict gst,\n            void        * __restrict parent,\n            json_t      * __restrict jtexinfo);\n\nAK_HIDE\nvoid\ngltf_textures(json_t * __restrict jtex,\n              void   * __restrict userdata);\n\n#endif /* gltf_imp_core_texture_h */\n"
  },
  {
    "path": "src/io/gltf/imp/ext/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME}\n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/gltf/imp/ext/compression.c",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n */\n\n#include \"decoder.h\"\n#include \"../core/ext.h\"\n\ntypedef enum AkGLTFMeshoptMode {\n  AK_GLTF_MESHOPT_MODE_UNKNOWN = 0,\n  AK_GLTF_MESHOPT_MODE_ATTRIBUTES,\n  AK_GLTF_MESHOPT_MODE_TRIANGLES,\n  AK_GLTF_MESHOPT_MODE_INDICES\n} AkGLTFMeshoptMode;\n\ntypedef enum AkGLTFMeshoptFilter {\n  AK_GLTF_MESHOPT_FILTER_NONE = 0,\n  AK_GLTF_MESHOPT_FILTER_OCTAHEDRAL,\n  AK_GLTF_MESHOPT_FILTER_QUATERNION,\n  AK_GLTF_MESHOPT_FILTER_EXPONENTIAL\n} AkGLTFMeshoptFilter;\n\nstatic\nAkGLTFMeshoptMode\ngltf_ext_meshoptMode(const json_t * __restrict jmode) {\n  if (!jmode)\n    return AK_GLTF_MESHOPT_MODE_UNKNOWN;\n\n  if (json_val_eq(jmode, _s_gltf_ATTRIBUTES))\n    return AK_GLTF_MESHOPT_MODE_ATTRIBUTES;\n  if (json_val_eq(jmode, _s_gltf_TRIANGLES))\n    return AK_GLTF_MESHOPT_MODE_TRIANGLES;\n  if (json_val_eq(jmode, _s_gltf_INDICES))\n    return AK_GLTF_MESHOPT_MODE_INDICES;\n\n  return AK_GLTF_MESHOPT_MODE_UNKNOWN;\n}\n\nstatic\nAkGLTFMeshoptFilter\ngltf_ext_meshoptFilter(const json_t * __restrict jfilter) {\n  if (!jfilter || json_val_eq(jfilter, _s_gltf_NONE))\n    return AK_GLTF_MESHOPT_FILTER_NONE;\n\n  if (json_val_eq(jfilter, _s_gltf_OCTAHEDRAL))\n    return AK_GLTF_MESHOPT_FILTER_OCTAHEDRAL;\n  if (json_val_eq(jfilter, _s_gltf_QUATERNION))\n    return AK_GLTF_MESHOPT_FILTER_QUATERNION;\n  if (json_val_eq(jfilter, _s_gltf_EXPONENTIAL))\n    return AK_GLTF_MESHOPT_FILTER_EXPONENTIAL;\n\n  return AK_GLTF_MESHOPT_FILTER_NONE;\n}\n\nAK_HIDE\nbool\ngltf_ext_bufferView(AkGLTFState  * __restrict gst,\n                    AkBufferView * __restrict buffView,\n                    const json_t * __restrict jext) {\n  const json_t          *jmo;\n  const json_t          *it;\n  AkBuffer              *srcBuff;\n  AkBuffer              *dstBuff;\n  const unsigned char   *src;\n  size_t                 srcOff;\n  size_t                 srcLen;\n  size_t                 stride;\n  size_t                 count;\n  size_t                 dstLen;\n  int32_t                buffIdx;\n  AkGLTFMeshoptMode      mode;\n  AkGLTFMeshoptFilter    filter;\n\n  if (!gst || !buffView || !jext)\n    return true;\n\n  jmo = json_get(jext, _s_gltf_EXT_meshopt_compression);\n  if (!jmo)\n    jmo = json_get(jext, _s_gltf_KHR_meshopt_compression);\n  if (!jmo)\n    return true;\n\n  if (!gltf_ext_meshopt(gst)) {\n    if (buffView->buffer && buffView->buffer->data)\n      return true;\n    return false;\n  }\n\n  it      = json_get(jmo, _s_gltf_buffer);\n  buffIdx = it ? json_int32(it, -1) : -1;\n  if (buffIdx < 0\n      || !(srcBuff = flist_sp_at(&gst->buffers, buffIdx))\n      || !srcBuff->data)\n    return false;\n\n  srcOff = (it = json_get(jmo, _s_gltf_byteOffset))\n             ? (size_t)json_uint64(it, 0) : 0;\n  srcLen = (it = json_get(jmo, _s_gltf_byteLength))\n             ? (size_t)json_uint64(it, 0) : 0;\n  stride = (it = json_get(jmo, _s_gltf_byteStride))\n             ? (size_t)json_uint64(it, 0) : 0;\n  count  = (it = json_get(jmo, _s_gltf_count))\n             ? (size_t)json_uint64(it, 0) : 0;\n  mode   = gltf_ext_meshoptMode(json_get(jmo, _s_gltf_mode));\n  filter = gltf_ext_meshoptFilter(json_get(jmo, _s_gltf_filter));\n\n  if (srcOff > srcBuff->length\n      || srcLen == 0\n      || srcLen > srcBuff->length - srcOff\n      || stride == 0\n      || count == 0)\n    return false;\n\n  dstLen = buffView->byteLength;\n  if (dstLen == 0) {\n    if (count > SIZE_MAX / stride)\n      return false;\n    dstLen = count * stride;\n  }\n\n  dstBuff         = ak_heap_calloc(gst->heap, gst->doc, sizeof(*dstBuff));\n  dstBuff->data   = ak_heap_alloc(gst->heap, dstBuff, dstLen);\n  dstBuff->length = dstLen;\n  src             = (const unsigned char *)srcBuff->data + srcOff;\n\n  if (!gltf_ext_meshoptDecode(gst,\n                              dstBuff->data,\n                              dstLen,\n                              src,\n                              srcLen,\n                              count,\n                              stride,\n                              mode,\n                              filter))\n    return false;\n\n  buffView->buffer     = dstBuff;\n  buffView->byteOffset = 0;\n  buffView->byteLength = dstLen;\n  buffView->byteStride = stride;\n\n  return true;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/ext/decoder.c",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n */\n\n#include \"decoder.h\"\n#include \"../../../../platform/dylib.h\"\n#include \"../../../../../include/ak/gsplat.h\"\n\ntypedef int\n(*AkMeshoptDecodeBufferFn)(void                *destination,\n                           size_t               destination_size,\n                           const unsigned char *buffer,\n                           size_t               buffer_size,\n                           size_t               count,\n                           size_t               stride,\n                           int                  mode,\n                           int                  filter);\n\ntypedef int\n(*AkDracoDecodePrimitiveFn)(AkGLTFState     *gst,\n                            AkMeshPrimitive *prim,\n                            const json_t    *jprim,\n                            const json_t    *jext);\n\nstruct AkGLTFMeshoptLib {\n  void                    *lib;\n  AkMeshoptDecodeBufferFn  decodeBuffer;\n  bool                     tried;\n};\n\nstruct AkGLTFDracoLib {\n  void                       *lib;\n  AkDracoDecodePrimitiveFn    decodePrimitive;\n  bool                        tried;\n};\n\nstruct AkGLTFSPZLib {\n  void                            *lib;\n  AkGaussianSplatDecoder           decoder;\n  bool                             tried;\n};\n\ntypedef struct AkKTX2MipLevel {\n  uint32_t width;\n  uint32_t height;\n  uint32_t byteOffset;\n  uint32_t byteLength;\n} AkKTX2MipLevel;\n\ntypedef struct AkKTX2DecodedImage {\n  uint8_t        *data;\n  size_t          dataLength;\n  uint32_t        width;\n  uint32_t        height;\n  uint32_t        channels;\n  uint32_t        mipCount;\n  AkKTX2MipLevel *mips;\n  uint32_t        reserved[2];\n} AkKTX2DecodedImage;\n\ntypedef int\n(*AkKTX2DecodeFn)(const uint8_t     *data,\n                  size_t             size,\n                  AkKTX2DecodedImage *out);\n\nstruct AkGLTFKTX2Lib {\n  void          *lib;\n  AkKTX2DecodeFn decode;\n  bool           tried;\n};\n\nstatic\nvoid*\ngltf_ext_openLib(AkOption      opt,\n                 const char * __restrict name) {\n  const char *path;\n  void       *lib;\n\n  path = (const char *)ak_opt_get(opt);\n  if (path && (lib = ak_dylib_open(path)))\n    return lib;\n\n  if (!ak_opt_get(AK_OPT_GLTF_EXT_DECODER_AUTOLOAD))\n    return NULL;\n\n  return ak_dylib_openName(name);\n}\n\nAK_HIDE\nbool\ngltf_ext_meshopt(AkGLTFState * __restrict gst) {\n  AkGLTFMeshoptLib *mo;\n  void             *lib;\n\n  if (gst->meshopt)\n    return gst->meshopt->lib != NULL;\n\n  mo = ak_calloc(NULL, sizeof(*mo));\n  mo->tried = true;\n  gst->meshopt = mo;\n\n  lib = gltf_ext_openLib(AK_OPT_GLTF_MESHOPT_DECODER_PATH,\n                         \"assetkit_meshoptimizer\");\n  if (!lib)\n    return false;\n\n  mo->decodeBuffer = (AkMeshoptDecodeBufferFn)\n    ak_dylib_sym(lib, \"ak_meshopt_decode_gltf_buffer\");\n\n  if (!mo->decodeBuffer) {\n    ak_dylib_close(lib);\n    return false;\n  }\n\n  mo->lib = lib;\n  return true;\n}\n\nAK_HIDE\nbool\ngltf_ext_spz(AkGLTFState * __restrict gst) {\n  AkGLTFSPZLib                    *sp;\n  AkGaussianSplatDecoderCreateFn   createFn;\n  void                            *lib;\n\n  if (gst->spz)\n    return gst->spz->lib != NULL;\n\n  sp        = ak_calloc(NULL, sizeof(*sp));\n  sp->tried = true;\n  gst->spz  = sp;\n\n  lib = gltf_ext_openLib(AK_OPT_GLTF_GSPLAT_DECODER_PATH,\n                         \"assetkit_spz\");\n  if (!lib)\n    return false;\n\n  createFn = (AkGaussianSplatDecoderCreateFn)\n              ak_dylib_sym(lib, \"assetkit_gsplat_create\");\n  if (!createFn || createFn(&sp->decoder) != 0\n      || (!sp->decoder.decodeBytes && !sp->decoder.decodePrimitive)) {\n    ak_dylib_close(lib);\n    return false;\n  }\n\n  sp->lib = lib;\n  return true;\n}\n\nAK_HIDE\nbool\ngltf_ext_ktx2(AkGLTFState * __restrict gst) {\n  AkGLTFKTX2Lib *kx;\n  void          *lib;\n\n  if (gst->ktx2)\n    return gst->ktx2->lib != NULL;\n\n  kx        = ak_calloc(NULL, sizeof(*kx));\n  kx->tried = true;\n  gst->ktx2 = kx;\n\n  lib = gltf_ext_openLib(AK_OPT_GLTF_KTX2_DECODER_PATH,\n                         \"assetkit_ktx2\");\n  if (!lib)\n    return false;\n\n  kx->decode = (AkKTX2DecodeFn)ak_dylib_sym(lib, \"assetkit_ktx2_decode\");\n  if (!kx->decode) {\n    ak_dylib_close(lib);\n    return false;\n  }\n\n  kx->lib = lib;\n  return true;\n}\n\nAK_HIDE\nbool\ngltf_ext_textureBasisu(AkGLTFState * __restrict gst) {\n  return gltf_ext_ktx2(gst);\n}\n\nAK_HIDE\nbool\ngltf_ext_draco(AkGLTFState * __restrict gst) {\n  AkGLTFDracoLib *dr;\n  void           *lib;\n\n  if (gst->draco)\n    return gst->draco->lib != NULL;\n\n  dr = ak_calloc(NULL, sizeof(*dr));\n  dr->tried = true;\n  gst->draco = dr;\n\n  lib = gltf_ext_openLib(AK_OPT_GLTF_DRACO_DECODER_PATH,\n                         \"assetkit_draco\");\n  if (!lib)\n    return false;\n\n  dr->decodePrimitive = (AkDracoDecodePrimitiveFn)\n    ak_dylib_sym(lib, \"ak_draco_decode_gltf_primitive\");\n  if (!dr->decodePrimitive) {\n    ak_dylib_close(lib);\n    return false;\n  }\n\n  dr->lib = lib;\n  return true;\n}\n\nAK_HIDE\nbool\ngltf_ext_spzDecodeBytes(AkGLTFState     * __restrict gst,\n                        AkMeshPrimitive * __restrict prim,\n                        const uint8_t   * __restrict data,\n                        size_t                       size) {\n  if (!gltf_ext_spz(gst) || !gst->spz->decoder.decodeBytes)\n    return false;\n\n  return gst->spz->decoder.decodeBytes(gst->heap, prim, data, size) == 0;\n}\n\nAK_HIDE\nbool\ngltf_ext_dracoPrimitive(AkGLTFState     * __restrict gst,\n                        AkMeshPrimitive * __restrict prim,\n                        const json_t    * __restrict jprim) {\n  const json_t *jext;\n  const json_t *jdraco;\n\n  if (!gst || !prim || !jprim)\n    return true;\n\n  jext = json_get(jprim, _s_gltf_extensions);\n  if (!jext)\n    return true;\n\n  jdraco = json_get(jext, _s_gltf_KHR_draco_mesh_compression);\n  if (!jdraco)\n    return true;\n\n  if (!gltf_ext_draco(gst))\n    return true;\n\n  return gst->draco->decodePrimitive(gst, prim, jprim, jdraco) == 0;\n}\n\nAK_HIDE\nbool\ngltf_ext_meshoptDecode(AkGLTFState          * __restrict gst,\n                       void                 * __restrict dst,\n                       size_t                            dstSize,\n                       const unsigned char  * __restrict src,\n                       size_t                            srcSize,\n                       size_t                            count,\n                       size_t                            stride,\n                       int                               mode,\n                       int                               filter) {\n  AkGLTFMeshoptLib *mo;\n  int               res;\n\n  if (!gltf_ext_meshopt(gst))\n    return false;\n\n  mo = gst->meshopt;\n\n  if (!dst\n      || !src\n      || stride == 0\n      || count > SIZE_MAX / stride\n      || dstSize < count * stride)\n    return false;\n\n  res = mo->decodeBuffer(dst,\n                         dstSize,\n                         src,\n                         srcSize,\n                         count,\n                         stride,\n                         mode,\n                         filter);\n\n  return res == 0;\n}\n\nAK_HIDE\nvoid\ngltf_ext_decoderClose(AkGLTFState * __restrict gst) {\n  if (!gst)\n    return;\n\n  if (gst->meshopt) {\n    if (gst->meshopt->lib)\n      ak_dylib_close(gst->meshopt->lib);\n    ak_free(gst->meshopt);\n    gst->meshopt = NULL;\n  }\n\n  if (gst->draco) {\n    if (gst->draco->lib)\n      ak_dylib_close(gst->draco->lib);\n    ak_free(gst->draco);\n    gst->draco = NULL;\n  }\n\n  if (gst->spz) {\n    if (gst->spz->decoder.close)\n      gst->spz->decoder.close(gst->spz->decoder.userdata);\n    if (gst->spz->lib)\n      ak_dylib_close(gst->spz->lib);\n    ak_free(gst->spz);\n    gst->spz = NULL;\n  }\n\n  if (gst->ktx2) {\n    if (gst->ktx2->lib)\n      ak_dylib_close(gst->ktx2->lib);\n    ak_free(gst->ktx2);\n    gst->ktx2 = NULL;\n  }\n}\n"
  },
  {
    "path": "src/io/gltf/imp/ext/decoder.h",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n */\n\n#ifndef gltf_imp_ext_decoder_h\n#define gltf_imp_ext_decoder_h\n\n#include \"../common.h\"\n\nAK_HIDE\nbool\ngltf_ext_meshopt(AkGLTFState * __restrict gst);\n\nAK_HIDE\nbool\ngltf_ext_draco(AkGLTFState * __restrict gst);\n\nAK_HIDE\nbool\ngltf_ext_spz(AkGLTFState * __restrict gst);\n\nAK_HIDE\nbool\ngltf_ext_ktx2(AkGLTFState * __restrict gst);\n\nAK_HIDE\nbool\ngltf_ext_textureBasisu(AkGLTFState * __restrict gst);\n\nAK_HIDE\nbool\ngltf_ext_spzDecodeBytes(AkGLTFState     * __restrict gst,\n                        AkMeshPrimitive * __restrict prim,\n                        const uint8_t   * __restrict data,\n                        size_t                       size);\n\nAK_HIDE\nbool\ngltf_ext_meshoptDecode(AkGLTFState          * __restrict gst,\n                       void                 * __restrict dst,\n                       size_t                            dstSize,\n                       const unsigned char  * __restrict src,\n                       size_t                            srcSize,\n                       size_t                            count,\n                       size_t                            stride,\n                       int                               mode,\n                       int                               filter);\n\nAK_HIDE\nvoid\ngltf_ext_decoderClose(AkGLTFState * __restrict gst);\n\n#endif /* gltf_imp_ext_decoder_h */\n"
  },
  {
    "path": "src/io/gltf/imp/ext/gsplat.c",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n */\n\n#include \"decoder.h\"\n#include \"../core/ext.h\"\n#include \"../../../../../include/ak/gsplat.h\"\n\nstatic\nAkGaussianSplatColorSpace\ngltf_gsplatColorSpace(const json_t * __restrict v) {\n  size_t      sz;\n  const char *s;\n\n  if (!v)\n    return AK_GSPLAT_COLOR_UNKNOWN;\n\n  s  = json_string(v);\n  sz = v->valsize;\n  if (sz == 19 && memcmp(s, \"srgb_rec709_display\", 19) == 0)\n    return AK_GSPLAT_COLOR_SRGB_REC709_DISPLAY;\n  if (sz == 18 && memcmp(s, \"lin_rec709_display\", 18) == 0)\n    return AK_GSPLAT_COLOR_LIN_REC709_DISPLAY;\n\n  return AK_GSPLAT_COLOR_UNKNOWN;\n}\n\nstatic\nAkGaussianSplatProjection\ngltf_gsplatProjection(const json_t * __restrict v) {\n  size_t      sz;\n  const char *s;\n\n  if (!v)\n    return AK_GSPLAT_PROJECTION_PERSPECTIVE;\n\n  s  = json_string(v);\n  sz = v->valsize;\n  if (sz == 12 && memcmp(s, \"orthographic\", 12) == 0)\n    return AK_GSPLAT_PROJECTION_ORTHOGRAPHIC;\n\n  return AK_GSPLAT_PROJECTION_PERSPECTIVE;\n}\n\nstatic\nAkGaussianSplatSortingMethod\ngltf_gsplatSorting(const json_t * __restrict v) {\n  size_t      sz;\n  const char *s;\n\n  if (!v)\n    return AK_GSPLAT_SORTING_CAMERA_DISTANCE;\n\n  s  = json_string(v);\n  sz = v->valsize;\n  if (sz == 4 && memcmp(s, \"none\", 4) == 0)\n    return AK_GSPLAT_SORTING_NONE;\n\n  return AK_GSPLAT_SORTING_CAMERA_DISTANCE;\n}\n\nAK_HIDE\nbool\ngltf_ext_primitiveGaussianSplat(AkGLTFState     * __restrict gst,\n                                AkMeshPrimitive * __restrict prim,\n                                const json_t    * __restrict jprim) {\n  const json_t    *jext;\n  const json_t    *jgsplat;\n  json_t          *jkernel;\n  json_t          *jcolor;\n  json_t          *jproj;\n  json_t          *jsort;\n  AkGaussianSplat *gs;\n\n  if (!gst || !prim || !jprim)\n    return true;\n\n  jext    = json_get(jprim, _s_gltf_extensions);\n  jgsplat = jext ? json_get(jext, _s_gltf_KHR_gaussian_splatting) : NULL;\n  if (!jgsplat)\n    return true;\n\n  gs = ak_heap_calloc(gst->heap, prim, sizeof(*gs));\n\n  jkernel = json_get(jgsplat, _s_gltf_kernel);\n  jcolor  = json_get(jgsplat, _s_gltf_colorSpace);\n  jproj   = json_get(jgsplat, _s_gltf_projection);\n  jsort   = json_get(jgsplat, _s_gltf_sortingMethod);\n\n  (void)jkernel;\n  gs->kernel        = AK_GSPLAT_KERNEL_ELLIPSE;\n  gs->colorSpace    = gltf_gsplatColorSpace(jcolor);\n  gs->projection    = gltf_gsplatProjection(jproj);\n  gs->sortingMethod = gltf_gsplatSorting(jsort);\n\n  prim->gsplat = gs;\n\n  {\n    const json_t *jcomp;\n    const json_t *jformat;\n    const json_t *jbv;\n    int32_t       bvIdx;\n    AkBufferView *bv;\n\n    if ((jcomp = json_get(jgsplat, _s_gltf_compression))) {\n      jformat = json_get(jcomp, _s_gltf_format);\n      jbv     = json_get(jcomp, _s_gltf_bufferView);\n\n      if (jformat && !json_val_eq(jformat, _s_gltf_spz))\n        return false;\n      if (!jbv)\n        return false;\n\n      bvIdx = json_int32(jbv, -1);\n      bv    = (bvIdx >= 0) ? flist_sp_at(&gst->bufferViews, bvIdx) : NULL;\n      if (!bv || !bv->buffer || !bv->buffer->data || bv->byteLength == 0)\n        return false;\n\n      {\n        const uint8_t *bytes;\n\n        bytes = (const uint8_t *)bv->buffer->data + bv->byteOffset;\n        if (!gltf_ext_spzDecodeBytes(gst, prim, bytes, bv->byteLength))\n          return false;\n      }\n    }\n  }\n\n  return true;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/ext/instancing.c",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n */\n\n#include \"instancing.h\"\n\nstatic\nbool\ngltf_ext_instanceAccOK(AkAccessor * __restrict acc,\n                       uint32_t                componentCount) {\n  return acc\n         && acc->componentType == AKT_FLOAT\n         && acc->componentCount == componentCount\n         && acc->count > 0;\n}\n\n/* EXT_mesh_gpu_instancing: pull the per-instance TRS accessor refs out of\n   the extension's `attributes` object. Each attribute is optional; present\n   attributes must all have the same accessor count per the spec. */\nAK_HIDE\nAkInstanceAttribs*\ngltf_ext_meshGPUInstancing(AkGLTFState * __restrict gst,\n                           AkNode      * __restrict node,\n                           const json_t * __restrict jinstancing) {\n  AkInstanceAttribs *attribs;\n  AkDoc             *doc;\n  json_t            *jattrs;\n  json_t            *jattr;\n  AkAccessor        *acc;\n  int32_t            accIdx;\n  uint32_t           count;\n  uint32_t           compCount;\n  bool               known;\n\n  if (!(jattrs = json_get(jinstancing, _s_gltf_attributes)))\n    return NULL;\n\n  doc     = gst->doc;\n  attribs = ak_heap_calloc(gst->heap, node, sizeof(*attribs));\n  count   = 0;\n\n  for (jattr = jattrs->value; jattr; jattr = jattr->next) {\n    accIdx    = json_int32(jattr, -1);\n    known     = false;\n    compCount = 0;\n\n    if (jattr->keysize == 11\n        && memcmp(jattr->key, \"TRANSLATION\", 11) == 0) {\n      known     = true;\n      compCount = 3;\n    } else if (jattr->keysize == 8\n               && memcmp(jattr->key, \"ROTATION\", 8) == 0) {\n      known     = true;\n      compCount = 4;\n    } else if (jattr->keysize == 5\n               && memcmp(jattr->key, \"SCALE\", 5) == 0) {\n      known     = true;\n      compCount = 3;\n    }\n\n    if (!known)\n      continue;\n\n    if (accIdx < 0) {\n      gst->stop = true;\n      return NULL;\n    }\n    if (!(acc = flist_sp_at(&doc->lib.accessors, accIdx)))\n      goto malformed;\n    if (!gltf_ext_instanceAccOK(acc, compCount))\n      goto malformed;\n\n    if (count == 0) {\n      count = (uint32_t)acc->count;\n    } else if (count != (uint32_t)acc->count) {\n      goto malformed;\n    }\n\n    ak_retain(acc);\n\n    if (jattr->keysize == 11\n        && memcmp(jattr->key, \"TRANSLATION\", 11) == 0) {\n      attribs->translation = acc;\n    } else if (jattr->keysize == 8\n               && memcmp(jattr->key, \"ROTATION\", 8) == 0) {\n      attribs->rotation = acc;\n    } else if (jattr->keysize == 5\n               && memcmp(jattr->key, \"SCALE\", 5) == 0) {\n      attribs->scale = acc;\n    }\n  }\n\n  if (count == 0)\n    return NULL;\n\n  attribs->count = count;\n  return attribs;\n\nmalformed:\n  gst->stop = true;\n  return NULL;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/ext/instancing.h",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n */\n\n#ifndef gltf_imp_ext_instancing_h\n#define gltf_imp_ext_instancing_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkInstanceAttribs*\ngltf_ext_meshGPUInstancing(AkGLTFState * __restrict gst,\n                           AkNode      * __restrict node,\n                           const json_t * __restrict jinstancing);\n\n#endif /* gltf_imp_ext_instancing_h */\n"
  },
  {
    "path": "src/io/gltf/imp/ext/lights.c",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n */\n\n#include \"lights.h\"\n#include \"../extra.h\"\n\nstatic\nAkLightType\ngltf_ext_lightType(const json_t * __restrict jtype) {\n  if (!jtype)\n    return AK_LIGHT_TYPE_POINT;\n\n  if (json_val_eq(jtype, _s_gltf_directional))\n    return AK_LIGHT_TYPE_DIRECTIONAL;\n  if (json_val_eq(jtype, _s_gltf_spot))\n    return AK_LIGHT_TYPE_SPOT;\n  if (json_val_eq(jtype, _s_gltf_point))\n    return AK_LIGHT_TYPE_POINT;\n\n  return AK_LIGHT_TYPE_POINT;\n}\n\nAK_HIDE\nvoid\ngltf_ext_lights(AkGLTFState * __restrict gst,\n                json_t      * __restrict jlights) {\n  const json_array_t *jarr;\n  json_t             *jlight;\n  json_t             *it;\n  AkLight            *light;\n  AkLightBase        *base;\n  AkSpotLight        *spot;\n\n  if (!gst || !(jarr = json_array(jlights)))\n    return;\n\n  jlight = jarr->base.value;\n  while (jlight) {\n    it    = json_get(jlight, _s_gltf_type);\n    light = ak_lightMake(gst->doc, gst->doc, gltf_ext_lightType(it));\n    if (!light)\n      goto nxt;\n\n    gltf_extra(gst,\n               light,\n               json_get(jlight, _s_gltf_extras),\n               json_get(jlight, _s_gltf_extensions));\n\n    base = light->tcommon;\n    if ((it = json_get(jlight, _s_gltf_name)))\n      light->name = json_strdup(it, gst->heap, light);\n\n    if ((it = json_get(jlight, _s_gltf_color))) {\n      json_array_float(base->color.vec, it, 1.0f, 3, true);\n      base->color.vec[3] = 1.0f;\n    }\n\n    base->intensity = json_float(json_get(jlight, _s_gltf_intensity), 1.0f);\n    base->range     = json_float(json_get(jlight, _s_gltf_range),     0.0f);\n\n    if (base->type == AK_LIGHT_TYPE_SPOT\n        && (it = json_get(jlight, _s_gltf_spot))) {\n      spot = (AkSpotLight *)base;\n      spot->innerConeAngle = json_float(json_get(it, _s_gltf_innerConeAngle),\n                                        0.0f);\n      spot->outerConeAngle = json_float(json_get(it, _s_gltf_outerConeAngle),\n                                        GLM_PI_4f);\n    }\n\n  nxt:\n    jlight = jlight->next;\n  }\n}\n\nAK_HIDE\nbool\ngltf_ext_nodeLight(AkGLTFState * __restrict gst,\n                   AkNode      * __restrict node,\n                   const json_t * __restrict jext) {\n  json_t  *jpunctual;\n  json_t  *jlight;\n  AkLight *light;\n  int32_t  lightIndex;\n\n  jpunctual = json_get(jext, _s_gltf_KHR_lights_punctual);\n  jlight    = jpunctual ? json_get(jpunctual, _s_gltf_light) : NULL;\n  if (!jlight)\n    return true;\n\n  lightIndex = json_int32(jlight, -1);\n  if (lightIndex < 0 || !gst->doc->lib.lights)\n    return false;\n\n  light = (void *)gst->doc->lib.lights->chld;\n  while (light && lightIndex > 0) {\n    light = light->next;\n    lightIndex--;\n  }\n\n  if (!light)\n    return false;\n\n  return ak_nodeAttachLight(node, light) != NULL;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/ext/lights.h",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n */\n\n#ifndef gltf_imp_ext_lights_h\n#define gltf_imp_ext_lights_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_ext_lights(AkGLTFState * __restrict gst,\n                json_t      * __restrict jlights);\n\nAK_HIDE\nbool\ngltf_ext_nodeLight(AkGLTFState * __restrict gst,\n                   AkNode      * __restrict node,\n                   const json_t * __restrict jext);\n\n#endif /* gltf_imp_ext_lights_h */\n"
  },
  {
    "path": "src/io/gltf/imp/ext/variants.c",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n */\n\n#include \"variants.h\"\n#include \"../core/ext.h\"\n\n/* KHR_materials_variants — root-level: capture the document's variant list\n   so primitive parser can resolve `variants[]` indices later. The doc\n   field stays in declaration-order (head→tail = index 0→N), matching the\n   indices the primitive `mappings[].variants[]` arrays use. */\nAK_HIDE\nvoid\ngltf_ext_materialVariants(AkGLTFState * __restrict gst,\n                          json_t      * __restrict jvariants) {\n  json_array_t       *jarr;\n  json_t             *jvariant;\n  AkMaterialVariant  *variant;\n  AkDoc              *doc;\n  uint32_t            count;\n  json_t             *jname;\n\n  if (!(jarr = json_array(jvariants)) || jarr->count == 0)\n    return;\n\n  doc      = gst->doc;\n  jvariant = jarr->base.value;\n  count    = 0;\n\n  doc->materialVariants = NULL;\n  while (jvariant) {\n    variant = ak_heap_calloc(gst->heap, doc, sizeof(*variant));\n\n    if ((jname = json_get(jvariant, _s_gltf_name)))\n      variant->name = json_strdup(jname, gst->heap, variant);\n\n    /* json_parse(..., true) gives array children in reverse. Prepend\n       restores source/index order: variants[0] is list head. */\n    variant->next         = doc->materialVariants;\n    doc->materialVariants = variant;\n\n    count++;\n    jvariant = jvariant->next;\n  }\n\n  doc->materialVariantCount = count;\n}\n\nAK_HIDE\nbool\ngltf_ext_primitiveVariants(AkGLTFState     * __restrict gst,\n                           AkMeshPrimitive * __restrict prim,\n                           const json_t    * __restrict jprim) {\n  const json_t             *jext;\n  const json_t             *jvariantsExt;\n  const json_t             *jmappings;\n  json_array_t             *jmapArr;\n  json_t                   *jmap;\n  json_t                   *jmaterial;\n  json_t                   *jvariants;\n  json_array_t             *jvarsArr;\n  json_t                   *jvarIdx;\n  AkMaterialVariantMapping *mapping;\n  AkMaterialVariantMapping *tail;\n  AkMaterial               *material;\n  int32_t                   matIdx;\n  uint32_t                  variantIdx;\n  uint32_t                  count;\n\n  if (!gst || !prim || !jprim)\n    return true;\n\n  jext         = json_get(jprim, _s_gltf_extensions);\n  jvariantsExt = jext ? json_get(jext, _s_gltf_KHR_materials_variants) : NULL;\n  jmappings    = jvariantsExt ? json_get(jvariantsExt, _s_gltf_mappings) : NULL;\n  if (!jmappings || !(jmapArr = json_array(jmappings)) || jmapArr->count == 0)\n    return true;\n\n  count = 0;\n  tail  = NULL;\n\n  /* Each `mapping` entry references a single material and the list of\n     variant indices that should resolve to it. We unroll the variants\n     list into a flat per-(variant, material) chain so runtime swap is\n     O(numVariants) — typical N is small (<10) so the unroll is cheap. */\n  for (jmap = jmapArr->base.value; jmap; jmap = jmap->next) {\n    jmaterial = json_get(jmap, _s_gltf_material);\n    jvariants = json_get(jmap, _s_gltf_variants);\n    if (!jmaterial || !jvariants)\n      continue;\n\n    matIdx = json_int32(jmaterial, -1);\n    if (matIdx < 0)\n      continue;\n\n    material = NULL;\n    if (gst->doc->lib.materials)\n      GETCHILD(gst->doc->lib.materials->chld, material, matIdx);\n    if (!material)\n      continue;\n\n    if (!(jvarsArr = json_array(jvariants)))\n      continue;\n\n    for (jvarIdx = jvarsArr->base.value; jvarIdx; jvarIdx = jvarIdx->next) {\n      variantIdx = (uint32_t)json_int32(jvarIdx, -1);\n      if ((int32_t)variantIdx < 0\n          || variantIdx >= gst->doc->materialVariantCount)\n        continue;\n\n      mapping = ak_heap_calloc(gst->heap, prim, sizeof(*mapping));\n      mapping->material     = material;\n      mapping->variantIndex = variantIdx;\n\n      if (tail) {\n        tail->next = mapping;\n      } else {\n        prim->variantMappings = mapping;\n      }\n      tail = mapping;\n\n      count++;\n    }\n  }\n\n  prim->variantMappingCount = count;\n  return true;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/ext/variants.h",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n */\n\n#ifndef gltf_imp_ext_variants_h\n#define gltf_imp_ext_variants_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ngltf_ext_materialVariants(AkGLTFState * __restrict gst,\n                          json_t      * __restrict jvariants);\n\n#endif /* gltf_imp_ext_variants_h */\n"
  },
  {
    "path": "src/io/gltf/imp/extra.c",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n */\n\n#include \"extra.h\"\n\nstatic\nAkTreeNodeAttr*\ngltf_treeAttr(AkHeap     * __restrict heap,\n              AkTreeNode * __restrict node,\n              const char * __restrict name,\n              const char * __restrict val) {\n  AkTreeNodeAttr *attr;\n\n  attr       = ak_heap_calloc(heap, node, sizeof(*attr));\n  attr->name = ak_heap_strdup(heap, attr, name);\n  attr->val  = ak_heap_strdup(heap, attr, val);\n\n  attr->next = node->attribs;\n  if (node->attribs)\n    node->attribs->prev = attr;\n\n  node->attribs = attr;\n  node->attrc++;\n\n  return attr;\n}\n\nstatic\nvoid\ngltf_treeAppend(AkTreeNode * __restrict parent,\n                AkTreeNode * __restrict child) {\n  child->next = parent->chld;\n  if (parent->chld)\n    parent->chld->prev = child;\n\n  parent->chld  = child;\n  child->parent = parent;\n  parent->chldc++;\n}\n\nstatic\nconst char*\ngltf_treeType(const json_t * __restrict json) {\n  if (!json)\n    return \"null\";\n\n  switch (json->type) {\n    case JSON_OBJECT:\n      return \"object\";\n    case JSON_ARRAY:\n      return \"array\";\n    case JSON_STRING:\n      return \"value\";\n    default:\n      break;\n  }\n\n  return \"unknown\";\n}\n\nstatic\nAkTreeNode*\ngltf_treeFromJson(AkHeap      * __restrict heap,\n                  void        * __restrict owner,\n                  const json_t * __restrict json,\n                  const char  * __restrict name,\n                  size_t                   nameLen) {\n  AkTreeNode *node;\n  json_t     *child;\n\n  node = ak_heap_calloc(heap, owner, sizeof(*node));\n  if (name && nameLen > 0)\n    node->name = ak_heap_strndup(heap, node, name, nameLen);\n  else\n    node->name = ak_heap_strdup(heap, node, \"item\");\n\n  gltf_treeAttr(heap, node, \"type\", gltf_treeType(json));\n\n  if (!json)\n    return node;\n\n  if (json->type == JSON_STRING) {\n    if (json->value && json->valsize > 0)\n      node->val = ak_heap_strndup(heap, node, json->value, json->valsize);\n    return node;\n  }\n\n  if (json->type != JSON_OBJECT && json->type != JSON_ARRAY)\n    return node;\n\n  child = json->value;\n  while (child) {\n    AkTreeNode *tnode;\n\n    if (json->type == JSON_OBJECT) {\n      tnode = gltf_treeFromJson(heap,\n                                node,\n                                child,\n                                child->key,\n                                child->keysize);\n    } else {\n      tnode = gltf_treeFromJson(heap, node, child, \"item\", 4);\n    }\n\n    /* json_parse(..., true) stores children in reverse. Prepend restores\n       source order for arrays while object order remains immaterial. */\n    gltf_treeAppend(node, tnode);\n    child = child->next;\n  }\n\n  return node;\n}\n\nstatic\nAkTreeNode*\ngltf_extraRoot(AkGLTFState * __restrict gst,\n               void        * __restrict owner) {\n  AkHeap     *heap;\n  AkTreeNode *root;\n\n  heap = gst->heap;\n  root = ak_extra(owner);\n  if (!root) {\n    root       = ak_heap_calloc(heap, owner, sizeof(*root));\n    root->name = ak_heap_strdup(heap, root, \"extra\");\n    ak_extra_set(owner, root);\n  }\n\n  return root;\n}\n\nAK_HIDE\nvoid\ngltf_extra_node(AkGLTFState * __restrict gst,\n                void        * __restrict owner,\n                const char  * __restrict name,\n                const json_t * __restrict json) {\n  AkTreeNode *root;\n  AkTreeNode *node;\n\n  if (!gst || !owner || !name || !json)\n    return;\n\n  root = gltf_extraRoot(gst, owner);\n  node = gltf_treeFromJson(gst->heap, root, json, name, strlen(name));\n  gltf_treeAppend(root, node);\n}\n\nAK_HIDE\nvoid\ngltf_extra(AkGLTFState * __restrict gst,\n           void        * __restrict owner,\n           const json_t * __restrict jextras,\n           const json_t * __restrict jextensions) {\n  if (!gst || !owner || (!jextras && !jextensions))\n    return;\n\n  gltf_extra_node(gst, owner, _s_gltf_extensions, jextensions);\n  gltf_extra_node(gst, owner, _s_gltf_extras, jextras);\n}\n"
  },
  {
    "path": "src/io/gltf/imp/extra.h",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n */\n\n#ifndef gltf_imp_extra_h\n#define gltf_imp_extra_h\n\n#include \"common.h\"\n\nAK_HIDE\nvoid\ngltf_extra(AkGLTFState * __restrict gst,\n           void        * __restrict owner,\n           const json_t * __restrict jextras,\n           const json_t * __restrict jextensions);\n\nAK_HIDE\nvoid\ngltf_extra_node(AkGLTFState * __restrict gst,\n                void        * __restrict owner,\n                const char  * __restrict name,\n                const json_t * __restrict json);\n\n#endif /* gltf_imp_extra_h */\n"
  },
  {
    "path": "src/io/gltf/imp/gltf.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../../../../include/ak/path.h\"\n#include \"../../../id.h\"\n#include \"../../../endian.h\"\n#include \"gltf.h\"\n#include \"core/asset.h\"\n#include \"core/buffer.h\"\n#include \"core/accessor.h\"\n#include \"core/mesh.h\"\n#include \"core/node.h\"\n#include \"core/scene.h\"\n#include \"core/camera.h\"\n#include \"core/image.h\"\n#include \"core/profile.h\"\n#include \"core/sampler.h\"\n#include \"core/texture.h\"\n#include \"core/material.h\"\n#include \"core/anim.h\"\n#include \"core/skin.h\"\n#include \"core/ext.h\"\n#include \"extra.h\"\n#include \"postscript.h\"\n\nstatic\nAkResult\ngltf_parse(AkDoc     ** __restrict dest,\n           const char * __restrict filepath,\n           const char * __restrict contents,\n           void       * __restrict bindata,\n           size_t                  bindataLen);\n\nstatic\nvoid\nak_gltfFreeDupl(RBTree *tree, RBNode *node);\n\nAK_HIDE\nAkResult\ngltf_glb(AkDoc     ** __restrict dest,\n         const char * __restrict filepath) {\n  void             *data, *bindata;\n  char             *pdata, *jsonData, *binData;\n  size_t            fileSize, bindataLen;\n  AkResult          ret;\n  uint32_t          magic, version, length, chunkLength, chunkType;\n  uint32_t          buffLen, buffType;\n\n  if ((ret = ak_readfile(filepath, NULL, &data, &fileSize)) != AK_OK)\n    return ret;\n\n  pdata = data;\n  bindata = NULL;\n  bindataLen = 0;\n\n  /* check if the is is glTF */\n  le_32(magic, pdata);\n  if (magic != 0x46546C67) {\n    ret = AK_ERR;\n    goto done;\n  }\n\n  le_32(version,     pdata);\n  le_32(length,      pdata);\n  le_32(chunkLength, pdata);\n  le_32(chunkType ,  pdata);\n\n  if (chunkType != 0x4E4F534A\n      || length > fileSize\n      || chunkLength > fileSize\n      || (size_t)(pdata - (char *)data) > fileSize - chunkLength) {\n    ret = AK_ERR;\n    goto done;\n  }\n\n  jsonData = pdata;\n  binData  = jsonData + chunkLength;\n\n  if ((size_t)(binData - (char *)data) <= fileSize - 8) {\n    le_32(buffLen,  binData);\n    le_32(buffType, binData);\n\n    if (buffType == 0x004E4942\n        && buffLen <= fileSize\n        && (size_t)(binData - (char *)data) <= fileSize - buffLen) {\n      bindata    = binData;\n      bindataLen = buffLen;\n    }\n  }\n\n  /* make the json NULL terminated */\n  /*\n   pdata[chunkLength] = '\\0';\n   */\n\n  ret = gltf_parse(dest, filepath, jsonData, bindata, bindataLen);\n\ndone:\n  if (data) {\n    if (ret == AK_OK && dest && *dest && bindata && ak_opt_get(AK_OPT_USE_MMAP))\n      ak_mmap_attach(*dest, data, fileSize);\n    else\n      ak_releasefile(data, fileSize);\n  }\n\n  return ret;\n}\n\nAK_HIDE\nAkResult\ngltf_gltf(AkDoc     ** __restrict dest,\n          const char * __restrict filepath) {\n  void             *jsonString;\n  size_t            jsonSize;\n  AkResult          ret;\n\n  if ((ret = ak_readfile(filepath, NULL, &jsonString, &jsonSize)) != AK_OK)\n    return ret;\n\n  ret = gltf_parse(dest, filepath, jsonString, NULL, 0);\n\n  if (jsonString)\n    ak_releasefile(jsonString, jsonSize);\n\n  return ret;\n}\n\nstatic\nAkResult\ngltf_parse(AkDoc     ** __restrict dest,\n           const char * __restrict filepath,\n           const char * __restrict contents,\n           void       * __restrict bindata,\n           size_t                  bindataLen) {\n  AkHeap           *heap;\n  AkDoc            *doc;\n  const json_doc_t *gltfRawDoc;\n  json_t           *json;\n  AkGLTFState       gstVal, *gst;\n  AkResult          ret;\n  \n  if (!contents)\n    return AK_ERR;\n\n  ret  = AK_OK;\n  heap = ak_heap_new(NULL, NULL, NULL);\n  doc  = ak_heap_calloc(heap, NULL, sizeof(*doc));\n  \n  doc->inf            = ak_heap_calloc(heap, doc, sizeof(*doc->inf));\n  doc->inf->dir       = ak_path_dir(heap, doc, filepath);\n  doc->inf->name      = filepath;\n  doc->inf->flipImage = false;\n  doc->inf->ftype     = AK_FILE_TYPE_GLTF;\n  \n  /* for fixing skin and morph vertices */\n  doc->reserved = rb_newtree_ptr();\n  ((RBTree *)doc->reserved)->onFreeNode = ak_gltfFreeDupl;\n  \n  if (doc->inf->dir)\n    doc->inf->dirlen = strlen(doc->inf->dir);\n  \n  ak_heap_setdata(heap, doc);\n  ak_id_newheap(heap);\n  \n  memset(&gstVal, 0, sizeof(gstVal));\n  \n  gst              = &gstVal;\n  gstVal.doc       = doc;\n  gstVal.heap      = heap;\n  gstVal.bindata   = bindata;\n  gstVal.bindataLen = bindataLen;\n  gstVal.borrowBufferViews = bindata != NULL && ak_opt_get(AK_OPT_USE_MMAP);\n  gstVal.tmpParent = ak_heap_alloc(heap, doc, sizeof(void*));\n  gst->bufferMap   = rb_newtree_ptr();\n  gst->meshTargets = rb_newtree_ptr();\n  gst->skinBound   = rb_newtree_ptr();\n  gst->skinBound->userData = gst;\n  \n  gltfRawDoc = json_parse(contents, true);\n  if (!gltfRawDoc || !gltfRawDoc->root) {\n    ak_free(doc);\n\n    if (gltfRawDoc)\n      free((void *)gltfRawDoc);\n    return AK_ERR;\n  }\n\n  if (doc->inf->dir)\n    doc->inf->dirlen = strlen(doc->inf->dir);\n\n  json = gltfRawDoc->root;\n  gltf_extra(gst,\n             doc,\n             json_get(json, _s_gltf_extras),\n             json_get(json, _s_gltf_extensions));\n  gltf_extra_node(gst,\n                  doc,\n                  _s_gltf_extensionsUsed,\n                  json_get(json, _s_gltf_extensionsUsed));\n  gltf_extra_node(gst,\n                  doc,\n                  _s_gltf_extensionsRequired,\n                  json_get(json, _s_gltf_extensionsRequired));\n  gltf_ext_root(json_get(json, _s_gltf_extensions), gst);\n\n  /* json_print_human(stderr, gltfRawDoc->root); */\n\n  json_objmap_t gltfMap[] = {\n    JSON_OBJMAP_FN(_s_gltf_asset,       gltf_asset,       gst),\n    JSON_OBJMAP_FN(_s_gltf_buffers,     gltf_buffers,     gst),\n    JSON_OBJMAP_FN(_s_gltf_bufferViews, gltf_bufferViews, gst),\n    JSON_OBJMAP_FN(_s_gltf_accessors,   gltf_accessors,   gst),\n    JSON_OBJMAP_FN(_s_gltf_images,      gltf_images,      gst),\n    JSON_OBJMAP_FN(_s_gltf_samplers,    gltf_samplers,    gst),\n    JSON_OBJMAP_FN(_s_gltf_textures,    gltf_textures,    gst),\n    JSON_OBJMAP_FN(_s_gltf_extensionsRequired, gltf_exts,  gst),\n    JSON_OBJMAP_FN(_s_gltf_materials,   gltf_materials,   gst),\n    JSON_OBJMAP_FN(_s_gltf_meshes,      gltf_meshes,      gst),\n    JSON_OBJMAP_FN(_s_gltf_cameras,     gltf_cameras,     gst),\n    JSON_OBJMAP_FN(_s_gltf_nodes,       gltf_nodes,       gst),\n    JSON_OBJMAP_FN(_s_gltf_skins,       gltf_skin,        gst),\n    JSON_OBJMAP_FN(_s_gltf_scenes,      gltf_scenes,      gst),\n    JSON_OBJMAP_FN(_s_gltf_scene,       gltf_scene,       gst),\n    JSON_OBJMAP_FN(_s_gltf_animations,  gltf_animations,  gst)\n  };\n\n  while (json) {\n    json_objmap_call(json, gltfMap, JSON_ARR_LEN(gltfMap), &gstVal.stop);\n    \n    if (gstVal.stop) {\n      ret = AK_EBADF;\n      goto err;\n    }\n    \n    json = json->next;\n  }\n\nerr:\n\n  if (gltfRawDoc)\n    free((void *)gltfRawDoc);\n\n  gltf_ext_close(gst);\n\n  /* probably unsupportted version or verion is missing */\n  if (ret == AK_EBADF) {\n    ak_free(doc);\n    return ret;\n  }\n\n\n  /* TODO: release resources in GLTFState */\n  \n  /* set first scene as default scene if not specified  */\n  if (!doc->scene.visualScene) {\n    if (doc->lib.visualScenes->chld) {\n      AkInstanceBase *instScene;\n      instScene = ak_heap_calloc(heap, doc, sizeof(*instScene));\n      \n      instScene->url.ptr     = doc->lib.visualScenes->chld;\n      doc->scene.visualScene = instScene;\n    }\n  }\n\n  *dest = doc;\n\n  /* post-parse operations */\n  gltf_postscript(gst);\n  \n  ak_free(gstVal.tmpParent);\n\n  rb_destroy(gst->bufferMap);\n  rb_destroy(gst->meshTargets);\n  rb_destroy(gst->skinBound);\n\n  return ret;\n}\n\nstatic\nvoid\nak_gltfFreeDupl(RBTree *tree, RBNode *node) {\n  if (node == tree->nullNode)\n    return;\n  ak_free(node->val);\n}\n"
  },
  {
    "path": "src/io/gltf/imp/gltf.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_h\n#define gltf_imp_h\n\n#include \"common.h\"\n\nAK_HIDE\nAkResult\ngltf_gltf(AkDoc     ** __restrict dest,\n          const char * __restrict filepath);\n\nAK_HIDE\nAkResult\ngltf_glb(AkDoc     ** __restrict dest,\n         const char * __restrict filepath);\n\n#endif /* gltf_imp_h */\n"
  },
  {
    "path": "src/io/gltf/imp/mesh_fixup.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"mesh_fixup.h\"\n#include \"../../../mesh/index.h\"\n#include \"../../../topo/topo.h\"\n\nAK_HIDE\nvoid\ngltf_mesh_fixup(AkGLTFState * __restrict gst) {\n  AkDoc      *doc;\n  AkLibrary  *geomLib;\n  AkGeometry *geom;\n\n  doc = gst->doc;\n\n  geomLib = doc->lib.geometries;\n  while (geomLib) {\n    geom = (void *)geomLib->chld;\n    while (geom) {\n      AkObject *primitive;\n\n      primitive = geom->gdata;\n      switch ((AkGeometryType)primitive->type) {\n        case AK_GEOMETRY_MESH: {\n          AkMesh *mesh;\n          mesh = ak_objGet(primitive);\n\n          topofix(mesh);\n\n          /* first fixup coord system because verts will be duplicated,\n             reduce extra process */\n          if (ak_opt_get(AK_OPT_COORD_CONVERT_TYPE) == AK_COORD_CVT_ALL\n              && (void *)ak_opt_get(AK_OPT_COORD) != doc->coordSys)\n            ak_changeCoordSysMesh(mesh, (void *)ak_opt_get(AK_OPT_COORD));\n\n          if (ak_opt_get(AK_OPT_COMPUTE_BBOX))\n            ak_bbox_mesh(mesh);\n\n          if (ak_opt_get(AK_OPT_GEN_NORMALS_IF_NEEDED))\n            if (ak_meshNeedsNormals(mesh))\n              ak_meshGenNormals(mesh);\n        }\n        default:\n          break;\n      }\n      geom = (void *)geom->base.next;\n    }\n\n    geomLib = geomLib->next;\n  }\n }\n"
  },
  {
    "path": "src/io/gltf/imp/mesh_fixup.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_mesh_fixup_h\n#define gltf_imp_mesh_fixup_h\n\n#include \"common.h\"\n\nAK_HIDE\nvoid\ngltf_mesh_fixup(AkGLTFState * __restrict gst);\n\n#endif /* gltf_imp_mesh_fixup_h */\n"
  },
  {
    "path": "src/io/gltf/imp/postscript.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"postscript.h\"\n#include \"mesh_fixup.h\"\n#include \"ak/coord-util.h\"\n\nAK_HIDE\nvoid\ngltf_setskinners(RBTree *tree, RBNode *rbnode);\n\nAK_HIDE\nvoid\ngltf_postscript(AkGLTFState * __restrict gst) {\n  AkCoordCvtType coordCvtType;\n  AkCoordSys    *sourceCoordSys, *targetCoordSys;\n  AkVisualScene *vscn;\n  bool           fixTransform;\n\n  coordCvtType   = (AkCoordCvtType)ak_opt_get(AK_OPT_COORD_CONVERT_TYPE);\n  sourceCoordSys = gst->doc ? gst->doc->coordSys : NULL;\n  targetCoordSys = (void *)ak_opt_get(AK_OPT_COORD);\n  fixTransform   = coordCvtType == AK_COORD_CVT_FIX_TRANSFORM\n                   && sourceCoordSys\n                   && targetCoordSys\n                   && sourceCoordSys != targetCoordSys\n                   && !ak_coordOrientationIsEq(sourceCoordSys, targetCoordSys);\n\n  gltf_mesh_fixup(gst);\n\n  if (coordCvtType != AK_COORD_CVT_DISABLED)\n    gst->doc->coordSys = targetCoordSys;\n\n  if (gst->doc && gst->doc->lib.visualScenes) {\n    for (vscn = (void *)gst->doc->lib.visualScenes->chld;\n         vscn;\n         vscn = (void *)vscn->base.next) {\n      if (fixTransform)\n        ak_fixSceneCoordSys(vscn);\n    }\n  }\n\n  rb_walk(gst->skinBound, gltf_setskinners);\n}\n\nAK_HIDE\nvoid\ngltf_setskinners(RBTree *tree, RBNode *rbnode) {\n  char                skinid[16];\n  AkGLTFState        *gst;\n  AkInstanceSkin     *skinner;\n  AkNode             *node;\n  AkInstanceGeometry *instGeom;\n  int32_t             i32val;\n\n  gst               = tree->userData;\n  node              = rbnode->key;\n  instGeom          = node->geometry;\n  i32val            = (int32_t)(intptr_t)rbnode->val;\n  \n  sprintf(skinid, \"%s%d\", _s_gltf_skin, i32val);\n  \n  skinner           = ak_heap_calloc(gst->heap, node, sizeof(*skinner));\n  skinner->skin     = ak_getObjectById(gst->doc, skinid);\n  instGeom->skinner = skinner;\n}\n"
  },
  {
    "path": "src/io/gltf/imp/postscript.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_imp_postscript_h\n#define gltf_imp_postscript_h\n\n#include \"common.h\"\n\nAK_HIDE\nvoid\ngltf_postscript(AkGLTFState * __restrict gst);\n\n#endif /* gltf_imp_postscript_h */\n"
  },
  {
    "path": "src/io/gltf/strpool.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _GLTF_STRPOOL_\n#  define _GLTF_STRPOOL_\n#endif\n\n#include \"strpool.h\"\n#include <string.h>\n\nconst char _s_gltf_pool_0[] =\n\" \\0\"\n\"gltf\\0\"\n\"asset\\0\"\n\"version\\0\"\n\"generator\\0\"\n\"copyright\\0\"\n\"meter\\0\"\n\"buffers\\0\"\n\"uri\\0\"\n\"byteLength\\0\"\n\"bufferViews\\0\"\n\"buffer\\0\"\n\"byteOffset\\0\"\n\"byteStride\\0\"\n\"bufferView\\0\"\n\"componentType\\0\"\n\"normalized\\0\"\n\"count\\0\"\n\"type\\0\"\n\"max\\0\"\n\"min\\0\"\n\"values\\0\"\n\"meshes\\0\"\n\"primitives\\0\"\n\"attributes\\0\"\n\"indices\\0\"\n\"mode\\0\"\n\"nodes\\0\"\n\"node\\0\"\n\"mesh\\0\"\n\"scene\\0\"\n\"scenes\\0\"\n\"name\\0\"\n\"accessors\\0\"\n\"POSITION\\0\"\n\"NORMAL\\0\"\n\"TANGENT\\0\"\n\"TEXCOORD\\0\"\n\"COLOR\\0\"\n\"JOINTS\\0\"\n\"WEIGHTS\\0\"\n\"SCALAR\\0\"\n\"VEC2\\0\"\n\"VEC3\\0\"\n\"VEC4\\0\"\n\"MAT2\\0\"\n\"MAT3\\0\"\n\"MAT4\\0\"\n\"children\\0\"\n\"matrix\\0\"\n\"rotation\\0\"\n\"scale\\0\"\n\"translation\\0\"\n\"weights\\0\"\n\"extensions\\0\"\n\"extensionsUsed\\0\"\n\"extensionsRequired\\0\"\n\"extras\\0\"\n\"skin\\0\"\n\"morph\\0\"\n\"skins\\0\"\n\"camera\\0\"\n\"cameras\\0\"\n\"light\\0\"\n\"lights\\0\"\n\"visible\\0\"\n\"color\\0\"\n\"intensity\\0\"\n\"range\\0\"\n\"spot\\0\"\n\"innerConeAngle\\0\"\n\"outerConeAngle\\0\"\n\"directional\\0\"\n\"point\\0\"\n\"perspective\\0\"\n\"orthographic\\0\"\n\"aspectRatio\\0\"\n\"xfov\\0\"\n\"yfov\\0\"\n\"zfar\\0\"\n\"znear\\0\"\n\"xmag\\0\"\n\"ymag\\0\"\n\"ak_flg\\0\"\n\"VERTEX\\0\"\n\"images\\0\"\n\"image\\0\"\n\"sampler\\0\"\n\"samplers\\0\"\n\"wrapS\\0\"\n\"wrapT\\0\"\n\"magFilter\\0\"\n\"minFilter\\0\"\n\"textures\\0\"\n\"source\\0\"\n\"material\\0\"\n\"materials\\0\"\n\"pbrMetallicRoughness\\0\"\n\"normalTexture\\0\"\n\"occlusionTexture\\0\"\n\"strength\\0\"\n\"emissiveTexture\\0\"\n\"emissiveFactor\\0\"\n\"alphaMode\\0\"\n\"alphaCutoff\\0\"\n\"doubleSided\\0\"\n\"baseColorFactor\\0\"\n\"baseColorTexture\\0\"\n\"metallicFactor\\0\"\n\"roughnessFactor\\0\"\n\"metallicRoughnessTexture\\0\"\n\"index\\0\"\n\"texCoord\\0\"\n\"TEXCOORD\\0\"\n\"smp\\0\"\n\"specgloss\\0\"\n\"KHR_materials_pbrSpecularGlossiness\\0\"\n\"KHR_materials_specular\\0\"\n\"KHR_materials_ior\\0\"\n\"specularFactor\\0\"\n\"specularTexture\\0\"\n\"specularColorFactor\\0\"\n\"specularColorTexture\\0\"\n\"diffuseFactor\\0\"\n\"specularFactor\\0\"\n\"diffuseTexture\\0\"\n\"glossinessFactor\\0\"\n\"specularGlossinessTexture\\0\"\n\"BLEND\\0\"\n\"MASK\\0\"\n\"OPAQUE\\0\"\n\"animations\\0\"\n\"channels\\0\"\n\"target\\0\"\n\"targets\\0\"\n\"targetNames\\0\"\n\"morphPresets\\0\"\n\"path\\0\"\n\"pointer\\0\"\n\"interpolation\\0\"\n\"input\\0\"\n\"output\\0\"\n\"anim\\0\"\n\"LINEAR\\0\"\n\"STEP\\0\"\n\"CUBICSPLINE\\0\"\n\"inverseBindMatrices\\0\"\n\"joints\\0\"\n\"skeleton\\0\"\n\"sparse\\0\"\n\"data:\\0\"\n\"offset\\0\"\n\"fallback\\0\"\n\"filter\\0\"\n\"ATTRIBUTES\\0\"\n\"TRIANGLES\\0\"\n\"INDICES\\0\"\n\"NONE\\0\"\n\"OCTAHEDRAL\\0\"\n\"QUATERNION\\0\"\n\"EXPONENTIAL\\0\"\n\"KHR_texture_transform\\0\"\n\"KHR_animation_pointer\\0\"\n\"KHR_node_visibility\\0\"\n\"KHR_draco_mesh_compression\\0\"\n\"KHR_mesh_quantization\\0\"\n\"KHR_meshopt_compression\\0\"\n\"EXT_meshopt_compression\\0\"\n\"EXT_mesh_gpu_instancing\\0\"\n\"EXT_texture_webp\\0\"\n\"EXT_lights_image_based\\0\"\n\"EXT_lights_ies\\0\"\n\"EXT_mesh_manifold\\0\"\n\"EXT_mesh_primitive_restart\\0\"\n\"EXT_texture_astc\\0\"\n\"EXT_gsplat_compression_spz\\0\"\n\"ADOBE_materials_clearcoat_specular\\0\"\n\"ADOBE_materials_clearcoat_tint\\0\"\n\"ADOBE_materials_thin_transparency\\0\"\n\"AGI_articulations\\0\"\n\"AGI_stk_metadata\\0\"\n\"CESIUM_primitive_outline\\0\"\n\"FB_geometry_metadata\\0\"\n;\n\nconst char _s_gltf_pool_1[] =\n\"GODOT_single_root\\0\"\n\"GRIFFEL_bim_data\\0\"\n\"MPEG_accessor_timed\\0\"\n\"MPEG_animation_timing\\0\"\n\"MPEG_audio_spatial\\0\"\n\"MPEG_buffer_circular\\0\"\n\"MPEG_media\\0\"\n\"MPEG_mesh_linking\\0\"\n\"MPEG_scene_dynamic\\0\"\n\"MPEG_texture_video\\0\"\n\"MPEG_viewport_recommended\\0\"\n\"MSFT_lod\\0\"\n\"MSFT_packing_normalRoughnessMetallic\\0\"\n\"MSFT_packing_occlusionRoughnessMetallic\\0\"\n\"MSFT_texture_dds\\0\"\n\"NV_materials_mdl\\0\"\n\"KHR_lights_punctual\\0\"\n\"KHR_materials_variants\\0\"\n\"KHR_texture_basisu\\0\"\n\"KHR_techniques_webgl\\0\"\n\"KHR_xmp\\0\"\n\"KHR_xmp_json_ld\\0\"\n\"KHR_gaussian_splatting\\0\"\n\"packet\\0\"\n\"packets\\0\"\n\"kernel\\0\"\n\"colorSpace\\0\"\n\"projection\\0\"\n\"sortingMethod\\0\"\n\"compression\\0\"\n\"format\\0\"\n\"spz\\0\"\n\"variants\\0\"\n\"mappings\\0\"\n\"variant\\0\"\n\"KHR_materials_clearcoat\\0\"\n\"clearcoatFactor\\0\"\n\"clearcoatTexture\\0\"\n\"clearcoatRoughnessFactor\\0\"\n\"clearcoatRoughnessTexture\\0\"\n\"clearcoatNormalTexture\\0\"\n\"KHR_materials_ior\\0\"\n\"ior\\0\"\n\"KHR_materials_unlit\\0\"\n\"KHR_materials_emissive_strength\\0\"\n\"emissiveStrength\\0\"\n\"KHR_materials_transmission\\0\"\n\"transmissionFactor\\0\"\n\"transmissionTexture\\0\"\n\"KHR_materials_sheen\\0\"\n\"sheenColorFactor\\0\"\n\"sheenColorTexture\\0\"\n\"sheenRoughnessFactor\\0\"\n\"sheenRoughnessTexture\\0\"\n\"KHR_materials_iridescence\\0\"\n\"iridescenceFactor\\0\"\n\"iridescenceTexture\\0\"\n\"iridescenceIor\\0\"\n\"iridescenceThicknessMinimum\\0\"\n\"iridescenceThicknessMaximum\\0\"\n\"iridescenceThicknessTexture\\0\"\n\"KHR_materials_volume\\0\"\n\"thicknessFactor\\0\"\n\"thicknessTexture\\0\"\n\"attenuationDistance\\0\"\n\"attenuationColor\\0\"\n\"KHR_materials_anisotropy\\0\"\n\"anisotropyStrength\\0\"\n\"anisotropyRotation\\0\"\n\"anisotropyTexture\\0\"\n\"KHR_materials_dispersion\\0\"\n\"dispersion\\0\"\n\"KHR_materials_diffuse_transmission\\0\"\n\"diffuseTransmissionFactor\\0\"\n\"diffuseTransmissionTexture\\0\"\n\"diffuseTransmissionColorFactor\\0\"\n\"diffuseTransmissionColorTexture\\0\"\n;\n\n#undef _GLTF_STRPOOL_\n"
  },
  {
    "path": "src/io/gltf/strpool.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef gltf_strpool_h\n#  define gltf_strpool_h\n\n#ifndef _GLTF_STRPOOL_\n#  define _AK_EXTERN extern\n#else\n#  define _AK_EXTERN\n#endif\n\n_AK_EXTERN const char _s_gltf_pool_0[];\n_AK_EXTERN const char _s_gltf_pool_1[];\n\n#define _s_gltf_0(x) (_s_gltf_pool_0 + x)\n#define _s_gltf_1(x) (_s_gltf_pool_1 + x)\n\n/* _s_gltf_pool_0 */\n#define _s_gltf_space _s_gltf_0(0)\n#define _s_gltf_gltf _s_gltf_0(2)\n#define _s_gltf_asset _s_gltf_0(7)\n#define _s_gltf_version _s_gltf_0(13)\n#define _s_gltf_generator _s_gltf_0(21)\n#define _s_gltf_copyright _s_gltf_0(31)\n#define _s_gltf_meter _s_gltf_0(41)\n#define _s_gltf_buffers _s_gltf_0(47)\n#define _s_gltf_uri _s_gltf_0(55)\n#define _s_gltf_byteLength _s_gltf_0(59)\n#define _s_gltf_bufferViews _s_gltf_0(70)\n#define _s_gltf_buffer _s_gltf_0(82)\n#define _s_gltf_byteOffset _s_gltf_0(89)\n#define _s_gltf_byteStride _s_gltf_0(100)\n#define _s_gltf_bufferView _s_gltf_0(111)\n#define _s_gltf_componentType _s_gltf_0(122)\n#define _s_gltf_normalized _s_gltf_0(136)\n#define _s_gltf_count _s_gltf_0(147)\n#define _s_gltf_type _s_gltf_0(153)\n#define _s_gltf_max _s_gltf_0(158)\n#define _s_gltf_min _s_gltf_0(162)\n#define _s_gltf_values _s_gltf_0(166)\n#define _s_gltf_meshes _s_gltf_0(173)\n#define _s_gltf_primitives _s_gltf_0(180)\n#define _s_gltf_attributes _s_gltf_0(191)\n#define _s_gltf_indices _s_gltf_0(202)\n#define _s_gltf_mode _s_gltf_0(210)\n#define _s_gltf_nodes _s_gltf_0(215)\n#define _s_gltf_node _s_gltf_0(221)\n#define _s_gltf_mesh _s_gltf_0(226)\n#define _s_gltf_scene _s_gltf_0(231)\n#define _s_gltf_scenes _s_gltf_0(237)\n#define _s_gltf_name _s_gltf_0(244)\n#define _s_gltf_accessors _s_gltf_0(249)\n#define _s_gltf_POSITION _s_gltf_0(259)\n#define _s_gltf_NORMAL _s_gltf_0(268)\n#define _s_gltf_TANGENT _s_gltf_0(275)\n#define _s_gltf_TEXCOORD _s_gltf_0(283)\n#define _s_gltf_COLOR _s_gltf_0(292)\n#define _s_gltf_JOINTS _s_gltf_0(298)\n#define _s_gltf_WEIGHTS _s_gltf_0(305)\n#define _s_gltf_SCALAR _s_gltf_0(313)\n#define _s_gltf_VEC2 _s_gltf_0(320)\n#define _s_gltf_VEC3 _s_gltf_0(325)\n#define _s_gltf_VEC4 _s_gltf_0(330)\n#define _s_gltf_MAT2 _s_gltf_0(335)\n#define _s_gltf_MAT3 _s_gltf_0(340)\n#define _s_gltf_MAT4 _s_gltf_0(345)\n#define _s_gltf_children _s_gltf_0(350)\n#define _s_gltf_matrix _s_gltf_0(359)\n#define _s_gltf_rotation _s_gltf_0(366)\n#define _s_gltf_scale _s_gltf_0(375)\n#define _s_gltf_translation _s_gltf_0(381)\n#define _s_gltf_weights _s_gltf_0(393)\n#define _s_gltf_extensions _s_gltf_0(401)\n#define _s_gltf_extensionsUsed _s_gltf_0(412)\n#define _s_gltf_extensionsRequired _s_gltf_0(427)\n#define _s_gltf_extras _s_gltf_0(446)\n#define _s_gltf_skin _s_gltf_0(453)\n#define _s_gltf_morph _s_gltf_0(458)\n#define _s_gltf_skins _s_gltf_0(464)\n#define _s_gltf_camera _s_gltf_0(470)\n#define _s_gltf_cameras _s_gltf_0(477)\n#define _s_gltf_light _s_gltf_0(485)\n#define _s_gltf_lights _s_gltf_0(491)\n#define _s_gltf_visible _s_gltf_0(498)\n#define _s_gltf_color _s_gltf_0(506)\n#define _s_gltf_intensity _s_gltf_0(512)\n#define _s_gltf_range _s_gltf_0(522)\n#define _s_gltf_spot _s_gltf_0(528)\n#define _s_gltf_innerConeAngle _s_gltf_0(533)\n#define _s_gltf_outerConeAngle _s_gltf_0(548)\n#define _s_gltf_directional _s_gltf_0(563)\n#define _s_gltf_point _s_gltf_0(575)\n#define _s_gltf_perspective _s_gltf_0(581)\n#define _s_gltf_orthographic _s_gltf_0(593)\n#define _s_gltf_aspectRatio _s_gltf_0(606)\n#define _s_gltf_xfov _s_gltf_0(618)\n#define _s_gltf_yfov _s_gltf_0(623)\n#define _s_gltf_zfar _s_gltf_0(628)\n#define _s_gltf_znear _s_gltf_0(633)\n#define _s_gltf_xmag _s_gltf_0(639)\n#define _s_gltf_ymag _s_gltf_0(644)\n#define _s_gltf_ak_flg _s_gltf_0(649)\n#define _s_gltf_VERTEX _s_gltf_0(656)\n#define _s_gltf_images _s_gltf_0(663)\n#define _s_gltf_image _s_gltf_0(670)\n#define _s_gltf_sampler _s_gltf_0(676)\n#define _s_gltf_samplers _s_gltf_0(684)\n#define _s_gltf_wrapS _s_gltf_0(693)\n#define _s_gltf_wrapT _s_gltf_0(699)\n#define _s_gltf_magFilter _s_gltf_0(705)\n#define _s_gltf_minFilter _s_gltf_0(715)\n#define _s_gltf_textures _s_gltf_0(725)\n#define _s_gltf_source _s_gltf_0(734)\n#define _s_gltf_material _s_gltf_0(741)\n#define _s_gltf_materials _s_gltf_0(750)\n#define _s_gltf_pbrMetalRough _s_gltf_0(760)\n#define _s_gltf_normalTex _s_gltf_0(781)\n#define _s_gltf_occlusionTex _s_gltf_0(795)\n#define _s_gltf_strength _s_gltf_0(812)\n#define _s_gltf_emissiveTex _s_gltf_0(821)\n#define _s_gltf_emissiveFac _s_gltf_0(837)\n#define _s_gltf_alphaMode _s_gltf_0(852)\n#define _s_gltf_alphaCutoff _s_gltf_0(862)\n#define _s_gltf_doubleSided _s_gltf_0(874)\n#define _s_gltf_baseColor _s_gltf_0(886)\n#define _s_gltf_baseColorTex _s_gltf_0(902)\n#define _s_gltf_metalFac _s_gltf_0(919)\n#define _s_gltf_roughFac _s_gltf_0(934)\n#define _s_gltf_metalRoughTex _s_gltf_0(950)\n#define _s_gltf_index _s_gltf_0(975)\n#define _s_gltf_texCoord _s_gltf_0(981)\n#define _s_gltf_texcoordPrefix _s_gltf_0(990)\n#define _s_gltf_sid_sampler _s_gltf_0(999)\n#define _s_gltf_id_specgloss _s_gltf_0(1003)\n#define _s_gltf_KHR_materials_pbrSpecularGlossiness _s_gltf_0(1013)\n#define _s_gltf_KHR_materials_specular _s_gltf_0(1049)\n#define _s_gltf_ext_KHR_materials_ior _s_gltf_0(1072)\n#define _s_gltf_specularFactor _s_gltf_0(1090)\n#define _s_gltf_specularTexture _s_gltf_0(1105)\n#define _s_gltf_specularColorFactor _s_gltf_0(1121)\n#define _s_gltf_specularColorTexture _s_gltf_0(1141)\n#define _s_gltf_diffuseFactor _s_gltf_0(1162)\n#define _s_gltf_specFactor _s_gltf_0(1176)\n#define _s_gltf_diffuseTexture _s_gltf_0(1191)\n#define _s_gltf_glossFactor _s_gltf_0(1206)\n#define _s_gltf_specGlossTex _s_gltf_0(1223)\n#define _s_gltf_BLEND _s_gltf_0(1249)\n#define _s_gltf_MASK _s_gltf_0(1255)\n#define _s_gltf_OPAQUE _s_gltf_0(1260)\n#define _s_gltf_animations _s_gltf_0(1267)\n#define _s_gltf_channels _s_gltf_0(1278)\n#define _s_gltf_target _s_gltf_0(1287)\n#define _s_gltf_targets _s_gltf_0(1294)\n#define _s_gltf_targetNames _s_gltf_0(1302)\n#define _s_gltf_morphPresets _s_gltf_0(1314)\n#define _s_gltf_path _s_gltf_0(1327)\n#define _s_gltf_pointer _s_gltf_0(1332)\n#define _s_gltf_interpolation _s_gltf_0(1340)\n#define _s_gltf_input _s_gltf_0(1354)\n#define _s_gltf_output _s_gltf_0(1360)\n#define _s_gltf_anim _s_gltf_0(1367)\n#define _s_gltf_LINEAR _s_gltf_0(1372)\n#define _s_gltf_STEP _s_gltf_0(1379)\n#define _s_gltf_CUBICSPLINE _s_gltf_0(1384)\n#define _s_gltf_inverseBindMatrices _s_gltf_0(1396)\n#define _s_gltf_joints _s_gltf_0(1416)\n#define _s_gltf_skeleton _s_gltf_0(1423)\n#define _s_gltf_sparse _s_gltf_0(1432)\n#define _s_gltf_b64d _s_gltf_0(1439)\n#define _s_gltf_offset _s_gltf_0(1445)\n#define _s_gltf_fallback _s_gltf_0(1452)\n#define _s_gltf_filter _s_gltf_0(1461)\n#define _s_gltf_ATTRIBUTES _s_gltf_0(1468)\n#define _s_gltf_TRIANGLES _s_gltf_0(1479)\n#define _s_gltf_INDICES _s_gltf_0(1489)\n#define _s_gltf_NONE _s_gltf_0(1497)\n#define _s_gltf_OCTAHEDRAL _s_gltf_0(1502)\n#define _s_gltf_QUATERNION _s_gltf_0(1513)\n#define _s_gltf_EXPONENTIAL _s_gltf_0(1524)\n#define _s_gltf_KHR_texture_transform _s_gltf_0(1536)\n#define _s_gltf_KHR_animation_pointer _s_gltf_0(1558)\n#define _s_gltf_KHR_node_visibility _s_gltf_0(1580)\n#define _s_gltf_KHR_draco_mesh_compression _s_gltf_0(1600)\n#define _s_gltf_KHR_mesh_quantization _s_gltf_0(1627)\n#define _s_gltf_KHR_meshopt_compression _s_gltf_0(1649)\n#define _s_gltf_EXT_meshopt_compression _s_gltf_0(1673)\n#define _s_gltf_EXT_mesh_gpu_instancing _s_gltf_0(1697)\n#define _s_gltf_EXT_texture_webp _s_gltf_0(1721)\n#define _s_gltf_EXT_lights_image_based _s_gltf_0(1738)\n#define _s_gltf_EXT_lights_ies _s_gltf_0(1761)\n#define _s_gltf_EXT_mesh_manifold _s_gltf_0(1776)\n#define _s_gltf_EXT_mesh_primitive_restart _s_gltf_0(1794)\n#define _s_gltf_EXT_texture_astc _s_gltf_0(1821)\n#define _s_gltf_EXT_gsplat_compression_spz _s_gltf_0(1838)\n#define _s_gltf_ADOBE_materials_clearcoat_specular _s_gltf_0(1865)\n#define _s_gltf_ADOBE_materials_clearcoat_tint _s_gltf_0(1900)\n#define _s_gltf_ADOBE_materials_thin_transparency _s_gltf_0(1931)\n#define _s_gltf_AGI_articulations _s_gltf_0(1965)\n#define _s_gltf_AGI_stk_metadata _s_gltf_0(1983)\n#define _s_gltf_CESIUM_primitive_outline _s_gltf_0(2000)\n#define _s_gltf_FB_geometry_metadata _s_gltf_0(2025)\n\n/* _s_gltf_pool_1 */\n#define _s_gltf_GODOT_single_root _s_gltf_1(0)\n#define _s_gltf_GRIFFEL_bim_data _s_gltf_1(18)\n#define _s_gltf_MPEG_accessor_timed _s_gltf_1(35)\n#define _s_gltf_MPEG_animation_timing _s_gltf_1(55)\n#define _s_gltf_MPEG_audio_spatial _s_gltf_1(77)\n#define _s_gltf_MPEG_buffer_circular _s_gltf_1(96)\n#define _s_gltf_MPEG_media _s_gltf_1(117)\n#define _s_gltf_MPEG_mesh_linking _s_gltf_1(128)\n#define _s_gltf_MPEG_scene_dynamic _s_gltf_1(146)\n#define _s_gltf_MPEG_texture_video _s_gltf_1(165)\n#define _s_gltf_MPEG_viewport_recommended _s_gltf_1(184)\n#define _s_gltf_MSFT_lod _s_gltf_1(210)\n#define _s_gltf_MSFT_packing_normalRoughnessMetallic _s_gltf_1(219)\n#define _s_gltf_MSFT_packing_occlusionRoughnessMetallic _s_gltf_1(256)\n#define _s_gltf_MSFT_texture_dds _s_gltf_1(296)\n#define _s_gltf_NV_materials_mdl _s_gltf_1(313)\n#define _s_gltf_KHR_lights_punctual _s_gltf_1(330)\n#define _s_gltf_KHR_materials_variants _s_gltf_1(350)\n#define _s_gltf_KHR_texture_basisu _s_gltf_1(373)\n#define _s_gltf_KHR_techniques_webgl _s_gltf_1(392)\n#define _s_gltf_KHR_xmp _s_gltf_1(413)\n#define _s_gltf_KHR_xmp_json_ld _s_gltf_1(421)\n#define _s_gltf_KHR_gaussian_splatting _s_gltf_1(437)\n#define _s_gltf_packet _s_gltf_1(460)\n#define _s_gltf_packets _s_gltf_1(467)\n#define _s_gltf_kernel _s_gltf_1(475)\n#define _s_gltf_colorSpace _s_gltf_1(482)\n#define _s_gltf_projection _s_gltf_1(493)\n#define _s_gltf_sortingMethod _s_gltf_1(504)\n#define _s_gltf_compression _s_gltf_1(518)\n#define _s_gltf_format _s_gltf_1(530)\n#define _s_gltf_spz _s_gltf_1(537)\n#define _s_gltf_variants _s_gltf_1(541)\n#define _s_gltf_mappings _s_gltf_1(550)\n#define _s_gltf_variant _s_gltf_1(559)\n#define _s_gltf_KHR_materials_clearcoat _s_gltf_1(567)\n#define _s_gltf_clearcoatFactor _s_gltf_1(591)\n#define _s_gltf_clearcoatTexture _s_gltf_1(607)\n#define _s_gltf_clearcoatRoughnessFactor _s_gltf_1(624)\n#define _s_gltf_clearcoatRoughnessTexture _s_gltf_1(649)\n#define _s_gltf_clearcoatNormalTexture _s_gltf_1(675)\n#define _s_gltf_KHR_materials_ior _s_gltf_1(698)\n#define _s_gltf_ior _s_gltf_1(716)\n#define _s_gltf_KHR_materials_unlit _s_gltf_1(720)\n#define _s_gltf_KHR_materials_emissive_strength _s_gltf_1(740)\n#define _s_gltf_emissiveStrength _s_gltf_1(772)\n#define _s_gltf_KHR_materials_transmission _s_gltf_1(789)\n#define _s_gltf_transmissionFactor _s_gltf_1(816)\n#define _s_gltf_transmissionTexture _s_gltf_1(835)\n#define _s_gltf_KHR_materials_sheen _s_gltf_1(855)\n#define _s_gltf_sheenColorFactor _s_gltf_1(875)\n#define _s_gltf_sheenColorTexture _s_gltf_1(892)\n#define _s_gltf_sheenRoughnessFactor _s_gltf_1(910)\n#define _s_gltf_sheenRoughnessTexture _s_gltf_1(931)\n#define _s_gltf_KHR_materials_iridescence _s_gltf_1(953)\n#define _s_gltf_iridescenceFactor _s_gltf_1(979)\n#define _s_gltf_iridescenceTexture _s_gltf_1(997)\n#define _s_gltf_iridescenceIor _s_gltf_1(1016)\n#define _s_gltf_iridescenceThicknessMinimum _s_gltf_1(1031)\n#define _s_gltf_iridescenceThicknessMaximum _s_gltf_1(1059)\n#define _s_gltf_iridescenceThicknessTexture _s_gltf_1(1087)\n#define _s_gltf_KHR_materials_volume _s_gltf_1(1115)\n#define _s_gltf_thicknessFactor _s_gltf_1(1136)\n#define _s_gltf_thicknessTexture _s_gltf_1(1152)\n#define _s_gltf_attenuationDistance _s_gltf_1(1169)\n#define _s_gltf_attenuationColor _s_gltf_1(1189)\n#define _s_gltf_KHR_materials_anisotropy _s_gltf_1(1206)\n#define _s_gltf_anisotropyStrength _s_gltf_1(1231)\n#define _s_gltf_anisotropyRotation _s_gltf_1(1250)\n#define _s_gltf_anisotropyTexture _s_gltf_1(1269)\n#define _s_gltf_KHR_materials_dispersion _s_gltf_1(1287)\n#define _s_gltf_dispersion _s_gltf_1(1312)\n#define _s_gltf_KHR_materials_diffuse_transmission _s_gltf_1(1323)\n#define _s_gltf_diffuseTransmissionFactor _s_gltf_1(1358)\n#define _s_gltf_diffuseTransmissionTexture _s_gltf_1(1384)\n#define _s_gltf_diffuseTransmissionColorFactor _s_gltf_1(1411)\n#define _s_gltf_diffuseTransmissionColorTexture _s_gltf_1(1442)\n\n#endif /* gltf_strpool_h */\n"
  },
  {
    "path": "src/io/gltf/strpool.json",
    "content": "{\n  \"space\": \" \",\n  \"gltf\": \"gltf\",\n  \"asset\": \"asset\",\n  \"version\": \"version\",\n  \"generator\": \"generator\",\n  \"copyright\": \"copyright\",\n  \"meter\": \"meter\",\n  \"buffers\": \"buffers\",\n  \"uri\": \"uri\",\n  \"byteLength\": \"byteLength\",\n  \"bufferViews\": \"bufferViews\",\n  \"buffer\": \"buffer\",\n  \"byteOffset\": \"byteOffset\",\n  \"byteStride\": \"byteStride\",\n  \"bufferView\": \"bufferView\",\n  \"componentType\": \"componentType\",\n  \"normalized\": \"normalized\",\n  \"count\": \"count\",\n  \"type\": \"type\",\n  \"max\": \"max\",\n  \"min\": \"min\",\n  \"values\": \"values\",\n  \"meshes\": \"meshes\",\n  \"primitives\": \"primitives\",\n  \"attributes\": \"attributes\",\n  \"indices\": \"indices\",\n  \"mode\": \"mode\",\n  \"nodes\": \"nodes\",\n  \"node\": \"node\",\n  \"mesh\": \"mesh\",\n  \"scene\": \"scene\",\n  \"scenes\": \"scenes\",\n  \"name\": \"name\",\n  \"accessors\": \"accessors\",\n  \"POSITION\": \"POSITION\",\n  \"NORMAL\": \"NORMAL\",\n  \"TANGENT\": \"TANGENT\",\n  \"TEXCOORD\": \"TEXCOORD\",\n  \"COLOR\": \"COLOR\",\n  \"JOINTS\": \"JOINTS\",\n  \"WEIGHTS\": \"WEIGHTS\",\n  \"SCALAR\": \"SCALAR\",\n  \"VEC2\": \"VEC2\",\n  \"VEC3\": \"VEC3\",\n  \"VEC4\": \"VEC4\",\n  \"MAT2\": \"MAT2\",\n  \"MAT3\": \"MAT3\",\n  \"MAT4\": \"MAT4\",\n  \"children\": \"children\",\n  \"matrix\": \"matrix\",\n  \"rotation\": \"rotation\",\n  \"scale\": \"scale\",\n  \"translation\": \"translation\",\n  \"weights\": \"weights\",\n  \"extensions\": \"extensions\",\n  \"extensionsUsed\": \"extensionsUsed\",\n  \"extensionsRequired\": \"extensionsRequired\",\n  \"extras\": \"extras\",\n  \"skin\": \"skin\",\n  \"morph\": \"morph\",\n  \"skins\": \"skins\",\n  \"camera\": \"camera\",\n  \"cameras\": \"cameras\",\n  \"light\": \"light\",\n  \"lights\": \"lights\",\n  \"visible\": \"visible\",\n  \"color\": \"color\",\n  \"intensity\": \"intensity\",\n  \"range\": \"range\",\n  \"spot\": \"spot\",\n  \"innerConeAngle\": \"innerConeAngle\",\n  \"outerConeAngle\": \"outerConeAngle\",\n  \"directional\": \"directional\",\n  \"point\": \"point\",\n  \"perspective\": \"perspective\",\n  \"orthographic\": \"orthographic\",\n  \"aspectRatio\": \"aspectRatio\",\n  \"xfov\": \"xfov\",\n  \"yfov\": \"yfov\",\n  \"zfar\": \"zfar\",\n  \"znear\": \"znear\",\n  \"xmag\": \"xmag\",\n  \"ymag\": \"ymag\",\n  \"ak_flg\": \"ak_flg\",\n  \"VERTEX\": \"VERTEX\",\n  \"images\": \"images\",\n  \"image\": \"image\",\n  \"sampler\": \"sampler\",\n  \"samplers\": \"samplers\",\n  \"wrapS\": \"wrapS\",\n  \"wrapT\": \"wrapT\",\n  \"magFilter\": \"magFilter\",\n  \"minFilter\": \"minFilter\",\n  \"textures\": \"textures\",\n  \"source\": \"source\",\n  \"material\": \"material\",\n  \"materials\": \"materials\",\n  \"pbrMetalRough\": \"pbrMetallicRoughness\",\n  \"normalTex\": \"normalTexture\",\n  \"occlusionTex\": \"occlusionTexture\",\n  \"strength\": \"strength\",\n  \"emissiveTex\": \"emissiveTexture\",\n  \"emissiveFac\": \"emissiveFactor\",\n  \"alphaMode\": \"alphaMode\",\n  \"alphaCutoff\": \"alphaCutoff\",\n  \"doubleSided\": \"doubleSided\",\n  \"baseColor\": \"baseColorFactor\",\n  \"baseColorTex\": \"baseColorTexture\",\n  \"metalFac\": \"metallicFactor\",\n  \"roughFac\": \"roughnessFactor\",\n  \"metalRoughTex\": \"metallicRoughnessTexture\",\n  \"index\": \"index\",\n  \"texCoord\": \"texCoord\",\n  \"texcoordPrefix\": \"TEXCOORD\",\n  \"sid_sampler\": \"smp\",\n  \"id_specgloss\": \"specgloss\",\n  \"KHR_materials_pbrSpecularGlossiness\": \"KHR_materials_pbrSpecularGlossiness\",\n  \"KHR_materials_specular\": \"KHR_materials_specular\",\n  \"ext_KHR_materials_ior\": \"KHR_materials_ior\",\n  \"specularFactor\": \"specularFactor\",\n  \"specularTexture\": \"specularTexture\",\n  \"specularColorFactor\": \"specularColorFactor\",\n  \"specularColorTexture\": \"specularColorTexture\",\n  \"diffuseFactor\": \"diffuseFactor\",\n  \"specFactor\": \"specularFactor\",\n  \"diffuseTexture\": \"diffuseTexture\",\n  \"glossFactor\": \"glossinessFactor\",\n  \"specGlossTex\": \"specularGlossinessTexture\",\n  \"BLEND\": \"BLEND\",\n  \"MASK\": \"MASK\",\n  \"OPAQUE\": \"OPAQUE\",\n  \"animations\": \"animations\",\n  \"channels\": \"channels\",\n  \"target\": \"target\",\n  \"targets\": \"targets\",\n  \"targetNames\": \"targetNames\",\n  \"morphPresets\": \"morphPresets\",\n  \"path\": \"path\",\n  \"pointer\": \"pointer\",\n  \"interpolation\": \"interpolation\",\n  \"input\": \"input\",\n  \"output\": \"output\",\n  \"anim\": \"anim\",\n  \"LINEAR\": \"LINEAR\",\n  \"STEP\": \"STEP\",\n  \"CUBICSPLINE\": \"CUBICSPLINE\",\n  \"inverseBindMatrices\": \"inverseBindMatrices\",\n  \"joints\": \"joints\",\n  \"skeleton\": \"skeleton\",\n  \"sparse\": \"sparse\",\n  \"b64d\": \"data:\",\n  \"offset\": \"offset\",\n  \"fallback\": \"fallback\",\n  \"filter\": \"filter\",\n  \"ATTRIBUTES\": \"ATTRIBUTES\",\n  \"TRIANGLES\": \"TRIANGLES\",\n  \"INDICES\": \"INDICES\",\n  \"NONE\": \"NONE\",\n  \"OCTAHEDRAL\": \"OCTAHEDRAL\",\n  \"QUATERNION\": \"QUATERNION\",\n  \"EXPONENTIAL\": \"EXPONENTIAL\",\n  \"KHR_texture_transform\": \"KHR_texture_transform\",\n  \"KHR_animation_pointer\": \"KHR_animation_pointer\",\n  \"KHR_node_visibility\": \"KHR_node_visibility\",\n  \"KHR_draco_mesh_compression\": \"KHR_draco_mesh_compression\",\n  \"KHR_mesh_quantization\": \"KHR_mesh_quantization\",\n  \"KHR_meshopt_compression\": \"KHR_meshopt_compression\",\n  \"EXT_meshopt_compression\": \"EXT_meshopt_compression\",\n  \"EXT_mesh_gpu_instancing\": \"EXT_mesh_gpu_instancing\",\n  \"EXT_texture_webp\": \"EXT_texture_webp\",\n  \"EXT_lights_image_based\": \"EXT_lights_image_based\",\n  \"EXT_lights_ies\": \"EXT_lights_ies\",\n  \"EXT_mesh_manifold\": \"EXT_mesh_manifold\",\n  \"EXT_mesh_primitive_restart\": \"EXT_mesh_primitive_restart\",\n  \"EXT_texture_astc\": \"EXT_texture_astc\",\n  \"EXT_gsplat_compression_spz\": \"EXT_gsplat_compression_spz\",\n  \"ADOBE_materials_clearcoat_specular\": \"ADOBE_materials_clearcoat_specular\",\n  \"ADOBE_materials_clearcoat_tint\": \"ADOBE_materials_clearcoat_tint\",\n  \"ADOBE_materials_thin_transparency\": \"ADOBE_materials_thin_transparency\",\n  \"AGI_articulations\": \"AGI_articulations\",\n  \"AGI_stk_metadata\": \"AGI_stk_metadata\",\n  \"CESIUM_primitive_outline\": \"CESIUM_primitive_outline\",\n  \"FB_geometry_metadata\": \"FB_geometry_metadata\",\n  \"GODOT_single_root\": \"GODOT_single_root\",\n  \"GRIFFEL_bim_data\": \"GRIFFEL_bim_data\",\n  \"MPEG_accessor_timed\": \"MPEG_accessor_timed\",\n  \"MPEG_animation_timing\": \"MPEG_animation_timing\",\n  \"MPEG_audio_spatial\": \"MPEG_audio_spatial\",\n  \"MPEG_buffer_circular\": \"MPEG_buffer_circular\",\n  \"MPEG_media\": \"MPEG_media\",\n  \"MPEG_mesh_linking\": \"MPEG_mesh_linking\",\n  \"MPEG_scene_dynamic\": \"MPEG_scene_dynamic\",\n  \"MPEG_texture_video\": \"MPEG_texture_video\",\n  \"MPEG_viewport_recommended\": \"MPEG_viewport_recommended\",\n  \"MSFT_lod\": \"MSFT_lod\",\n  \"MSFT_packing_normalRoughnessMetallic\": \"MSFT_packing_normalRoughnessMetallic\",\n  \"MSFT_packing_occlusionRoughnessMetallic\": \"MSFT_packing_occlusionRoughnessMetallic\",\n  \"MSFT_texture_dds\": \"MSFT_texture_dds\",\n  \"NV_materials_mdl\": \"NV_materials_mdl\",\n  \"KHR_lights_punctual\": \"KHR_lights_punctual\",\n  \"KHR_materials_variants\": \"KHR_materials_variants\",\n  \"KHR_texture_basisu\": \"KHR_texture_basisu\",\n  \"KHR_techniques_webgl\": \"KHR_techniques_webgl\",\n  \"KHR_xmp\": \"KHR_xmp\",\n  \"KHR_xmp_json_ld\": \"KHR_xmp_json_ld\",\n  \"KHR_gaussian_splatting\": \"KHR_gaussian_splatting\",\n  \"packet\": \"packet\",\n  \"packets\": \"packets\",\n  \"kernel\": \"kernel\",\n  \"colorSpace\": \"colorSpace\",\n  \"projection\": \"projection\",\n  \"sortingMethod\": \"sortingMethod\",\n  \"compression\": \"compression\",\n  \"format\": \"format\",\n  \"spz\": \"spz\",\n  \"variants\": \"variants\",\n  \"mappings\": \"mappings\",\n  \"variant\": \"variant\",\n  \"KHR_materials_clearcoat\": \"KHR_materials_clearcoat\",\n  \"clearcoatFactor\": \"clearcoatFactor\",\n  \"clearcoatTexture\": \"clearcoatTexture\",\n  \"clearcoatRoughnessFactor\": \"clearcoatRoughnessFactor\",\n  \"clearcoatRoughnessTexture\": \"clearcoatRoughnessTexture\",\n  \"clearcoatNormalTexture\": \"clearcoatNormalTexture\",\n  \"KHR_materials_ior\": \"KHR_materials_ior\",\n  \"ior\": \"ior\",\n  \"KHR_materials_unlit\": \"KHR_materials_unlit\",\n  \"KHR_materials_emissive_strength\": \"KHR_materials_emissive_strength\",\n  \"emissiveStrength\": \"emissiveStrength\",\n  \"KHR_materials_transmission\": \"KHR_materials_transmission\",\n  \"transmissionFactor\": \"transmissionFactor\",\n  \"transmissionTexture\": \"transmissionTexture\",\n  \"KHR_materials_sheen\": \"KHR_materials_sheen\",\n  \"sheenColorFactor\": \"sheenColorFactor\",\n  \"sheenColorTexture\": \"sheenColorTexture\",\n  \"sheenRoughnessFactor\": \"sheenRoughnessFactor\",\n  \"sheenRoughnessTexture\": \"sheenRoughnessTexture\",\n  \"KHR_materials_iridescence\": \"KHR_materials_iridescence\",\n  \"iridescenceFactor\": \"iridescenceFactor\",\n  \"iridescenceTexture\": \"iridescenceTexture\",\n  \"iridescenceIor\": \"iridescenceIor\",\n  \"iridescenceThicknessMinimum\": \"iridescenceThicknessMinimum\",\n  \"iridescenceThicknessMaximum\": \"iridescenceThicknessMaximum\",\n  \"iridescenceThicknessTexture\": \"iridescenceThicknessTexture\",\n  \"KHR_materials_volume\": \"KHR_materials_volume\",\n  \"thicknessFactor\": \"thicknessFactor\",\n  \"thicknessTexture\": \"thicknessTexture\",\n  \"attenuationDistance\": \"attenuationDistance\",\n  \"attenuationColor\": \"attenuationColor\",\n  \"KHR_materials_anisotropy\": \"KHR_materials_anisotropy\",\n  \"anisotropyStrength\": \"anisotropyStrength\",\n  \"anisotropyRotation\": \"anisotropyRotation\",\n  \"anisotropyTexture\": \"anisotropyTexture\",\n  \"KHR_materials_dispersion\": \"KHR_materials_dispersion\",\n  \"dispersion\": \"dispersion\",\n  \"KHR_materials_diffuse_transmission\": \"KHR_materials_diffuse_transmission\",\n  \"diffuseTransmissionFactor\": \"diffuseTransmissionFactor\",\n  \"diffuseTransmissionTexture\": \"diffuseTransmissionTexture\",\n  \"diffuseTransmissionColorFactor\": \"diffuseTransmissionColorFactor\",\n  \"diffuseTransmissionColorTexture\": \"diffuseTransmissionColorTexture\"\n}\n"
  },
  {
    "path": "src/io/gltf/strpool.py",
    "content": "#!/usr/bin/python3\n#\n# Copyright (C) 2020 Recep Aslantas\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport json\nfrom collections import OrderedDict\nfrom os.path     import realpath\nfrom os.path     import dirname\n\nheaderContents = [ ]\ndestdir        = dirname(realpath(__file__))\nspidx          = 0\npos            = 0\n\nfspoolJson = open(destdir + \"/strpool.json\")\nspool      = json.loads(fspoolJson.read(),\n                        object_pairs_hook=OrderedDict)\nfspoolJson.close()\n\nfspool_h = open(destdir + \"/strpool.h\", \"wb\");\nfspool_c = open(destdir + \"/strpool.c\", \"wb\");\n\ncopyright_str = \"\"\"\\\n/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"\"\"\n\nfspool_h.write(copyright_str.encode())\nfspool_c.write(copyright_str.encode())\n\nfspool_h.write(\"\"\"\n#ifndef gltf_strpool_h\n#  define gltf_strpool_h\n\n#ifndef _GLTF_STRPOOL_\n#  define _AK_EXTERN extern\n#else\n#  define _AK_EXTERN\n#endif\n\"\"\".encode())\n\nfspool_c.write(\"\"\"\n#ifndef _GLTF_STRPOOL_\n#  define _GLTF_STRPOOL_\n#endif\n\n#include \"strpool.h\"\n#include <string.h>\n\nconst char _s_gltf_pool_0[] =\n\"\"\".encode())\n\nheaderContents.append(\"\\n/* _s_gltf_pool_0 */\\n\")\n\nfor name, val in spool.items():\n  valLen = len(val) + 1\n\n  # string literal size: 2048\n  if pos + valLen > 2048:\n    pos    = 0\n    spidx += 1\n\n    fspool_c.write(\";\\n\\nconst char _s_gltf_pool_{0}[] =\\n\"\n                     .format(str(spidx)).encode())\n\n    headerContents.append(\"\\n/* _s_gltf_pool_{0} */\\n\"\n                            .format(spidx))\n\n  fspool_c.write(\"\\\"{0}\\\\0\\\"\\n\".format(val).encode())\n\n  headerContents.append(\"#define _s_gltf_{0} _s_gltf_{1}({2})\\n\"\n                          .format(name, str(spidx), str(pos)))\n\n  pos += valLen\n\n# source file, then close it\nfspool_c.write(\";\\n\\n#undef _GLTF_STRPOOL_\\n\".encode())\nfspool_c.close()\n\n# header file\nfor idx in range(spidx + 1):\n  fspool_h.write(\"\\n_AK_EXTERN const char _s_gltf_pool_{0}[];\"\n                   .format(str(idx)).encode())\n\nfspool_h.write(\"\\n\\n\".encode())\n\nfor idx in range(spidx + 1):\n  fspool_h.write(\"#define _s_gltf_{0}(x) (_s_gltf_pool_{0} + x)\\n\"\n                   .format(str(idx)).encode())\n\n# write header contents, then close it\nfspool_h.writelines(map(lambda x: x.encode(), headerContents))\nfspool_h.write(\"\\n#endif /* gltf_strpool_h */\\n\".encode())\nfspool_h.close()\n\n# try free array\ndel headerContents[:]\n\n"
  },
  {
    "path": "src/io/obj/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/obj/README.md",
    "content": "# AssetKit: Wavefront Object Status\n\n- [x] v - Positions\n- [x] vt - Texture Coords\n- [x] vn - Normals\n- [x] f - Faces/Indices \n- [ ] Materials\n  - [x] d - Dissolve\n  - [x] Tr - Transparent (1 - d)\n  - [x] Ka - Ambient\n  - [x] Kd - Diffuse\n  - [x] Ks - Specular\n  - [x] Ke - Emission\n  - [x] map_Ka - Ambient Texture\n  - [x] map_Kd - Diffuse Texture\n  - [x] map_Ks - Specular Texture\n  - [x] map_Ke - Emission Texture\n  - [x] Ns - Shininess/specular exponent\n  - [x] Ni - Index of refraction\n  - [x] bump - Bump/Normal map\n  - [x] illum: Constant\n  - [x] illum: Lambert\n  - [x] illum: Blinn\n  - [ ] illum: Others\n- [x] Convert Multi Indices to Single Index Buffer\n- [x] Group/Separate mesh primitives by usemtl\n- [ ] ...\n\n"
  },
  {
    "path": "src/io/obj/common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef wobj_commoh_h\n#define wobj_commoh_h\n\n#include \"../../../include/ak/assetkit.h\"\n#include \"../../common.h\"\n#include \"../../utils.h\"\n#include \"../../tree.h\"\n#include \"../../json.h\"\n#include \"../../data.h\"\n\n#include <string.h>\n#include <stdlib.h>\n\ntypedef struct WOMtl {\n  char *name;\n  char *map_Ka;\n  char *map_Kd;\n  char *map_Ks;\n  char *map_Ke;\n  char *map_Ns;\n  char *map_d;\n  char *decal;\n  char *disp;\n  char *bump;\n  vec3  Ka;\n  vec3  Kd;\n  vec3  Ks;\n  vec3  Ke;\n  vec3  Tf;\n  float Ni;\n  float Ns;     /* exponent */\n  float d;      /* dissolve */\n  float dHalo;  /* dissolve halo */\n  float Tr;     /* Transparent (1 - d) */\n  float sharpness; /* The default is 60   */\n  int   illum;\n  bool  map_aat;\n  bool  has_Ns;\n} WOMtl;\n\ntypedef struct WOMtlLib {\n  char   *name;\n  RBTree *materials;\n} WOMtlLib;\n\ntypedef struct WOPrim {\n  struct WOPrim *next;\n  const char    *mtlname;\n  AkDataContext *dc_face;\n  AkDataContext *dc_vcount;\n  uint32_t       maxVC;\n  bool           hasTexture;\n  bool           hasNormal;\n} WOPrim;\n\ntypedef struct WOObject {\n  struct WOObject *next;\n  AkGeometry      *geom;\n  WOPrim          *prim;\n} WOObject;\n\ntypedef struct WOState {\n  AkHeap        *heap;\n  AkDoc         *doc;\n  void          *tmp;\n  AkLibrary     *lib_geom;\n  AkNode        *node;\n  WOMtlLib      *mtlib;\n  AkDataContext *dc_pos, *dc_tex, *dc_nor;\n  AkAccessor    *ac_pos, *ac_tex, *ac_nor;\n  WOObject      *obj;\n} WOState;\n\n#ifdef SKIP_SPACES\n# undef SKIP_SPACES\n#endif\n\n#define SKIP_SPACES                                                           \\\n  {                                                                           \\\n    c = p ? *p : '\\0';                                                        \\\n    while (c != '\\0' && AK_ARRAY_SPACE_CHECK) c = *++p;                       \\\n    if (c == '\\0')                                                            \\\n      break; /* to break loop */                                              \\\n  }\n\n#ifdef NEXT_LINE\n# undef NEXT_LINE\n#endif\n\n#define NEXT_LINE                                                             \\\n  do {                                                                        \\\n    c = p ? *p : '\\0';                                                        \\\n    while (p                                                                  \\\n           && p[0] != '\\0'                                                    \\\n           && !AK_ARRAY_NLINE_CHECK                                           \\\n           && (c = *++p) != '\\0'                                              \\\n           && !AK_ARRAY_NLINE_CHECK);                                         \\\n                                                                              \\\n    while (p                                                                  \\\n           && p[0] != '\\0'                                                    \\\n           && AK_ARRAY_NLINE_CHECK                                            \\\n           && (c = *++p) != '\\0'                                              \\\n           && AK_ARRAY_NLINE_CHECK);                                          \\\n  } while(0);\n\n#endif /* wobj_commoh_h */\n"
  },
  {
    "path": "src/io/obj/group.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"group.h\"\n#include \"util.h\"\n#include \"../../strpool.h\"\n\n/* Buffer > Accessor > Input > Prim > Mesh > Geom > InstanceGeom > Node */\n\nstatic\nvoid\nwobj_finishPrim(WOState  * __restrict wst,\n                WOObject * __restrict wo,\n                WOPrim   * __restrict wp);\n\nstatic\nvoid\nwobj_finishPrim(WOState  * __restrict wst,\n                WOObject * __restrict wo,\n                WOPrim   * __restrict wp) {\n  AkHeap             *heap;\n  AkGeometry         *geom;\n  AkMesh             *mesh;\n  AkMeshPrimitive    *prim;\n  uint32_t            inputOffset;\n\n  if (wp->maxVC == 0)\n    return;\n\n  heap        = wst->heap;\n  geom        = wo->geom;\n  mesh        = ak_objGet(geom->gdata);\n  inputOffset = 0;\n\n  /* finish prim */\n  if (wp->maxVC == 3) {\n    AkTriangles *tri;\n    \n    tri = ak_heap_calloc(heap, ak_objFrom(mesh), sizeof(*tri));\n    tri->mode      = AK_TRIANGLES;\n    tri->base.type = AK_PRIMITIVE_TRIANGLES;\n    prim = (AkMeshPrimitive *)tri;\n  } else {\n    AkPolygon *poly;\n    \n    poly = ak_heap_calloc(heap, ak_objFrom(mesh), sizeof(*poly));\n    poly->base.type = AK_PRIMITIVE_POLYGONS;\n    \n    poly->vcount = ak_heap_calloc(heap,\n                                  poly,\n                                  sizeof(*poly->vcount)\n                                  + wp->dc_vcount->usedsize);\n\n    poly->vcount->count  = wp->dc_vcount->itemcount;\n    poly->base.nPolygons = (uint32_t)poly->vcount->count;\n\n    ak_data_join(wp->dc_vcount, poly->vcount->items, 0, 0);\n\n    prim = (AkMeshPrimitive *)poly;\n  }\n\n  prim->mesh      = mesh;\n  prim->next      = mesh->primitive;\n  mesh->primitive = prim;\n  mesh->primitiveCount++;\n  \n  prim->pos = wobj_input(wst, prim, wst->ac_pos,\n                         AK_INPUT_POSITION, _s_POSITION, inputOffset++);\n  \n  if (wst->mtlib && wp->mtlname)\n    prim->material = rb_find(wst->mtlib->materials, (void *)wp->mtlname);\n  \n   if (wp->hasTexture && wst->dc_tex->itemcount > 0)\n     wobj_input(wst, prim, wst->ac_tex,\n                AK_INPUT_TEXCOORD, _s_TEXCOORD, inputOffset++);\n \n   if (wp->hasNormal && wst->dc_nor->itemcount > 0)\n     wobj_input(wst, prim, wst->ac_nor,\n                AK_INPUT_NORMAL, _s_NORMAL, inputOffset);\n   \n  /* fix indices */\n  wobj_joinIndices(wst, wp, prim);\n\n  if (wp->maxVC == 3) {\n    prim->nPolygons = (uint32_t)prim->indices->count / 3;\n  }\n}\n\nAK_HIDE\nWOPrim*\nwobj_switchPrim(WOState * __restrict wst, const char * __restrict mtlname) {\n  WOPrim *wp;\n\n  if ((wp = wst->obj->prim) && wp->dc_face->itemcount == 0) {\n    wp->mtlname = mtlname;\n    return wst->obj->prim;\n  }\n\n  wp             = ak_heap_calloc(wst->heap, wst->tmp, sizeof(*wp));\n  wp->dc_face    = ak_data_new(wst->tmp, 128, sizeof(ivec3), ak_cmp_ivec3);\n  wp->dc_vcount  = ak_data_new(wst->tmp, 128, sizeof(int32_t), NULL);\n  wp->mtlname    = mtlname;\n  wp->next       = wst->obj->prim;\n  wst->obj->prim = wp;\n\n  return wp;\n}\n\nAK_HIDE\nvoid\nwobj_finishObject(WOState * __restrict wst, WOObject * __restrict obj) {\n  WOPrim             *wp, *next;\n  AkInstanceGeometry *instGeom;\n  AkGeometry         *geom;\n  \n  if (!obj->geom)\n    return;\n  \n  /* clean the geom if none resource is found for default state */\n  if (wst->dc_pos->itemcount < 1)\n    return;\n\n  geom = obj->geom;\n\n  /* add to library */\n  geom->base.next     = wst->lib_geom->chld;\n  wst->lib_geom->chld = &geom->base;\n  wst->lib_geom->count++;\n  \n  /* make instance geeometry and attach to the root node  */\n  instGeom = ak_instanceMakeGeom(wst->heap, wst->node, geom);\n  \n  if (wst->node->geometry) {\n    wst->node->geometry->base.prev = (void *)instGeom;\n    instGeom->base.next            = (void *)wst->node->geometry;\n  }\n\n  wst->node->geometry = instGeom;\n\n  /* mesh primitives */\n  wp = obj->prim;\n  do {\n    next = wp->next;\n    wobj_finishPrim(wst, obj, wp);\n  } while ((wp = next));\n}\n\nAK_HIDE\nvoid\nwobj_finishObjects(WOState * __restrict wst) {\n  WOObject *obj;\n\n  obj = wst->obj;\n  while (obj) {\n    wobj_finishObject(wst, obj);\n    obj = obj->next;\n  }\n}\n\nAK_HIDE\nvoid\nwobj_switchObject(WOState * __restrict wst) {\n  WOObject   *obj;\n  AkGeometry *geom;\n  \n  if (wst->obj && wst->obj->prim && wst->obj->prim->dc_face->itemcount == 0)\n    return;\n\n  obj       = ak_heap_calloc(wst->heap, wst->tmp, sizeof(*obj));\n  obj->next = wst->obj;\n  wst->obj  = obj;\n  \n  ak_allocMesh(wst->heap, wst->lib_geom, &geom);\n\n  /* set current geometry */\n  obj->geom = geom;\n\n  wobj_switchPrim(wst, NULL);\n}\n"
  },
  {
    "path": "src/io/obj/group.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef wobj_group_h\n#define wobj_group_h\n\n#include \"common.h\"\n\nAK_HIDE\nWOPrim*\nwobj_switchPrim(WOState * __restrict wst, const char * __restrict mtlname);\n\nAK_HIDE\nvoid\nwobj_finishObject(WOState * __restrict wst, WOObject * __restrict obj);\n\nAK_HIDE\nvoid\nwobj_finishObjects(WOState * __restrict wst);\n\nAK_HIDE\nvoid\nwobj_switchObject(WOState * __restrict wst);\n\n#endif /* wobj_group_h */\n"
  },
  {
    "path": "src/io/obj/mtl.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"mtl.h\"\n#include \"../../strpool.h\"\n\n/*\n Resources:\n   https://all3dp.com/1/obj-file-format-3d-printing-cad/\n   http://paulbourke.net/dataformats/obj/\n   http://paulbourke.net/dataformats/mtl/\n   https://en.wikipedia.org/wiki/Wavefront_.obj_file#Material_template_library\n*/\n\nstatic\nAkProfileCommon*\nwobj_cmnEffect(WOState * __restrict wst);\n\nstatic\nvoid\nwobj_handleMaterial(WOState  * __restrict wst,\n                    WOMtlLib * __restrict mtllib,\n                    WOMtl    * __restrict mtl);\n\nstatic\nAkTextureRef*\nwobj_texref(WOState            * __restrict wst,\n            void               * __restrict memp,\n            char               *            name,\n            AkTextureColorSpace             colorSpace,\n            AkTextureChannels               channels);\n\nAK_HIDE\nWOMtlLib*\nwobj_mtl(WOState    * __restrict wst,\n         const char * __restrict name) {\n  AkHeap   *heap;\n  void     *mtlstr;\n  char     *p, *localurl, *begin, *end;\n  WOMtlLib *mtllib;\n  WOMtl    *mtl;\n  size_t    mtlstrSize;\n  char      c;\n\n  mtllib   = NULL;\n  localurl = ak_getFileFrom(wst->doc, name);\n\n  if (ak_readfile(localurl, NULL, &mtlstr, &mtlstrSize) != AK_OK\n      || !((p = mtlstr) && (c = *p) != '\\0'))\n    goto ret;\n\n  heap              = wst->heap;\n  mtl               = NULL;\n  mtllib            = ak_heap_calloc(heap, wst->tmp, sizeof(*mtllib));\n  mtllib->materials = rb_newtree_str();\n  \n  /* parse .mtl */\n  do {\n    /* skip spaces */\n    SKIP_SPACES\n    \n    if (p[0] == 'n'\n        && p[1] == 'e'\n        && p[2] == 'w'\n        && p[3] == 'm'\n        && p[4] == 't'\n        && p[5] == 'l'\n        && (p[6] == ' ' || p[6] == '\\t')) {\n      p += 6;\n      SKIP_SPACES\n      \n      begin = p;\n      while ((c = *++p) != '\\0' && !AK_ARRAY_NLINE_CHECK);\n      end = p;\n      \n      if (end > begin) {\n        if (mtl)\n          wobj_handleMaterial(wst, mtllib, mtl);\n        \n        mtl       = ak_heap_calloc(heap, wst->tmp, sizeof(*mtl));\n        mtl->name = ak_heap_strndup(heap, mtl, begin, end - begin);\n        \n        /* default params */\n        mtl->Tr    = 0.0f;\n        mtl->d     = 1.0f;\n        mtl->illum = 1;\n      }\n    } else if (mtl) {\n      if (p[1] == ' ' || p[1] == '\\t') {\n        if (p[0] == 'd') {\n          p++;\n          ak_strtof_line(p, 0, 1, &mtl->d);\n        }\n      } else if (p[2] == ' ' || p[2] == '\\t') {\n        switch (p[0]) {\n          case 'K':\n            switch (p[1]) {\n              case 'a':\n                p += 2;\n                ak_strtof_line(p, 0, 3, mtl->Ka);\n                break;\n              case 'd':\n                p += 2;\n                ak_strtof_line(p, 0, 3, mtl->Kd);\n                break;\n              case 's':\n                p += 2;\n                ak_strtof_line(p, 0, 3, mtl->Ks);\n                break;\n              case 'e':\n                p += 2;\n                ak_strtof_line(p, 0, 3, mtl->Ke);\n                break;\n              default:\n                p += 2;\n                break;\n            }\n            break;\n          case 'N':\n            switch (p[1]) {\n              case 's':\n                p += 2;\n                ak_strtof_line(p, 0, 1, &mtl->Ns);\n                mtl->has_Ns = true;\n                break;\n              case 'i':\n                p += 2;\n                ak_strtof_line(p, 0, 1, &mtl->Ni);\n                break;\n              default:\n                p += 2;\n                break;\n            }\n            break;\n          case 'T':\n            switch (p[1]) {\n              case 'r':\n                p += 2;\n                ak_strtof_line(p, 0, 1, &mtl->Tr);\n                break;\n              default:\n                p += 2;\n                break;\n            }\n            break;\n          default:\n            p += 2;\n            break;\n        }\n      } else if (p[0] == 'm'\n                 && p[1] == 'a'\n                 && p[2] == 'p'\n                 && p[3] == '_') {\n        p += 4;\n        switch (p[0]) {\n          case 'K':\n            switch (p[1]) {\n              case 'a':\n                p += 2;\n                SKIP_SPACES\n\n                begin = p;\n                while ((c = *++p) != '\\0' && !AK_ARRAY_NLINE_CHECK);\n                end = p;\n                \n                mtl->map_Ka = ak_heap_strndup(heap, mtl, begin, end - begin);\n                break;\n              case 'd':\n                p += 2;\n                SKIP_SPACES\n\n                begin = p;\n                while ((c = *++p) != '\\0' && !AK_ARRAY_NLINE_CHECK);\n                end = p;\n                \n                mtl->map_Kd = ak_heap_strndup(heap, mtl, begin, end - begin);\n                \n                break;\n              case 's':\n                p += 2;\n                SKIP_SPACES\n\n                begin = p;\n                while ((c = *++p) != '\\0' && !AK_ARRAY_NLINE_CHECK);\n                end = p;\n                \n                mtl->map_Ks = ak_heap_strndup(heap, mtl, begin, end - begin);\n                \n                break;\n              case 'e':\n                p += 2;\n                SKIP_SPACES\n\n                begin = p;\n                while ((c = *++p) != '\\0' && !AK_ARRAY_NLINE_CHECK);\n                end = p;\n                \n                mtl->map_Ke = ak_heap_strndup(heap, mtl, begin, end - begin);\n                \n                break;\n              default: break;\n            }\n            break;\n          default: break;\n        }\n      } else if (p[0] == 'b'\n                 && p[1] == 'u'\n                 && p[2] == 'm'\n                 && p[3] == 'p') {\n        p += 4;\n\n        SKIP_SPACES\n\n        begin = p;\n        while ((c = *++p) != '\\0' && !AK_ARRAY_NLINE_CHECK);\n        end = p;\n\n        mtl->bump = ak_heap_strndup(heap, mtl, begin, end - begin);\n      } else if (p[0] == 'i'\n                 && p[1] == 'l'\n                 && p[2] == 'l'\n                 && p[3] == 'u'\n                 && p[4] == 'm'\n                 && (p[5] == ' ' || p[5] == '\\t')) {\n        p += 5;\n        ak_strtoi_line(p, 0, 1, &mtl->illum);\n      }\n    }\n\n    NEXT_LINE\n  } while (p && p[0] != '\\0'/* && (c = *++p) != '\\0'*/);\n\n  if (mtl)\n    wobj_handleMaterial(wst, mtllib, mtl);\n\nret:\n  if (mtlstr)\n    ak_releasefile(mtlstr, mtlstrSize);\n  ak_free((void *)localurl);\n  return mtllib;\n}\n\nstatic\nAkProfileCommon*\nwobj_cmnEffect(WOState * __restrict wst) {\n  AkLibrary       *lib;\n  AkEffect        *effect;\n  AkProfileCommon *profile;\n\n  if (!(lib = wst->doc->lib.effects)) {\n    lib = ak_heap_calloc(wst->heap, wst->doc, sizeof(*lib));\n    wst->doc->lib.effects = lib;\n  }\n\n  effect        = ak_heap_calloc(wst->heap, lib,    sizeof(*effect));\n  profile       = ak_heap_calloc(wst->heap, effect, sizeof(*profile));\n  profile->type = AK_PROFILE_TYPE_COMMON;\n\n  lib->count++;\n\n  effect->profile = profile;\n  effect->next    = (void *)lib->chld;\n  lib->chld       = (void *)effect;\n\n  ak_setypeid(profile, AKT_PROFILE);\n  ak_setypeid(effect,  AKT_EFFECT);\n\n  return effect->profile;\n}\n\nAK_INLINE\nvoid\nwobj_clrtexset(WOState     * __restrict wst,\n               void        * __restrict memp,\n               float       *            rgb,\n               char        * __restrict map,\n               AkColorDesc * __restrict clr,\n               AkTextureColorSpace      colorSpace,\n               AkTextureChannels        channels) {\n\n  if (rgb) {\n    clr->color = ak_heap_calloc(wst->heap, clr,  sizeof(*clr->color));\n    glm_vec3_copy(rgb, clr->color->vec);\n    clr->color->vec[3] = 1.0f;\n  }\n\n  if (map) {\n    clr->texture = wobj_texref(wst, memp, map, colorSpace, channels);\n  }\n}\n\nAK_INLINE\nAkColorDesc*\nwobj_clrtex(WOState    * __restrict wst,\n            void       * __restrict memp,\n            float      *            rgb,\n            char       * __restrict map,\n            AkTextureColorSpace     colorSpace,\n            AkTextureChannels       channels) {\n  AkColorDesc *clr;\n  clr = ak_heap_calloc(wst->heap, memp, sizeof(*clr));\n  wobj_clrtexset(wst, memp, rgb, map, clr, colorSpace, channels);\n  return clr;\n}\n\n//AK_INLINE\n//AkFloatOrParam*\n//wobj_flt(AkHeap * __restrict heap,\n//         void   * __restrict memp,\n//         float               val) {\n//  AkFloatOrParam *flt;\n//\n//  flt       = ak_heap_calloc(heap, memp, sizeof(*flt));\n//  flt->val  = ak_heap_calloc(heap, flt, sizeof(*flt->val));\n//  *flt->val = val;\n//\n//  return flt;\n//}\n\nstatic\nvoid\nwobj_handleMaterial(WOState  * __restrict wst,\n                    WOMtlLib * __restrict mtllib,\n                    WOMtl    * __restrict mtl) {\n  AkHeap                 *heap;\n  AkDoc                  *doc;\n  AkLibrary              *libmat;\n  AkProfileCommon        *pcommon;\n  AkTechniqueFx          *technfx;\n  AkTechniqueFxCommon    *cmnTechn;\n  AkEffect               *effect;\n  AkInstanceEffect       *ieff;\n  AkMaterial             *mat;\n  AkMaterialSpecularProp *specularProp;\n  AkMaterialEmissionProp *emissionProp;\n\n  heap = wst->heap;\n  doc  = wst->doc;\n  \n  if (!(libmat = doc->lib.materials)) {\n    libmat             = ak_heap_calloc(heap, wst->doc, sizeof(*libmat));\n    doc->lib.materials = libmat;\n  }\n\n  pcommon = wobj_cmnEffect(wst);\n  effect  = ak_mem_parent(pcommon);\n  technfx = ak_heap_calloc(heap, pcommon, sizeof(*technfx));\n  mat     = ak_heap_calloc(heap, libmat,  sizeof(*mat));\n\n  ak_setypeid(technfx, AKT_TECHNIQUE_FX);\n\n  cmnTechn = ak_heap_calloc(heap, technfx, sizeof(*cmnTechn));\n  switch (mtl->illum) {\n    case 0: /* Constant */\n      cmnTechn->type = AK_MATERIAL_CONSTANT;\n      break;\n    case 1: /* Lambert */\n      cmnTechn->type = AK_MATERIAL_LAMBERT;\n      break;\n    case 2: /* TODO: Currently all others are Blinn */\n      cmnTechn->type = AK_MATERIAL_BLINN;\n    default:\n      break;\n  }\n\n  cmnTechn->ambient       = wobj_clrtex(wst, cmnTechn, mtl->Ka, mtl->map_Ka,\n                                        AK_TEXTURE_COLORSPACE_SRGB,\n                                        AK_TEXTURE_CHANNEL_RGB);\n  cmnTechn->diffuse       = wobj_clrtex(wst, cmnTechn, mtl->Kd, mtl->map_Kd,\n                                        AK_TEXTURE_COLORSPACE_SRGB,\n                                        AK_TEXTURE_CHANNEL_RGBA);\n\n  specularProp            = ak_heap_calloc(heap, cmnTechn, sizeof(*specularProp));\n  cmnTechn->specular      = specularProp;\n  specularProp->shininess = mtl->Ns;\n  specularProp->color     = wobj_clrtex(wst, cmnTechn, mtl->Ks, mtl->map_Ks,\n                                        AK_TEXTURE_COLORSPACE_SRGB,\n                                        AK_TEXTURE_CHANNEL_RGB);\n\n  emissionProp            = ak_heap_calloc(heap, cmnTechn, sizeof(*emissionProp));\n  cmnTechn->emission      = emissionProp;\n  emissionProp->strength  = 1.0f;\n\n  wobj_clrtexset(wst, cmnTechn, mtl->Ke, mtl->map_Ke, &emissionProp->color,\n                 AK_TEXTURE_COLORSPACE_SRGB,\n                 AK_TEXTURE_CHANNEL_RGB);\n\n  cmnTechn->ior = mtl->Ni;\n\n  if (mtl->bump) {\n    cmnTechn->normal        = ak_heap_calloc(heap, cmnTechn, sizeof(*cmnTechn->normal));\n    cmnTechn->normal->scale = 1.0f;\n    cmnTechn->normal->tex   = wobj_texref(wst, cmnTechn, mtl->bump,\n                                          AK_TEXTURE_COLORSPACE_LINEAR,\n                                          AK_TEXTURE_CHANNEL_RGB);\n  }\n  \n  if (mtl->Tr > 0.0f || mtl->d < 1.0f) {\n    AkTransparent *transp;\n    float          t;\n\n    if (mtl->d < 1.0f)\n      t = mtl->d;\n    else\n      t = 1.0f - mtl->Tr;\n\n    transp         = ak_heap_calloc(heap, cmnTechn, sizeof(*transp));\n    transp->amount = t;\n    transp->opaque = AK_OPAQUE_BLEND;\n\n    cmnTechn->transparent = transp;\n  }\n\n  technfx->common    = cmnTechn;\n  technfx->next      = pcommon->technique;\n  pcommon->technique = technfx;\n  \n  ieff               = ak_heap_calloc(heap, mat, sizeof(*ieff));\n  ieff->base.type    = AK_INSTANCE_EFFECT;\n  ieff->base.url.ptr = effect;\n  mat->effect        = ieff;\n  \n  mat->base.next     = libmat->chld;\n  libmat->chld       = (void *)mat;\n  libmat->count++;\n\n  rb_insert(mtllib->materials, mtl->name, mat);\n}\n\nstatic\nAkTextureRef*\nwobj_texref(WOState            * __restrict wst,\n            void               * __restrict memp,\n            char               *            name,\n            AkTextureColorSpace             colorSpace,\n            AkTextureChannels               channels) {\n  AkHeap       *heap;\n  AkDoc        *doc;\n  AkImage      *image;\n  AkInitFrom   *initFrom;\n  AkTexture    *tex;\n  AkSampler    *sampler;\n  AkTextureRef *texref;\n \n  heap = wst->heap;\n  doc  = wst->doc;\n  \n  /* create image */\n  image           = ak_heap_calloc(heap, doc, sizeof(*image));\n  initFrom        = ak_heap_calloc(heap, image, sizeof(*initFrom));\n  initFrom->ref   = name;\n  image->initFrom = initFrom;\n\n  ak_mem_setp(name, initFrom);\n  flist_sp_insert(&doc->lib.images, image);\n\n  /* create sampler */\n  sampler        = ak_heap_calloc(heap, doc, sizeof(*sampler));\n  sampler->wrapS = AK_WRAP_MODE_WRAP;\n  sampler->wrapT = AK_WRAP_MODE_WRAP;\n  ak_setypeid(sampler, AKT_SAMPLER2D);\n  \n  /* create texture */\n  tex          = ak_heap_calloc(heap, doc, sizeof(*tex));\n  tex->type    = AKT_SAMPLER2D;\n  tex->image   = image;\n  tex->sampler = sampler;\n\n  flist_sp_insert(&doc->lib.textures, tex);\n\n  /* create texture ref */\n  texref = ak_heap_calloc(heap, memp, sizeof(*texref));\n  ak_setypeid(texref, AKT_TEXTURE_REF);\n  \n  texref->coordInputName = _s_TEXCOORD;\n  texref->texture        = tex;\n  texref->slot           = 0;\n  ak_texref_usage(texref, colorSpace, channels);\n\n  return texref;\n}\n"
  },
  {
    "path": "src/io/obj/mtl.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef wobj_mtl_h\n#define wobj_mtl_h\n\n#include \"common.h\"\n\nAK_HIDE\nWOMtlLib*\nwobj_mtl(WOState    * __restrict wst,\n         const char * __restrict name);\n\n#endif /* wobj_mtl_h */\n"
  },
  {
    "path": "src/io/obj/obj.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n Resources:\n   https://all3dp.com/1/obj-file-format-3d-printing-cad/\n   http://paulbourke.net/dataformats/obj/\n   http://paulbourke.net/dataformats/mtl/\n   https://en.wikipedia.org/wiki/Wavefront_.obj_file\n*/\n\n#include \"obj.h\"\n#include \"common.h\"\n#include \"group.h\"\n#include \"mtl.h\"\n#include \"util.h\"\n#include \"../common/postscript.h\"\n#include \"../../id.h\"\n#include \"../../data.h\"\n#include \"../../../include/ak/path.h\"\n\nstatic\nvoid\nak_wobjFreeDupl(RBTree *tree, RBNode *node);\n\nAK_HIDE\nAkResult\nwobj_obj(AkDoc     ** __restrict dest,\n         const char * __restrict filepath) {\n  AkHeap             *heap;\n  AkDoc              *doc;\n  void               *objstr;\n  char               *p, *begin, *end, *m;\n  AkLibrary          *lib_vscene;\n  AkVisualScene      *scene;\n  WOPrim             *prim;\n  WOState             wstVal = {0}, *wst;\n  float               v[4];\n  size_t              objstrSize;\n  AkResult            ret;\n  uint32_t            vc;\n  char                c;\n\n  if ((ret = ak_readfile(filepath, NULL, &objstr, &objstrSize)) != AK_OK)\n    return ret;\n\n  heap = ak_heap_new(NULL, NULL, NULL);\n  doc  = ak_heap_calloc(heap, NULL, sizeof(*doc));\n\n  doc->inf                = ak_heap_calloc(heap, doc, sizeof(*doc->inf));\n  doc->inf->name          = filepath;\n  doc->inf->dir           = ak_path_dir(heap, doc, filepath);\n  doc->inf->flipImage     = true;\n  doc->inf->ftype         = AK_FILE_TYPE_WAVEFRONT;\n  doc->inf->base.coordSys = AK_YUP;\n  doc->coordSys           = AK_YUP; /* Default */\n\n  if (!((p = objstr) && (c = *p) != '\\0')) {\n    ak_free(doc);\n    ak_releasefile(objstr, objstrSize);\n    return AK_ERR;\n  }\n  \n  /* for fixing skin and morph vertices */\n  doc->reserved = rb_newtree_ptr();\n  ((RBTree *)doc->reserved)->onFreeNode = ak_wobjFreeDupl;\n  \n  ak_heap_setdata(heap, doc);\n  ak_id_newheap(heap);\n\n  /* libraries */\n  doc->lib.geometries = ak_heap_calloc(heap, doc, sizeof(AkLibrary));\n  lib_vscene = ak_heap_calloc(heap, doc, sizeof(*lib_vscene));\n  \n  /* default scene */\n  scene                  = ak_heap_calloc(heap, doc, sizeof(*scene));\n  scene->node            = ak_heap_calloc(heap, doc, sizeof(*scene->node));\n  lib_vscene->chld       = &scene->base;\n  lib_vscene->count      = 1;\n  doc->lib.visualScenes  = lib_vscene;\n  doc->scene.visualScene = ak_instanceMake(heap, doc, scene);\n\n  /* parse state */\n  memset(&wstVal, 0, sizeof(wstVal));\n  wst              = &wstVal;\n  wstVal.doc       = doc;\n  wstVal.heap      = heap;\n  wstVal.tmp       = ak_heap_alloc(heap, doc, sizeof(void*));\n  wstVal.node      = scene->node;\n  wstVal.lib_geom  = doc->lib.geometries;\n\n  /* vertex data (shared across file) */\n  wst->dc_pos      = ak_data_new(wst->tmp, 128, sizeof(vec3), NULL);\n  wst->dc_tex      = ak_data_new(wst->tmp, 128, sizeof(vec2), NULL);\n  wst->dc_nor      = ak_data_new(wst->tmp, 128, sizeof(vec3), NULL);\n\n  /* default group */\n  wobj_switchObject(wst);\n\n  prim = wst->obj->prim;\n  \n  /* parse .obj */\n  do {\n    /* skip spaces */\n    SKIP_SPACES\n\n    if (p[1] == ' ' || p[1] == '\\t') {\n      switch (c) {\n        case '#': {\n          /* ignore comments */\n          while ((c = *++p) != '\\0' && !AK_ARRAY_NLINE_CHECK);\n          /* while ((c = *++p) != '\\0' &&  AK_ARRAY_NLINE_CHECK); */\n          break;\n        }\n        case 'v': {\n          if (*++p == '\\0')\n            goto err;\n\n          /* TODO: handle 4 components */\n          ak_strtof_line(p, 0, 3, v);\n          ak_data_append(wst->dc_pos, v);\n          break;\n        }\n        case 'f': {\n          if ((c = *(p += 2)) == '\\0')\n            goto err;\n\n          vc = 0;\n\n          do {\n            ivec3 face;\n\n            /* vertex index */\n            SKIP_SPACES\n\n            if (AK_ARRAY_NLINE_CHECK)\n              break;\n            \n            face[0] = (int32_t)strtol(p, &p, 10);\n            face[1] = 0;\n            face[2] = 0;\n\n            /* texture index */\n            SKIP_SPACES\n            if (p && p[0] == '/') {\n              if (p[1] != '/') {\n                face[1] = (int32_t)strtol(++p, &p, 10);\n                \n                if (!prim->hasTexture)\n                  prim->hasTexture = true;\n              } else {\n                p++;\n              }\n            }\n            \n            /* normal index */\n            SKIP_SPACES\n            if (p && p[0] == '/') {\n              face[2] = (int32_t)strtol(++p, &p, 10);\n\n              if (!prim->hasNormal)\n                prim->hasNormal = true;\n            }\n\n            ak_data_append(prim->dc_face, face);\n            vc += 1;\n\n            c = *p;\n          } while (p\n                   && (c = p[0]) != '\\0'\n                   && !AK_ARRAY_NLINE_CHECK\n                   && (c = *++p) != '\\0'\n                   && !AK_ARRAY_NLINE_CHECK);\n\n          prim->maxVC = GLM_MAX(prim->maxVC, vc);\n          ak_data_append(prim->dc_vcount, &vc);\n          break;\n        }\n        case 'o':\n        case 'g': {\n          wobj_switchObject(wst);\n          prim = wst->obj->prim;\n          break;\n        }\n        default:\n          break;\n      }\n    } else if (p[2] == ' ' || p[2] == '\\t') {\n      if (p[0] == 'v' && p[1] == 'n') {\n        if (*(p += 2) == '\\0')\n          goto err;\n\n        ak_strtof_line(p, 0, 3, v);\n        ak_data_append(wst->dc_nor, v);\n      } else if (p[0] == 'v' && p[1] == 't') {\n        if (*(p += 2) == '\\0')\n          goto err;\n\n        ak_strtof_line(p, 0, 2, v);\n        ak_data_append(wst->dc_tex, v);\n      }\n    } else if (p[0] == 'm'\n               && p[1] == 't'\n               && p[2] == 'l'\n               && p[3] == 'l'\n               && p[4] == 'i'\n               && p[5] == 'b'\n               && (p[6] == ' ' || p[6] == '\\t')) {\n      p += 7;\n      SKIP_SPACES\n\n      begin = p;\n      while ((c = *++p) != '\\0' && !AK_ARRAY_NLINE_CHECK);\n      end = p;\n\n      if (end > begin\n          && (m = ak_heap_strndup(heap, wst->doc, begin, end - begin)))\n        wst->mtlib = wobj_mtl(wst, m);\n    } else if (p[0] == 'u'\n               && p[1] == 's'\n               && p[2] == 'e'\n               && p[3] == 'm'\n               && p[4] == 't'\n               && p[5] == 'l'\n               && (p[6] == ' ' || p[6] == '\\t')) {\n      p += 7;\n      SKIP_SPACES\n\n      begin = p;\n      while ((c = *++p) != '\\0' && !AK_ARRAY_NLINE_CHECK);\n      end = p;\n\n      m = NULL;\n      if (end > begin)\n        m = ak_heap_strndup(heap, wst->doc, begin, end - begin);\n      \n      prim = wobj_switchPrim(wst, m);\n    }\n    \n    NEXT_LINE\n  } while (p && p[0] != '\\0'/* && (c = *++p) != '\\0'*/);\n\n  wst->ac_pos = wobj_acc(wst, wst->dc_pos, AK_COMPONENT_SIZE_VEC3, AKT_FLOAT);\n  wst->ac_nor = wobj_acc(wst, wst->dc_nor, AK_COMPONENT_SIZE_VEC3, AKT_FLOAT);\n  wst->ac_tex = wobj_acc(wst, wst->dc_tex, AK_COMPONENT_SIZE_VEC2, AKT_FLOAT);\n\n  wobj_finishObjects(wst);\n\n  io_postscript(doc);\n\n  *dest = doc;\n  \n  /* cleanup */\n  ak_free(wst->tmp);\n  ak_releasefile(objstr, objstrSize);\n\n  return AK_OK;\n\nerr:\n  ak_free(doc);\n  \n  if (objstr)\n    ak_releasefile(objstr, objstrSize);\n\n  return AK_ERR;\n}\n\nstatic\nvoid\nak_wobjFreeDupl(RBTree *tree, RBNode *node) {\n  if (node == tree->nullNode)\n    return;\n  ak_free(node->val);\n}\n"
  },
  {
    "path": "src/io/obj/obj.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef wobj_h\n#define wobj_h\n\n#include \"common.h\"\n\nAK_HIDE\nAkResult\nwobj_obj(AkDoc     ** __restrict dest,\n         const char * __restrict filepath);\n\n#endif /* wobj_h */\n"
  },
  {
    "path": "src/io/obj/util.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"util.h\"\n\n#define wobj_real_index(count, val) val > 0 ? val - 1 : count - val\n\nAK_HIDE\nAkAccessor*\nwobj_acc(WOState         * __restrict wst,\n         AkDataContext   * __restrict dctx,\n         AkComponentSize              compSize,\n         AkTypeId                     type) {\n  AkHeap     *heap;\n  AkBuffer   *buff;\n  AkAccessor *acc;\n  AkTypeDesc *typeDesc;\n  int         nComponents;\n\n  heap        = wst->heap;\n  typeDesc    = ak_typeDesc(type);\n  nComponents = (int)compSize;\n\n  buff         = ak_heap_calloc(heap, wst->doc, sizeof(*buff));\n  buff->data   = ak_heap_alloc(heap, buff, dctx->usedsize);\n  buff->length = dctx->usedsize;\n  ak_data_join(dctx, buff->data, 0, 0);\n  \n  flist_sp_insert(&wst->doc->lib.buffers, buff);\n  \n  acc                    = ak_heap_calloc(heap, wst->doc, sizeof(*acc));\n  acc->buffer            = buff;\n  acc->byteLength        = buff->length;\n  acc->byteStride        = typeDesc->size * nComponents;\n  acc->componentSize     = compSize;\n  acc->componentType     = type;\n  acc->bytesPerComponent = typeDesc->size;\n  acc->componentCount    = nComponents;\n  acc->fillByteSize      = typeDesc->size * nComponents;\n  acc->count             = (uint32_t)dctx->itemcount;\n\n  return acc;\n}\n\nAK_HIDE\nAkInput*\nwobj_input(WOState         * __restrict wst,\n           AkMeshPrimitive * __restrict prim,\n           AkAccessor      * __restrict acc,\n           AkInputSemantic              sem,\n           const char      * __restrict semRaw,\n           uint32_t                     offset) {\n  AkInput *inp;\n\n  inp                 = ak_heap_calloc(wst->heap, prim, sizeof(*inp));\n  inp->accessor       = acc;\n  inp->semantic       = sem;\n  inp->semanticRaw    = ak_heap_strdup(wst->heap, inp, semRaw);\n  inp->offset         = offset;\n\n  inp->next   = prim->input;\n  prim->input = inp;\n  prim->inputCount++;\n  \n  ak_retain(acc);\n\n  return inp;\n}\n\nAK_HIDE\nvoid\nwobj_joinIndices(WOState         * __restrict wst,\n                 WOPrim          * __restrict wp,\n                 AkMeshPrimitive * __restrict prim) {\n  AkDataChunk *chunk;\n  AkUInt      *it, *it2, val;\n  size_t       count;\n  size_t       isz, csz, i;\n  uint32_t     istride, count_pos, count_tex, count_nor;\n\n  if (!wp->dc_face->data)\n    return;\n  \n  count   = wp->dc_face->itemcount;\n  istride = 1;\n\n  count_pos = wst->ac_pos->count;\n  count_tex = wst->ac_tex->count;\n  count_nor = wst->ac_nor->count;\n\n  if (wp->hasTexture || wp->hasNormal) {\n    istride += (int)wp->hasNormal + (int)wp->hasTexture;\n    count   *= istride;\n  }\n\n  prim->indices = ak_heap_calloc(wst->heap,\n                                prim,\n                                sizeof(*prim->indices) + count * sizeof(AkUInt));\n  prim->indices->count = count;\n  prim->indexStride    = istride;\n\n  it = prim->indices->items;\n\n  /* join index buffer chunks */\n  isz   = wp->dc_face->itemsize;\n  chunk = wp->dc_face->data;\n\n  /* to make it faster split cases */\n  if (wp->hasNormal && wp->hasTexture) {\n    while (chunk) {\n      csz = chunk->usedsize;\n      it2 = (void *)chunk->data;\n\n      for (i = 0; i < csz; i += isz) {\n        /* position */\n        val = *it2;\n        *it = wobj_real_index(count_pos, val);\n\n        /* texture */\n        val = *(it2 + 1);\n        *(it + 1) = wobj_real_index(count_tex, val);\n\n        /* normal */\n        val = *(it2 + 2);\n        *(it + 2) = wobj_real_index(count_nor, val);\n\n        it  += 3;\n        it2 += 3;\n      }\n      chunk = chunk->next;\n    }\n  } else if (wp->hasNormal) {\n    while (chunk) {\n      csz = chunk->usedsize;\n      it2 = (void *)chunk->data;\n\n      for (i = 0; i < csz; i += isz) {\n        /* position */\n        val = *it2;\n        *it = wobj_real_index(count_pos, val);\n        \n        /* normal */\n        val = *(it2 + 2);\n        *(it + 1) = wobj_real_index(count_nor, val);\n\n        it  += 2;\n        it2 += 3;\n      }\n      chunk = chunk->next;\n    }\n  } else if (wp->hasTexture) {\n    while (chunk) {\n      csz = chunk->usedsize;\n      it2 = (void *)chunk->data;\n\n      for (i = 0; i < csz; i += isz) {\n        /* position */\n        val = *it2;\n        *it = wobj_real_index(count_pos, val);\n        \n        /* texture */\n        val = *(it2 + 1);\n        *(it + 1) = wobj_real_index(count_tex, val);\n\n        it  += 2;\n        it2 += 3;\n      }\n      chunk = chunk->next;\n    }\n  } else {\n    while (chunk) {\n      csz = chunk->usedsize;\n      it2 = (void *)chunk->data;\n\n      for (i = 0; i < csz; i += isz) {\n        /* position */\n        val = *it2;\n        *it = wobj_real_index(count_pos, val);\n\n        it  += 1;\n        it2 += 3;\n      }\n      chunk = chunk->next;\n    }\n  }\n}\n"
  },
  {
    "path": "src/io/obj/util.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef wobj_util_h\n#define wobj_util_h\n\n#include \"common.h\"\n#include \"../common/util.h\"\n\nAK_HIDE\nAkAccessor*\nwobj_acc(WOState         * __restrict wst,\n         AkDataContext   * __restrict dctx,\n         AkComponentSize              compSize,\n         AkTypeId                     type);\n\nAK_HIDE\nAkInput*\nwobj_input(WOState         * __restrict wst,\n           AkMeshPrimitive * __restrict prim,\n           AkAccessor      * __restrict acc,\n           AkInputSemantic              sem,\n           const char      * __restrict semRaw,\n           uint32_t                     offset);\n\nAK_HIDE\nvoid\nwobj_joinIndices(WOState         * __restrict wst,\n                 WOPrim          * __restrict wp,\n                 AkMeshPrimitive * __restrict prim);\n\n#endif /* wobj_util_h */\n"
  },
  {
    "path": "src/io/ply/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/ply/README.md",
    "content": "# AssetKit: Polygon File Format (ply) Status\n\n- [x] ASCII\n- [x] Binary\n- [x] X, Y, Z \n- [x] NX, NY, NZ\n- [x] S, T, U, V\n- [x] R, G, B\n- [x] Stop loading is one of X, Y, Z property is missing\n- [x] Ignore tuple if one of them is not exist e.g. ignore normals if NY is missing\n"
  },
  {
    "path": "src/io/ply/ascii.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ply.h\"\n#include \"common.h\"\n#include \"util.h\"\n#include \"../common/util.h\"\n#include \"../../data.h\"\n\nAK_HIDE\nvoid\nply_ascii(char * __restrict src, PLYState * __restrict pst) {\n  char        *p;\n  float       *b;\n  PLYElement  *elem;\n  PLYProperty *prop;\n  AkBuffer    *buff;\n  char         c;\n  uint32_t     i, stride;\n  \n  p    = src;\n  elem = pst->element;\n\n  while (elem) {\n    if (elem->type == PLY_ELEM_VERTEX) {\n      buff   = elem->buff;\n      b      = buff->data; /* TODO: all vertices are floats for now */\n      stride = elem->knownCount;\n      i      = 0;\n      c      = *p;\n\n      /* stop */\n      if (!elem->buff || elem->buff->length == 0)\n        return;\n\n      do {\n        SKIP_SPACES\n\n        prop = elem->property;\n        while (prop) {\n          if (!prop->ignore)\n            b[prop->slot] = strtof(p, &p);\n          prop = prop->next;\n        }\n\n        b += stride;\n\n        NEXT_LINE\n\n        if (++i >= elem->count)\n          break;\n      } while (p && p[0] != '\\0');\n    } else if (elem->type == PLY_ELEM_FACE) {\n      AkUInt *f, center, fc, j, count, last_fc;\n      \n      pst->dc_ind = ak_data_new(pst->tmp, 128, sizeof(AkUInt), NULL);\n      c           = *p;\n      f           = NULL;\n      i           = 0;\n      count       = 0;\n      last_fc     = 0;\n\n      do {\n        SKIP_SPACES\n        \n        fc = (AkUInt)strtol(p, &p, 10);\n        if (fc >= 3) {\n          if (!f || last_fc < fc)\n            f = alloca(sizeof(AkUInt) * fc);\n          \n          for (j = 0; j < fc; j++)\n            f[j] = (AkUInt)strtol(p, &p, 10);\n          \n          center = f[0];\n          for (j = 0; j < fc - 2; j++) {\n            ak_data_append(pst->dc_ind, &center);\n            ak_data_append(pst->dc_ind, &f[j + 1]);\n            ak_data_append(pst->dc_ind, &f[j + 2]);\n            count += 3;\n          }\n        }\n\n        last_fc = fc;\n\n        NEXT_LINE\n\n        if (++i >= elem->count)\n          break;\n      } while (p && p[0] != '\\0');\n      \n      pst->count = count;\n    } else {\n      /* skip unsupported elements */\n      for (i = 0; i < elem->count; i++) {\n        NEXT_LINE\n      }\n    }\n    elem = elem->next;\n  }\n  \n  ply_finish(pst);\n}\n"
  },
  {
    "path": "src/io/ply/bin.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ply.h\"\n#include \"common.h\"\n#include \"util.h\"\n#include \"../common/util.h\"\n#include \"../../data.h\"\n#include \"../../endian.h\"\n\nAK_HIDE\nvoid\nply_bin(char * __restrict src, PLYState * __restrict pst, bool le) {\n  char        *p;\n  float       *b;\n  PLYElement  *elem;\n  PLYProperty *prop;\n  AkBuffer    *buff;\n  uint32_t     i, stride, vertcount;\n  \n  p         = src;\n  elem      = pst->element;\n  vertcount = pst->vertcount;\n\n  while (elem) {\n    if (elem->type == PLY_ELEM_VERTEX) {\n      AkUInt elemc;\n      \n      elemc  = elem->count;\n      buff   = elem->buff;\n      b      = buff->data; /* TODO: all vertices are floats for now */\n      stride = elem->knownCount;\n      i      = 0;\n\n      /* stop */\n      if (!elem->buff || elem->buff->length == 0)\n        return;\n\n      while (i++ < elemc) {\n        prop = elem->property;\n        while (prop) {\n          if (!prop->ignore) {\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wstrict-aliasing\"\n\n            ply_val(p, prop->typeDesc, le, float, b[prop->slot], 0.0f);\n            \n#pragma GCC diagnostic pop\n          }\n          prop = prop->next;\n        }\n\n        b += stride;\n      }\n    } else if (elem->type == PLY_ELEM_FACE) {\n      char   *e;\n      AkUInt *f, center, fc, j, count, last_fc, valid, elemc;\n\n      pst->dc_ind = ak_data_new(pst->tmp, 128, sizeof(AkUInt), NULL);\n      elemc       = elem->count;\n      e           = pst->end;\n      f           = NULL;\n      i           = 0;\n      count       = 0;\n      last_fc     = 0;\n\n      while (i++ < elemc) {\n        prop = elem->property;\n        \n        /* iterate thorough list and other properties */\n        while (prop) {\n          if (!prop->ignore && prop->islist) { /* TODO: */\n            if ((p + prop->typeDesc->size) > e)\n              goto fns;\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wstrict-aliasing\"\n            \n            ply_val(p, prop->listCountTypeDesc, le, AkUInt, fc, 0);\n            \n#pragma GCC diagnostic pop\n\n            if (fc >= 3) {\n              if (!f || fc > last_fc)\n                f = alloca(sizeof(*f) * fc);\n\n              valid = 0;\n\n              /* copy data */\n              for (j = 0; j < fc; j++) {\n                if ((p + prop->typeDesc->size) > e)\n                  goto fns;\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wstrict-aliasing\"\n                \n                ply_val(p, prop->typeDesc, le, uint32_t, f[j], 0);\n                \n#pragma GCC diagnostic pop\n\n                valid += f[j] < vertcount;\n              }\n              \n              /* check valid loop */\n              if (valid == fc) {\n                center = f[0];\n                for (j = 0; j < fc - 2; j++) {\n                  ak_data_append(pst->dc_ind, &center);\n                  ak_data_append(pst->dc_ind, &f[j + 1]);\n                  ak_data_append(pst->dc_ind, &f[j + 2]);\n                  count += 3;\n                }\n              }\n            } else if (fc > 0) {\n              for (j = 0; j < fc; j++)\n                p += prop->typeDesc->size;\n            }\n            \n            last_fc = fc;\n          } else {\n            /* do ignore */\n            /* TODO: */\n          }\n\n          prop = prop->next;\n        }\n      }\n\n      pst->count = count;\n    } else {\n      /* skip unsupported elements */\n      /* TODO: */\n    }\n    elem = elem->next;\n  }\n  \nfns:\n  ply_finish(pst);\n}\n"
  },
  {
    "path": "src/io/ply/common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ply_commoh_h\n#define ply_commoh_h\n\n#include \"../../../include/ak/assetkit.h\"\n#include \"../../common.h\"\n#include \"../../utils.h\"\n#include \"../../tree.h\"\n#include \"../../json.h\"\n#include \"../../data.h\"\n\n#include <string.h>\n#include <stdlib.h>\n\ntypedef enum PLYPropertyType {\n  PLY_PROP_UNSUPPORTED = 0,\n  PLY_PROP_X,\n  PLY_PROP_Y,\n  PLY_PROP_Z,\n  PLY_PROP_S,\n  PLY_PROP_T,\n  PLY_PROP_NX,\n  PLY_PROP_NY,\n  PLY_PROP_NZ,\n  PLY_PROP_R,\n  PLY_PROP_G,\n  PLY_PROP_B\n} PLYPropertyType;\n\ntypedef struct PLYProperty {\n  struct PLYProperty *prev;\n  struct PLYProperty *next;\n  char               *name;\n  char               *typestr;\n  AkTypeDesc         *typeDesc;\n  char               *listCountType;\n  AkTypeDesc         *listCountTypeDesc;\n  PLYPropertyType     semantic;\n  uint32_t            slot;\n  size_t              off;\n  bool                islist;\n  bool                ignore;\n} PLYProperty;\n\ntypedef enum PLYElementType {\n  PLY_ELEM_UNKNOWN = 0,\n  PLY_ELEM_VERTEX  = 1,\n  PLY_ELEM_FACE    = 2\n} PLYElementType;\n\ntypedef struct PLYElement {\n  struct PLYElement *next;\n  PLYProperty       *property;\n  AkBuffer          *buff;\n  char              *name;\n  uint32_t           count;\n  uint32_t           knownCount;\n  PLYElementType     type;\n  size_t             buffsize;\n} PLYElement;\n\ntypedef struct PLYState {\n  AkHeap        *heap;\n  AkDoc         *doc;\n  void          *tmp;\n  char          *end;\n  AkLibrary     *lib_geom;\n  AkGeometry    *geom;\n  AkDataContext *dc_ind;\n  AkAccessor    *ac_pos, *ac_nor, *ac_tex, *ac_rgb;\n  AkNode        *node;\n  PLYElement    *element;\n  PLYElement    *lastElement;\n  size_t         vertBuffsize;\n  uint32_t       byteStride;\n  uint32_t       count;\n  uint32_t       vertcount;\n} PLYState;\n\n#define SKIP_SPACES                                                           \\\n  {                                                                           \\\n    while (c != '\\0' && AK_ARRAY_SPACE_CHECK) c = *++p;                       \\\n    if (c == '\\0')                                                            \\\n      break; /* to break loop */                                              \\\n  }\n\n#define NEXT_LINE                                                             \\\n  do {                                                                        \\\n    c = p ? *p : '\\0';                                                        \\\n    while (p                                                                  \\\n           && p[0] != '\\0'                                                    \\\n           && !AK_ARRAY_NLINE_CHECK                                           \\\n           && (c = *++p) != '\\0'                                              \\\n           && !AK_ARRAY_NLINE_CHECK);                                         \\\n                                                                              \\\n    while (p                                                                  \\\n           && p[0] != '\\0'                                                    \\\n           && AK_ARRAY_NLINE_CHECK                                            \\\n           && (c = *++p) != '\\0'                                              \\\n           && AK_ARRAY_NLINE_CHECK);                                          \\\n  } while(0);\n\n#define EQ4(c1,c2,c3,c4) \\\n    (p[0] == c1 \\\n  && p[1] == c2 \\\n  && p[2] == c3 \\\n  && p[3] == c4 \\\n  && (p[4] == ' ' || p[4] == '\\t'))\n\n#define EQ5(c1,c2,c3,c4,c5) \\\n    (p[0] == c1 \\\n  && p[1] == c2 \\\n  && p[2] == c3 \\\n  && p[3] == c4 \\\n  && p[4] == c5 \\\n  && (p[5] == ' ' || p[5] == '\\t'))\n\n#define EQ6(c1,c2,c3,c4,c5,c6) \\\n    (p[0] == c1 \\\n  && p[1] == c2 \\\n  && p[2] == c3 \\\n  && p[3] == c4 \\\n  && p[4] == c5 \\\n  && p[5] == c6 \\\n  && (p[6] == ' ' || p[6] == '\\t'))\n\n#define EQ7(c1,c2,c3,c4,c5,c6,c7) \\\n    (p[0] == c1 \\\n  && p[1] == c2 \\\n  && p[2] == c3 \\\n  && p[3] == c4 \\\n  && p[4] == c5 \\\n  && p[5] == c6 \\\n  && p[6] == c7 \\\n  && (p[7] == ' ' || p[7] == '\\t'))\n\n#define EQ8(c1,c2,c3,c4,c5,c6,c7,c8) \\\n    (p[0] == c1 \\\n  && p[1] == c2 \\\n  && p[2] == c3 \\\n  && p[3] == c4 \\\n  && p[4] == c5 \\\n  && p[5] == c6 \\\n  && p[6] == c7 \\\n  && p[7] == c8 \\\n  && (p[8] == ' ' || p[8] == '\\t'))\n\n#define EQT7(c1,c2,c3,c4,c5,c6,c7) \\\n    (p[0] == c1 \\\n  && p[1] == c2 \\\n  && p[2] == c3 \\\n  && p[3] == c4 \\\n  && p[4] == c5 \\\n  && p[5] == c6 \\\n  && p[6] == c7)\n\n#endif /* ply_commoh_h */\n"
  },
  {
    "path": "src/io/ply/ply.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n Resources:\n   http://people.math.sc.edu/Burkardt/data/ply/ply.txt\n   http://paulbourke.net/dataformats/ply/\n   https://en.wikipedia.org/wiki/PLY_(file_format)\n   http://gamma.cs.unc.edu/POWERPLANT/papers/ply.pdf\n   https://people.sc.fsu.edu/~jburkardt/data/ply/ply.html            (samples)\n*/\n\n#include \"ply.h\"\n#include \"common.h\"\n#include \"util.h\"\n#include \"../../id.h\"\n#include \"../../data.h\"\n#include \"../../../include/ak/path.h\"\n#include \"../common/util.h\"\n#include \"../common/postscript.h\"\n#include \"../../strpool.h\"\n\nAK_HIDE\nAkResult\nply_ply(AkDoc ** __restrict dest, const char * __restrict filepath) {\n  AkHeap        *heap;\n  AkDoc         *doc;\n  void          *plystr;\n  char          *p, *b, *e;\n  AkLibrary     *lib_vscene;\n  AkVisualScene *scene;\n  PLYElement    *elem;\n  PLYProperty   *prop, *pit;\n  PLYState       pstVal = {0}, *pst;\n  size_t         plystrSize, off;\n  uint32_t       i;\n  bool           isAscii, isLittleEndian;\n  char           c;\n\n  if (ak_readfile(filepath, NULL, &plystr, &plystrSize) != AK_OK\n      || !((p = plystr) && *p != '\\0')) {\n    if (plystr)\n      ak_releasefile(plystr, plystrSize);\n    return AK_ERR;\n  }\n\n  if (!(tolower(p[0]) == 'p' && tolower(p[1]) == 'l' && tolower(p[2]) == 'y')) {\n    ak_releasefile(plystr, plystrSize);\n    return AK_ERR;\n  }\n  \n  p += 3;\n  /* c  = *p; */\n\n  NEXT_LINE\n\n  elem = NULL;\n  prop = NULL;\n  heap = ak_heap_new(NULL, NULL, NULL);\n  doc  = ak_heap_calloc(heap, NULL, sizeof(*doc));\n\n  /* for fixing skin and morph vertices */\n  doc->reserved = rb_newtree_ptr();\n  \n  doc->inf                = ak_heap_calloc(heap, doc, sizeof(*doc->inf));\n  doc->inf->name          = filepath;\n  doc->inf->dir           = ak_path_dir(heap, doc, filepath);\n  doc->inf->flipImage     = true;\n  doc->inf->ftype         = AK_FILE_TYPE_PLY;\n  doc->inf->base.coordSys = AK_YUP;\n  doc->coordSys           = AK_YUP; /* Default */\n  \n  ak_heap_setdata(heap, doc);\n  ak_id_newheap(heap);\n\n  /* libraries */\n  doc->lib.geometries = ak_heap_calloc(heap, doc, sizeof(AkLibrary));\n  lib_vscene          = ak_heap_calloc(heap, doc, sizeof(*lib_vscene));\n\n  /* default scene */\n  scene                  = ak_heap_calloc(heap, doc, sizeof(*scene));\n  scene->node            = ak_heap_calloc(heap, doc, sizeof(*scene->node));\n  lib_vscene->chld       = &scene->base;\n  lib_vscene->count      = 1;\n  doc->lib.visualScenes  = lib_vscene;\n  doc->scene.visualScene = ak_instanceMake(heap, doc, scene);\n\n  /* parse state */\n  memset(&pstVal, 0, sizeof(pstVal));\n  pst              = &pstVal;\n  pstVal.doc       = doc;\n  pstVal.heap      = heap;\n  pstVal.tmp = ak_heap_alloc(heap, doc, sizeof(void*));\n  pstVal.node      = scene->node;\n  pstVal.lib_geom  = doc->lib.geometries;\n\n  isAscii        = false;\n  isLittleEndian = false;\n  pst->end       = (char *)plystr + plystrSize;\n  \n  /* parse header */\n  do {\n    /* skip spaces */\n    SKIP_SPACES\n\n    /* parse format but ignore version (for now maybe) */\n    if (EQ6('f', 'o', 'r', 'm', 'a', 't')) {\n      p += 7;\n\n      SKIP_SPACES\n\n      if (EQ5('a', 's', 'c', 'i', 'i')) {\n        isAscii = true;\n      } else if (p[0] == 'b' && p[1] == 'i' && p[2] == 'n'\n                 && p[7] == 'l' && p[8] == 'i' && p[9] == 't') {\n        /* strncmp(p, \"binary_little_endian\", 20) == 0 */\n        isLittleEndian = true;\n      } else if (p[0] == 'b' && p[1] == 'i' && p[2] == 'n'\n                 && p[7] == 'b' && p[8] == 'i' && p[9] == 'g') {\n        /* strncmp(p, \"binary_big_endian\", 17) == 0 */\n        isLittleEndian = false;\n      } else {\n        goto err; /* unknown format */\n      }\n    } else if (EQ7('e', 'l', 'e', 'm', 'e', 'n', 't')) {\n      p += 8;\n\n      elem = ak_heap_calloc(heap, pst->tmp, sizeof(*elem));\n      \n      if (!pst->element)\n        pst->element = elem;\n\n      if (pst->lastElement)\n        pst->lastElement->next = elem;\n      pst->lastElement = elem;\n\n      if (EQ6('v', 'e', 'r', 't', 'e', 'x')) {\n        p += 7;\n        SKIP_SPACES\n        elem->count    = (uint32_t)strtoul(p, &p, 10);\n        elem->type     = PLY_ELEM_VERTEX;\n        pst->vertcount = elem->count;\n      } else if (EQ4('f', 'a', 'c', 'e')) {\n        p += 5;\n        SKIP_SPACES\n        elem->count = (uint32_t)strtoul(p, &p, 10);\n        elem->type  = PLY_ELEM_FACE;\n      }\n    } else if (elem && EQ8('p', 'r', 'o', 'p', 'e', 'r', 't', 'y')) {\n      p += 9;\n      SKIP_SPACES\n      \n      prop = ak_heap_calloc(heap, pst->tmp, sizeof(*prop));\n      \n      /* 1. type */\n      \n      b = p;\n      while ((c = *++p) != '\\0' && !AK_ARRAY_SPACE_CHECK);\n      e = p;\n      \n      prop->islist = b[0] == 'l'\n                  && b[1] == 'i'\n                  && b[2] == 's'\n                  && b[3] == 't';\n      \n      if (!prop->islist) {\n        prop->typestr = ak_heap_strndup(heap, doc, b, e - b);\n      } else {\n        /* 1.1 count type */\n        SKIP_SPACES\n        \n        b = p;\n        while ((c = *++p) != '\\0' && !AK_ARRAY_SEP_CHECK);\n        e = p;\n        \n        prop->listCountType     = ak_heap_strndup(heap, doc, b, e - b);\n        prop->listCountTypeDesc = ak_typeDescByName(prop->listCountType);\n\n        /* 1.2 type */\n        SKIP_SPACES\n        \n        b = p;\n        while ((c = *++p) != '\\0' && !AK_ARRAY_SEP_CHECK);\n        e = p;\n        \n        prop->typestr = ak_heap_strndup(heap, doc, b, e - b);\n      }\n      \n      prop->typeDesc = ak_typeDescByName(prop->typestr);\n      \n      /* 2. name */\n      \n      SKIP_SPACES\n      \n      b = p;\n      while ((c = *++p) != '\\0' && !AK_ARRAY_SEP_CHECK);\n      e = p;\n\n      prop->name = ak_heap_strndup(heap, doc, b, e - b);\n      \n      if (prop->typeDesc) {\n        elem->buffsize += prop->typeDesc->size;\n      } else if (!prop->islist && !isAscii) {\n        /* we cannot traverse the binary because we don't know some types */\n        goto err;\n      }\n\n      if (e - b == 1) {\n        switch (b[0]) {\n          case 'x': prop->semantic = PLY_PROP_X; break;\n          case 'y': prop->semantic = PLY_PROP_Y; break;\n          case 'z': prop->semantic = PLY_PROP_Z; break;\n          case 's':\n          case 'u': prop->semantic = PLY_PROP_S; break;\n          case 't':\n          case 'v': prop->semantic = PLY_PROP_T; break;\n          case 'r': prop->semantic = PLY_PROP_R; break;\n          case 'g': prop->semantic = PLY_PROP_G; break;\n          case 'b': prop->semantic = PLY_PROP_B; break;\n          default:\n            prop->semantic   = PLY_PROP_UNSUPPORTED;\n            prop->ignore = true;\n            break;\n        }\n      } else if (e - b == 2) {\n        switch (b[0]) {\n          case 'n':\n            switch (b[1]) {\n              case 'x': prop->semantic = PLY_PROP_NX; break;\n              case 'y': prop->semantic = PLY_PROP_NY; break;\n              case 'z': prop->semantic = PLY_PROP_NZ; break;\n              default:\n                prop->semantic   = PLY_PROP_UNSUPPORTED;\n                prop->ignore = true;\n                break;\n            }\n            break;\n          default:\n            prop->semantic   = PLY_PROP_UNSUPPORTED;\n            prop->ignore = true;\n            break;\n        }\n      }\n      \n      if (!elem->property) {\n        elem->property = prop;\n      } else {\n        PLYProperty *last_prop;\n        \n        /* insert propety by ORDER */\n        last_prop = pit = elem->property;\n        while (pit) {\n          if ((int)prop->semantic < (int)pit->semantic) {\n            if (pit->prev) {\n              pit->prev->next = prop;\n              prop->prev      = pit->prev;\n            }\n\n            prop->next = pit;\n            pit->prev  = prop;\n            \n            if (pit == elem->property)\n              elem->property = prop;\n            \n            break;\n          }\n\n          last_prop = pit;\n          pit       = pit->next;\n        }\n        \n        /* couldn't add, so add to last */\n        if (!pit && last_prop) {\n          last_prop->next = prop;\n          prop->prev      = last_prop;\n        }\n      }\n    } else if (EQT7('e', 'n', 'd', '_', 'h', 'e', 'a')) {\n      NEXT_LINE\n      break;\n    }\n\n    NEXT_LINE\n  } while (p && p[0] != '\\0'/* && (c = *++p) != '\\0'*/);\n\n  /* prepare property offsets/slots */\n  i    = 0;\n  off  = 0;\n  elem = pst->element;\n\n  while (elem) {\n    pit = elem->property;\n    if (elem->type == PLY_ELEM_VERTEX) {\n      size_t byteSffset;\n      \n      byteSffset = 0;\n      elem->buff = ak_heap_calloc(heap, pst->doc, sizeof(*elem->buff));\n\n      while (pit) {\n        if (pit->ignore)\n          goto ign;\n\n        /* validate, check missing properties in the group */\n        if (pit->semantic == PLY_PROP_X) {\n          if ((!pit->next || pit->next->semantic != PLY_PROP_Y)\n              ||(!pit->next->next || pit->next->next->semantic != PLY_PROP_Z))\n            goto err; /* we cannot load this PLY, TODO: */\n          \n          /* alloc input and accessor for positions */\n          pst->ac_pos = io_acc(heap, doc, AK_COMPONENT_SIZE_VEC3,\n                               AKT_FLOAT, elem->count, elem->buff);\n        }\n        \n        if (pit->semantic == PLY_PROP_NX) {\n          if ((!pit->next || pit->next->semantic != PLY_PROP_NY)\n              ||(!pit->next->next || pit->next->next->semantic != PLY_PROP_NZ)) {\n            pit->ignore = true;\n            \n            if (pit->next) {\n              pit->next->ignore = true;\n              if (pit->next->next)\n                pit->next->next->ignore = true;\n            }\n\n            goto ign; /* we cannot load this PLY, TODO: */\n          }\n          \n          /* alloc input and accessor for normals */\n          pst->ac_nor = io_acc(heap, doc, AK_COMPONENT_SIZE_VEC3,\n                               AKT_FLOAT, elem->count, elem->buff);\n        }\n        \n        if (pit->semantic == PLY_PROP_S) {\n          if (!pit->next || pit->next->semantic != PLY_PROP_T) {\n            pit->ignore = true;\n            if (pit->next && pit->next->next)\n              pit->next->ignore = true;\n            goto ign; /* ignore, TODO: */\n          }\n          \n          /* alloc input and accessor for tex coords */\n          pst->ac_tex = io_acc(heap, doc, AK_COMPONENT_SIZE_VEC2,\n                               AKT_FLOAT, elem->count, elem->buff);\n        }\n        \n        if (pit->semantic == PLY_PROP_R) {\n          if ((!pit->next || pit->next->semantic != PLY_PROP_G)\n              || (!pit->next->next || pit->next->next->semantic != PLY_PROP_B)) {\n            pit->ignore = true;\n            \n            if (pit->next) {\n              pit->next->ignore = true;\n              if (pit->next->next)\n                pit->next->next->ignore = true;\n            }\n\n            goto ign; /* ignore, TODO: */\n          }\n          \n          /* alloc input and accessor for vertex colors */\n          pst->ac_rgb = io_acc(heap, doc, AK_COMPONENT_SIZE_VEC3,\n                               AKT_FLOAT, elem->count, elem->buff);\n        }\n        \n        pit->slot = i++;\n        pit->off  = off;\n        /* TODO: currently all are floats */\n        off      += sizeof(float); /* pit->typeDesc->size; */\n        \n        if (pit->typeDesc)\n          pst->byteStride += pit->typeDesc->size;\n        elem->knownCount++;\n        \n      ign:\n        pit = pit->next;\n      }\n\n      /* empty buffer */\n      if (off < 1)\n        goto err;\n      \n      /* alloc buffer for vertex element */\n      pst->vertBuffsize  = off * elem->count;\n      elem->buff->length = pst->vertBuffsize;\n      elem->buff->data   = ak_heap_alloc(heap, elem->buff, elem->buff->length);\n      flist_sp_insert(&pst->doc->lib.buffers, elem->buff);\n\n      /* prepare accessors' misssing params */\n      if (pst->ac_pos) {\n        pst->ac_pos->byteLength = pst->vertBuffsize;\n        pst->ac_pos->byteStride = pst->byteStride;\n        byteSffset += sizeof(float) * 3;\n      }\n      \n      if (pst->ac_nor) {\n        pst->ac_nor->byteLength = pst->vertBuffsize;\n        pst->ac_nor->byteStride = pst->byteStride;\n        pst->ac_nor->byteOffset = byteSffset;\n        byteSffset += sizeof(float) * 3;\n      }\n\n      if (pst->ac_tex) {\n        pst->ac_tex->byteStride = pst->byteStride;\n        pst->ac_tex->byteLength = pst->vertBuffsize;\n        pst->ac_tex->byteOffset = byteSffset;\n        byteSffset += sizeof(float) * 2;\n      }\n       \n      if (pst->ac_rgb) {\n        pst->ac_rgb->byteStride = pst->byteStride;\n        pst->ac_rgb->byteLength = pst->vertBuffsize;\n        pst->ac_rgb->byteOffset = byteSffset;\n        /* byteSffset += sizeof(float) * 3; */\n      }\n    }\n\n    elem = elem->next;\n  }\n\n  /* parse */\n  if (isAscii) {\n    ply_ascii(p, pst);\n  } else {\n    ply_bin(p, pst, isLittleEndian);\n  }\n\n  io_postscript(doc);\n\n  *dest = doc;\n\n  /* cleanup */\n  ak_free(pst->tmp);\n  ak_releasefile(plystr, plystrSize);\n\n  return AK_OK;\n  \nerr:\n  ak_free(pst->tmp);\n  ak_free(doc);\n  ak_releasefile(plystr, plystrSize);\n  return AK_ERR;\n}\n\nAK_HIDE\nvoid\nply_finish(PLYState * __restrict pst) {\n  AkHeap             *heap;\n  AkGeometry         *geom;\n  AkMesh             *mesh;\n  AkMeshPrimitive    *prim;\n  AkInstanceGeometry *instGeom;\n  AkTriangles        *tri;\n\n  /* Buffer > Accessor > Input > Prim > Mesh > Geom > InstanceGeom > Node */\n  \n  heap = pst->heap;\n  mesh = ak_allocMesh(pst->heap, pst->lib_geom, &geom);\n\n  tri            = ak_heap_calloc(pst->heap, ak_objFrom(mesh), sizeof(*tri));\n  tri->mode      = AK_TRIANGLES;\n  tri->base.type = AK_PRIMITIVE_TRIANGLES;\n  prim           = (AkMeshPrimitive *)tri;\n\n  prim->indexStride    = 1;\n  prim->nPolygons      = pst->count;\n  prim->mesh           = mesh;\n  mesh->primitive      = prim;\n  mesh->primitiveCount = 1;\n\n  /* add to library */\n  geom->base.next      = pst->lib_geom->chld;\n  pst->lib_geom->chld  = &geom->base;\n  pst->lib_geom->count = 1;\n  \n  /* make instance geeometry and attach to the root node  */\n  instGeom = ak_instanceMakeGeom(heap, pst->node, geom);\n  if (pst->node->geometry) {\n    pst->node->geometry->base.prev = (void *)instGeom;\n    instGeom->base.next            = (void *)pst->node->geometry;\n  }\n\n  pst->node->geometry = instGeom;\n  \n  /* positions */\n  if (pst->ac_pos)\n    prim->pos = io_input(heap, prim, pst->ac_pos,\n                         AK_INPUT_POSITION, _s_POSITION, 0);\n\n  /* normals */\n  if (pst->ac_nor)\n    io_input(heap, prim, pst->ac_nor, AK_INPUT_NORMAL, _s_NORMAL, 0);\n\n  /* tex coords */\n  if (pst->ac_tex)\n    io_input(heap, prim, pst->ac_tex, AK_INPUT_TEXCOORD, _s_TEXCOORD, 0);\n  \n  /* vertex colors */\n  if (pst->ac_rgb)\n    io_input(heap, prim, pst->ac_rgb, AK_INPUT_COLOR, _s_COLOR, 0);\n  \n  /* indices */\n  prim->indices = ak_heap_calloc(heap,\n                                 tri,\n                                 sizeof(*prim->indices)\n                                 + pst->dc_ind->usedsize);\n  prim->indices->count = pst->dc_ind->itemcount;\n  ak_data_join(pst->dc_ind, prim->indices->items, 0, 0);\n}\n"
  },
  {
    "path": "src/io/ply/ply.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ply_h\n#define ply_h\n\n#include \"common.h\"\n\nAK_HIDE\nAkResult\nply_ply(AkDoc ** __restrict dest, const char * __restrict filepath);\n\nAK_HIDE\nvoid\nply_ascii(char * __restrict src, PLYState * __restrict pst);\n\nAK_HIDE\nvoid\nply_bin(char * __restrict src, PLYState * __restrict pst, bool le);\n\nAK_HIDE\nvoid\nply_finish(PLYState * __restrict pst);\n\n#endif /* stl_h */\n"
  },
  {
    "path": "src/io/ply/util.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ply_util_h\n#define ply_util_h\n\n#include \"common.h\"\n#include \"../../endian.h\"\n\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wstrict-aliasing\"\n\n#define ply_val(p, typeDesc, leEndian, T, DEST, DEFAULT)                      \\\n  do {                                                                        \\\n    uint64_t buf;                                                             \\\n                                                                              \\\n    buf = 0;                                                                  \\\n    switch (typeDesc->typeId) {                                               \\\n      case AKT_FLOAT:                                                         \\\n      case AKT_INT:                                                           \\\n      case AKT_UINT:   memcpy_endian32(leEndian, buf, p); break;              \\\n      case AKT_DOUBLE:                                                        \\\n      case AKT_INT64:                                                         \\\n      case AKT_UINT64: memcpy_endian64(leEndian, buf, p); break;              \\\n      case AKT_SHORT:                                                         \\\n      case AKT_USHORT: memcpy_endian16(leEndian, buf, p); break;              \\\n      case AKT_BYTE:                                                          \\\n      case AKT_UBYTE:  memcpy(&buf, p++, 1);              break;              \\\n      default:         DEST = DEFAULT;                    break;              \\\n    }                                                                         \\\n                                                                              \\\n    switch (typeDesc->typeId) {                                               \\\n      case AKT_FLOAT:  DEST = (T)(*(float    *)(void *)&buf);     break;      \\\n      case AKT_INT:    DEST = (T)(*(int32_t  *)(void *)&buf);     break;      \\\n      case AKT_UINT:   DEST = (T)(*(uint32_t *)(void *)&buf);     break;      \\\n      case AKT_DOUBLE: DEST = (T)(*(double   *)(void *)&buf);     break;      \\\n      case AKT_INT64:  DEST = (T)(*(int64_t  *)(void *)&buf);     break;      \\\n      case AKT_UINT64: DEST = (T)(*(uint64_t *)(void *)&buf);     break;      \\\n      case AKT_SHORT:  DEST = (T)(*(int16_t  *)(void *)&buf);     break;      \\\n      case AKT_USHORT: DEST = (T)(*(uint16_t *)(void *)&buf);     break;      \\\n      case AKT_BYTE:   DEST = (T)(*(int8_t   *)(void *)&buf);     break;      \\\n      case AKT_UBYTE:  DEST = (T)(*(uint8_t  *)(void *)&buf);     break;      \\\n      default:         DEST = DEFAULT;                    break;              \\\n    }                                                                         \\\n  } while (0)\n\n#pragma GCC diagnostic pop\n\n#endif /* ply_util_h */\n"
  },
  {
    "path": "src/io/stl/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/io/stl/README.md",
    "content": "# AssetKit: Standard Tesselated Geometry File Format (stl) Status\n\n- [x] ASCII\n- [x] Binary\n"
  },
  {
    "path": "src/io/stl/common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef stl_commoh_h\n#define stl_commoh_h\n\n#include \"../../../include/ak/assetkit.h\"\n#include \"../../common.h\"\n#include \"../../utils.h\"\n#include \"../../tree.h\"\n#include \"../../json.h\"\n#include \"../../data.h\"\n\n#include <string.h>\n#include <stdlib.h>\n\ntypedef struct STLState {\n  AkHeap        *heap;\n  AkDoc         *doc;\n  void          *tmp;\n  AkLibrary     *lib_geom;\n  AkGeometry    *geom;\n  AkDataContext *dc_ind, *dc_pos, *dc_nor, *dc_vcount;\n  AkNode        *node;\n  uint32_t       maxVC;\n  uint32_t       count;\n} STLState;\n\n#ifdef SKIP_SPACES\n# undef SKIP_SPACES\n#endif\n\n#define SKIP_SPACES                                                           \\\n  {                                                                           \\\n    while (c != '\\0' && AK_ARRAY_SPACE_CHECK) c = *++p;                       \\\n    if (c == '\\0')                                                            \\\n      break; /* to break loop */                                              \\\n  }\n\n#ifdef NEXT_LINE\n# undef NEXT_LINE\n#endif\n\n#define NEXT_LINE                                                             \\\n  do {                                                                        \\\n    c = p ? *p : '\\0';                                                        \\\n    while (p                                                                  \\\n           && p[0] != '\\0'                                                    \\\n           && !AK_ARRAY_NLINE_CHECK                                           \\\n           && (c = *++p) != '\\0'                                              \\\n           && !AK_ARRAY_NLINE_CHECK);                                         \\\n                                                                              \\\n    while (p                                                                  \\\n           && p[0] != '\\0'                                                    \\\n           && AK_ARRAY_NLINE_CHECK                                            \\\n           && (c = *++p) != '\\0'                                              \\\n           && AK_ARRAY_NLINE_CHECK);                                          \\\n  } while(0);\n\n#define EQ4(c1,c2,c3,c4) \\\n    (p[0] == c1 \\\n  && p[1] == c2 \\\n  && p[2] == c3 \\\n  && p[3] == c4 \\\n  && (p[4] == ' ' || p[4] == '\\t'))\n\n#define EQ5(c1,c2,c3,c4,c5) \\\n    (p[0] == c1 \\\n  && p[1] == c2 \\\n  && p[2] == c3 \\\n  && p[3] == c4 \\\n  && p[4] == c5 \\\n  && (p[5] == ' ' || p[5] == '\\t'))\n\n#define EQ6(c1,c2,c3,c4,c5,c6) \\\n    (p[0] == c1 \\\n  && p[1] == c2 \\\n  && p[2] == c3 \\\n  && p[3] == c4 \\\n  && p[4] == c5 \\\n  && p[5] == c6 \\\n  && (p[6] == ' ' || p[6] == '\\t'))\n\n#define EQ7(c1,c2,c3,c4,c5,c6,c7) \\\n    (p[0] == c1 \\\n  && p[1] == c2 \\\n  && p[2] == c3 \\\n  && p[3] == c4 \\\n  && p[4] == c5 \\\n  && p[5] == c6 \\\n  && p[6] == c7 \\\n  && (p[7] == ' ' || p[7] == '\\t'))\n\n#define EQT7(c1,c2,c3,c4,c5,c6,c7) \\\n    (p[0] == c1 \\\n  && p[1] == c2 \\\n  && p[2] == c3 \\\n  && p[3] == c4 \\\n  && p[4] == c5 \\\n  && p[5] == c6 \\\n  && p[6] == c7)\n\n#endif /* stl_commoh_h */\n"
  },
  {
    "path": "src/io/stl/stl.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n Resources:\n   https://all3dp.com/what-is-stl-file-format-extension-3d-printing/\n   https://danbscott.ghost.io/writing-an-stl-file-from-scratch/\n   https://en.wikipedia.org/wiki/STL_%28file_format%29\n*/\n\n#include \"stl.h\"\n#include \"common.h\"\n#include \"../../id.h\"\n#include \"../../data.h\"\n#include \"../../../include/ak/path.h\"\n#include \"../common/util.h\"\n#include \"../common/postscript.h\"\n#include \"../../endian.h\"\n#include \"../../strpool.h\"\n\nAK_HIDE\nAkResult\nstl_stl(AkDoc     ** __restrict dest,\n        const char * __restrict filepath) {\n  AkHeap        *heap;\n  AkDoc         *doc;\n  void          *stlstr;\n  char          *p;\n  AkLibrary     *lib_vscene;\n  AkVisualScene *scene;\n  STLState       sstVal = {0}, *sst;\n  size_t         stlstrSize;\n  bool           isAscii;\n\n  if (ak_readfile(filepath, NULL, &stlstr, &stlstrSize) != AK_OK\n      || !((p = stlstr) && *p != '\\0'))\n    return AK_ERR;\n\n  if (p[0] == 's'\n   && p[1] == 'o'\n   && p[2] == 'l'\n   && p[3] == 'i'\n   && p[4] == 'd') {\n    isAscii = true;\n  } else if (p[0] != '\\0' && stlstrSize > 80) {\n    isAscii = false;\n  } else {\n    return AK_ERR;\n  }\n  \n  heap  = ak_heap_new(NULL, NULL, NULL);\n  doc   = ak_heap_calloc(heap, NULL, sizeof(*doc));\n\n  doc->inf                = ak_heap_calloc(heap, doc, sizeof(*doc->inf));\n  doc->inf->name          = filepath;\n  doc->inf->dir           = ak_path_dir(heap, doc, filepath);\n  doc->inf->flipImage     = true;\n  doc->inf->ftype         = AK_FILE_TYPE_STL;\n  doc->inf->base.coordSys = AK_YUP;\n  doc->coordSys           = AK_YUP; /* Default */\n  \n  ak_heap_setdata(heap, doc);\n  ak_id_newheap(heap);\n\n  /* libraries */\n  doc->lib.geometries = ak_heap_calloc(heap, doc, sizeof(AkGeometry));\n  lib_vscene = ak_heap_calloc(heap, doc, sizeof(*lib_vscene));\n  \n  /* default scene */\n  scene                  = ak_heap_calloc(heap, doc, sizeof(*scene));\n  scene->node            = ak_heap_calloc(heap, doc, sizeof(*scene->node));\n  lib_vscene->chld       = &scene->base;\n  lib_vscene->count      = 1;\n  doc->lib.visualScenes  = lib_vscene;\n  doc->scene.visualScene = ak_instanceMake(heap, doc, scene);\n\n  /* parse state */\n  memset(&sstVal, 0, sizeof(sstVal));\n  sst              = &sstVal;\n  sstVal.doc       = doc;\n  sstVal.heap      = heap;\n  sstVal.tmp       = ak_heap_alloc(heap, doc, sizeof(void*));\n  sstVal.node      = scene->node;\n  sstVal.lib_geom  = doc->lib.geometries;\n  \n  sst->dc_ind    = ak_data_new(sst->tmp, 128, sizeof(int32_t), NULL);\n  sst->dc_pos    = ak_data_new(sst->tmp, 128, sizeof(vec3),    NULL);\n  sst->dc_nor    = ak_data_new(sst->tmp, 128, sizeof(vec3),    NULL);\n  sst->dc_vcount = ak_data_new(sst->tmp, 128, sizeof(int32_t), NULL);\n\n  if (!isAscii) {\n    stl_binary(sst, p);\n  } else {\n    stl_ascii(sst, p);\n  }\n  \n  sst_finish(sst);\n  io_postscript(doc);\n  \n  *dest = doc;\n\n  /* cleanup */\n  ak_free(sst->tmp);\n  ak_releasefile(stlstr, stlstrSize);\n\n  return AK_OK;\n}\n\nAK_HIDE\nvoid\nstl_binary(STLState * __restrict sst, char * __restrict p) {\n  vec4     v, n;\n  uint32_t count,  nTriangles, i;\n  \n  /* skip 80-char header */\n  p += 80;\n\n  /* parse integers from little endian to native */\n  le_32(nTriangles, p);\n\n  count      = nTriangles * 3;\n  sst->maxVC = 3;\n\n  for (i = 0; i < nTriangles; i++) {\n    /* normal */\n    le_32(n[0], p);\n    le_32(n[1], p);\n    le_32(n[2], p);\n    \n    ak_data_append(sst->dc_nor, n);\n    ak_data_append(sst->dc_nor, n);\n    ak_data_append(sst->dc_nor, n);\n    \n    /* vertex */\n    le_32(v[0], p);\n    le_32(v[1], p);\n    le_32(v[2], p);\n    ak_data_append(sst->dc_pos, v);\n    \n    le_32(v[0], p);\n    le_32(v[1], p);\n    le_32(v[2], p);\n    ak_data_append(sst->dc_pos, v);\n    \n    le_32(v[0], p);\n    le_32(v[1], p);\n    le_32(v[2], p);\n    ak_data_append(sst->dc_pos, v);\n    p += 2;\n  }\n  \n  sst->count = count;\n}\n\nAK_HIDE\nvoid\nstl_ascii(STLState * __restrict sst, char * __restrict p) {\n  vec4     v, n;\n  uint32_t vc, count;\n  char     c;\n\n  /* c     = '\\0'; */\n  count = 0;\n\n  NEXT_LINE\n\n  /* parse ASCII STL */\n  do {\n    /* skip spaces */\n    SKIP_SPACES\n\n    if (EQ5('f', 'a', 'c', 'e', 't')) {\n      p += 6;\n      \n      SKIP_SPACES\n      \n      if (EQ6('n', 'o', 'r', 'm', 'a', 'l')) {\n        p += 7;\n        memset(v, 0, sizeof(vec4));\n        ak_strtof_line(p, 0, 3, n);\n        /* ak_data_append(sst->dc_nor, n); */\n        \n        NEXT_LINE\n        SKIP_SPACES\n        \n        /* parse each vertex */\n        if (EQ5('o', 'u', 't', 'e', 'r')) {\n          NEXT_LINE\n          \n          vc = 0;\n          \n          /* parse vertices */\n          while (c != '\\0') {\n            SKIP_SPACES\n            if (EQ6('v', 'e', 'r', 't', 'e', 'x')) {\n              p += 7;\n              memset(v, 0, sizeof(vec4));\n              ak_strtof_line(p, 0, 3, v);\n              ak_data_append(sst->dc_nor, n); /* duplicate normal */\n              ak_data_append(sst->dc_pos, v);\n\n              vc++;\n            } else if (EQT7('e', 'n', 'd', 'l', 'o', 'o', 'p')) {\n              break;\n            }\n\n            NEXT_LINE\n          } /* vertex */\n\n          count += vc;\n          sst->maxVC = GLM_MAX(sst->maxVC, vc);\n          ak_data_append(sst->dc_vcount, &vc);\n        } /* outer loop */\n      } /* normal */\n    } /* facet */\n\n    NEXT_LINE\n  } while (p && p[0] != '\\0'/* && (c = *++p) != '\\0'*/);\n  \n  sst->count = count;\n}\n\nAK_HIDE\nvoid\nsst_finish(STLState * __restrict sst) {\n  AkHeap             *heap;\n  AkGeometry         *geom;\n  AkMesh             *mesh;\n  AkMeshPrimitive    *prim;\n  AkInstanceGeometry *instGeom;\n\n  /* Buffer > Accessor > Input > Prim > Mesh > Geom > InstanceGeom > Node */\n  \n  heap = sst->heap;\n  mesh = ak_allocMesh(sst->heap, sst->lib_geom, &geom);\n\n  if (sst->maxVC == 3) {\n    AkTriangles *tri;\n    \n    tri = ak_heap_calloc(sst->heap, ak_objFrom(mesh), sizeof(*tri));\n    tri->mode      = AK_TRIANGLES;\n    tri->base.type = AK_PRIMITIVE_TRIANGLES;\n    prim = (AkMeshPrimitive *)tri;\n  } else {\n    AkPolygon *poly;\n    \n    poly = ak_heap_calloc(sst->heap, ak_objFrom(mesh), sizeof(*poly));\n    poly->base.type = AK_PRIMITIVE_POLYGONS;\n    \n    poly->vcount = ak_heap_calloc(heap,\n                                  poly,\n                                  sizeof(*poly->vcount)\n                                  + sst->dc_vcount->usedsize);\n    poly->vcount->count = sst->dc_vcount->itemcount;\n    ak_data_join(sst->dc_vcount, poly->vcount->items, 0, 0);\n    \n    prim = (AkMeshPrimitive *)poly;\n  }\n  \n  prim->nPolygons      = sst->count;\n  prim->mesh           = mesh;\n  mesh->primitive      = prim;\n  mesh->primitiveCount = 1;\n\n  /* add to library */\n  geom->base.next      = sst->lib_geom->chld;\n  sst->lib_geom->chld  = &geom->base;\n  sst->lib_geom->count = 1;\n  \n  /* make instance geeometry and attach to the root node  */\n  instGeom = ak_instanceMakeGeom(heap, sst->node, geom);\n  if (sst->node->geometry) {\n    sst->node->geometry->base.prev = (void *)instGeom;\n    instGeom->base.next            = (void *)sst->node->geometry;\n  }\n\n  sst->node->geometry = instGeom;\n  \n  prim->pos = io_addInput(heap, sst->dc_pos, prim, AK_INPUT_POSITION,\n                          _s_POSITION, AK_COMPONENT_SIZE_VEC3, AKT_FLOAT, 0);\n\n  if (sst->dc_nor->itemcount > 0) {\n    io_addInput(heap, sst->dc_nor, prim, AK_INPUT_NORMAL,\n                _s_NORMAL, AK_COMPONENT_SIZE_VEC3, AKT_FLOAT, 1);\n  }\n\n  /* cleanup */\n  if (sst->dc_ind) {\n    ak_free(sst->dc_ind);\n    ak_free(sst->dc_pos);\n    ak_free(sst->dc_nor);\n    ak_free(sst->dc_vcount);\n  }\n  \n  sst->dc_ind    = NULL;\n  sst->dc_pos    = NULL;\n  sst->dc_nor    = NULL;\n  sst->dc_vcount = NULL;\n}\n"
  },
  {
    "path": "src/io/stl/stl.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef stl_h\n#define stl_h\n\n#include \"common.h\"\n\nAK_HIDE\nAkResult\nstl_stl(AkDoc     ** __restrict dest,\n        const char * __restrict filepath);\n\nAK_HIDE\nvoid\nsst_finish(STLState * __restrict sst);\n\nAK_HIDE\nvoid\nstl_ascii(STLState * __restrict sst, char * __restrict p);\n\nAK_HIDE\nvoid\nstl_binary(STLState * __restrict sst, char * __restrict p);\n\n#endif /* stl_h */\n"
  },
  {
    "path": "src/json.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_json_h\n#define ak_json_h\n\n#include <string.h>\n#include <stdlib.h>\n\n/* JSON parser */\n#include <json/json.h>\n\nAK_INLINE\nchar *\njson_strdup(const json_t * __restrict jsonObject,\n            AkHeap       * __restrict heap,\n            void         * __restrict parent) {\n  return ak_heap_strndup(heap,\n                         parent,\n                         json_string(jsonObject),\n                         jsonObject->valsize);\n}\n\nAK_INLINE\nvoid\njson_array_set(void         * __restrict p,\n               AkTypeId                  typeId,\n               int                       index,\n               const json_t * __restrict json) {\n  switch (typeId) {\n    case AKT_FLOAT:\n      ((float *)p)[index] = json_float(json, 0.0f);\n      break;\n    case AKT_INT:\n      ((int32_t *)p)[index] = json_int32(json, 0);\n      break;\n    case AKT_UINT:\n      ((int32_t *)p)[index] = json_uint32(json, 0);\n      break;\n    case AKT_SHORT:\n      ((int16_t *)p)[index] = json_int32(json, 0);\n      break;\n    case AKT_USHORT:\n      ((uint16_t *)p)[index] = json_uint32(json, 0);\n      break;\n    case AKT_BYTE:\n      ((char *)p)[index] = json_int32(json, 0);\n      break;\n    case AKT_UBYTE:\n      ((unsigned char *)p)[index] = json_uint32(json, 0);\n      break;\n    default:\n      break;\n  }\n}\n\n#endif /* ak_json_h */\n"
  },
  {
    "path": "src/lib/CMakeLists.txt",
    "content": "target_sources(${PROJECT_NAME} \n  PRIVATE\n  geom.c\n  lib.c\n)\n"
  },
  {
    "path": "src/lib/geom.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n\nAK_EXPORT\nAkGeometry *\nak_libFirstGeom(AkDoc * __restrict doc) {\n  if (!doc->lib.geometries)\n    return NULL;\n\n  return (void *)doc->lib.geometries->chld;\n}\n"
  },
  {
    "path": "src/lib/lib.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n\nAK_EXPORT\nAkResult\nak_libAddCamera(AkDoc * __restrict doc, AkCamera * __restrict cam) {\n  AkHeap    *heap;\n  AkLibrary *libItem;\n  AkCamera  *cami;\n\n  heap    = ak_heap_getheap(doc);\n  libItem = doc->lib.cameras;\n  if (!libItem) {\n    libItem = ak_heap_calloc(heap, doc, sizeof(*libItem));\n    doc->lib.cameras = libItem;\n  }\n\n  cami = (void *)libItem->chld;\n  if (cami) {\n    cam->base.next = &cami->base;\n  }\n\n  libItem->chld = (void *)cam;\n  /* Increment on every add — earlier this was set to 1 on library\n     creation only, so adding a second / third camera left\n     `count` at 1 and downstream code that sized arrays from this\n     value silently truncated. */\n  libItem->count++;\n\n  return AK_OK;\n}\n\nAK_EXPORT\nAkResult\nak_libAddLight(AkDoc   * __restrict doc,\n               AkLight * __restrict light) {\n  AkHeap    *heap;\n  AkLibrary *libItem;\n  AkLight   *lighti;\n\n  heap    = ak_heap_getheap(doc);\n  libItem = doc->lib.lights;\n  if (!libItem) {\n    libItem = ak_heap_calloc(heap, doc, sizeof(*libItem));\n    doc->lib.lights = libItem;\n  }\n\n  lighti = (void *)libItem->chld;\n  if (lighti) {\n    light->next = lighti;\n  }\n\n  libItem->chld = (void *)light;\n  libItem->count++;\n\n  return AK_OK;\n}\n\nAK_EXPORT\nvoid\nak_libInsertInto(AkLibrary *lib,\n                 void      *item,\n                 int32_t    prevOffset,\n                 int32_t    nextOffset) {\n  char *libChld, *lastLibChld;\n\n  libChld = lastLibChld = (void *)lib->chld;\n  while (libChld) {\n    libChld = *(char **)(lastLibChld + nextOffset);\n    if (libChld)\n      lastLibChld = libChld;\n  }\n\n  if (lastLibChld)\n    *(void **)(lastLibChld + nextOffset) = item;\n  else\n    lib->chld = item;\n\n  if (prevOffset > -1)\n    *(void **)((char *)item + prevOffset) = lastLibChld;\n}\n\nAK_EXPORT\nAkLibrary*\nak_libFirstOrCreat(AkDoc * __restrict doc,\n                   uint32_t           itemOffset) {\n  AkHeap    *heap;\n  AkLibrary *lib;\n\n  heap = ak_heap_getheap(doc);\n  lib  = *(void **)((char *)&doc->lib + itemOffset);\n  if (lib)\n    return lib;\n\n  lib = ak_heap_calloc(heap,\n                       doc,\n                       sizeof(*lib));\n  doc->lib.libimages = lib;\n  return lib;\n}\n\nAK_EXPORT\nAkLibrary*\nak_libImageFirstOrCreat(AkDoc * __restrict doc) {\n  return ak_libFirstOrCreat(doc, offsetof(AkLibraries, libimages));\n}\n"
  },
  {
    "path": "src/light/CMakeLists.txt",
    "content": "target_sources(${PROJECT_NAME} \n  PRIVATE\n  light.c\n)\n"
  },
  {
    "path": "src/light/light.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../default/light.h\"\n#include <cglm/cglm.h>\n\n/* this duplicates default light to new light,\n   because we want to keep default not modified,\n   users may want to modify imported light,\n   they don't know this is default or not,\n   and we don't wan to force them to check lights are default or not.\n */\nAK_EXPORT\nAkLight *\nak_lightMake(AkDoc * __restrict doc,\n             void  * __restrict memparent,\n             AkLightType type) {\n  AkHeap      *heap;\n  AkLight     *light;\n  AkLightBase *base;\n  size_t       baseSize;\n\n  if (!doc) return NULL;\n  heap = ak_heap_getheap(doc);\n\n  /* Pick the right concrete sub-struct for the type. Ambient and\n     directional don't have extra fields beyond AkLightBase, so they\n     share its allocation. Point and Spot extend the base with\n     attenuation (and Spot adds falloff). */\n  switch (type) {\n    case AK_LIGHT_TYPE_POINT: baseSize = sizeof(AkPointLight); break;\n    case AK_LIGHT_TYPE_SPOT:  baseSize = sizeof(AkSpotLight);  break;\n    default:                  baseSize = sizeof(AkLightBase);  break;\n  }\n\n  light          = ak_heap_calloc(heap, memparent ? memparent : (void *)doc,\n                                        sizeof(*light));\n  light->tcommon = ak_heap_calloc(heap, light, baseSize);\n  base           = light->tcommon;\n  base->type     = type;\n\n  /* White color, full alpha — sensible default for any light kind. */\n  base->color.rgba.R = 1.0f;\n  base->color.rgba.G = 1.0f;\n  base->color.rgba.B = 1.0f;\n  base->color.rgba.A = 1.0f;\n  base->intensity    = 1.0f;\n  base->range        = 0.0f;\n\n  /* Point downward by convention. Ignored for ambient/point but\n     harmless to set; directional/spot use it as the beam axis. */\n  base->direction[0] =  0.0f;\n  base->direction[1] = -1.0f;\n  base->direction[2] =  0.0f;\n\n  /* Per-type attenuation defaults. constAttn=1 means \"no attenuation\n     at the source\"; linear/quad stay 0 so the light reaches forever\n     until the consumer caps the range. Spot adds a 30° cone with\n     linear falloff. */\n  if (type == AK_LIGHT_TYPE_POINT) {\n    AkPointLight *p = (AkPointLight *)base;\n    p->constAttn  = 1.0f;\n    p->linearAttn = 0.0f;\n    p->quadAttn   = 0.0f;\n  } else if (type == AK_LIGHT_TYPE_SPOT) {\n    AkSpotLight *s = (AkSpotLight *)base;\n    s->constAttn    = 1.0f;\n    s->linearAttn   = 0.0f;\n    s->quadAttn     = 0.0f;\n    s->innerConeAngle = 0.0f;\n    s->outerConeAngle = GLM_PI_4f;\n    s->falloffAngle = glm_rad(30.0f);\n    s->falloffExp   = 1.0f;\n  }\n\n  ak_libAddLight(doc, light);\n  return light;\n}\n\nAK_EXPORT\nAkLight*\nak_defaultLight(void * __restrict memparent) {\n  AkHeap        *heap;\n  AkDoc         *doc;\n  AkLight       *light;\n  AkCoordSys    *coordsys;\n  const AkLight *deflight;\n\n  deflight = ak_def_light();\n\n  if (memparent)\n    heap  = ak_heap_getheap(memparent);\n  else\n    heap = ak_heap_default();\n\n  doc = ak_heap_data(heap);\n\n  light = ak_heap_calloc(heap,\n                         memparent,\n                         sizeof(*light));\n  memcpy(light, deflight, sizeof(*deflight));\n\n  light->tcommon = ak_heap_calloc(heap,\n                                  light,\n                                  sizeof(AkDirectionalLight));\n\n  memcpy(light->tcommon,\n         deflight->tcommon,\n         sizeof(AkDirectionalLight));\n\n  /* convert light direction */\n  if (ak_opt_get(AK_OPT_COORD_CONVERT_TYPE) != AK_COORD_CVT_DISABLED)\n    coordsys = (void *)ak_opt_get(AK_OPT_COORD);\n  else\n    coordsys = doc->coordSys;\n\n  ak_coordCvtVector(AK_YUP,\n                    light->tcommon->direction,\n                    coordsys);\n  return light;\n}\n"
  },
  {
    "path": "src/main.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n#include \"mem/rb.h\"\n#include \"mem/lt.h\"\n#include \"trash.h\"\n#include \"sid.h\"\n#include \"profile.h\"\n#include \"type.h\"\n#include \"resc/resource.h\"\n\nvoid\nAK_CONSTRUCTOR\nak__init(void);\n\nvoid\nAK_DESTRUCTOR\nak__cleanup(void);\n\nvoid\nAK_CONSTRUCTOR\nak__init(void) {\n  ak_mem_init();\n  ak_trash_init();\n  ak_resc_init();\n  ak_type_init();\n  ak_sid_init();\n  ak_profile_init();\n}\n\nvoid\nAK_DESTRUCTOR\nak__cleanup(void) {\n  ak_profile_deinit();\n  ak_sid_deinit();\n  ak_type_deinit();\n  ak_resc_deinit();\n  ak_trash_deinit();\n  ak_mem_deinit();\n}\n"
  },
  {
    "path": "src/map.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../include/ak/map.h\"\n#include \"../include/ak/util.h\"\n#include \"mem/common.h\"\n\nAkMap *\nak_map_new(AkMapCmp cmp) {\n  AkMap  *map;\n  AkHeap *heap;\n  heap = ak_heap_new(NULL, cmp ? cmp : ak_cmp_ptr, NULL);\n\n  map = ak_heap_alloc(heap, NULL, sizeof(*map));\n  ak_heap_setdata(heap, map);\n\n  map->heap = heap;\n  map->root = NULL;\n  return map;\n}\n\nvoid\nak_map_addptr(AkMap *map, void *ptr) {\n  AkHeapNode *hnode;\n  AkMapItem  *mi;\n  AkResult    ret;\n\n  ret = ak_heap_getNodeById(map->heap, ptr, &hnode);\n  if (ret == AK_OK)\n    return;\n\n  mi = ak_heap_alloc(map->heap, NULL, sizeof(*mi));\n  ak_heap_setId(map->heap, ak__alignof(mi), ptr);\n\n  mi->prev = NULL;\n  mi->next = map->root;\n  if (map->root)\n    map->root->prev = mi;\n  map->root = mi;\n}\n\nvoid*\nak_map_find(AkMap *map, void *id) {\n  AkMapItem *mi;\n\n  mi = ak_map_findm(map, id);\n  if (mi)\n    return mi->data;\n\n  return NULL;\n}\n\nAkMapItem*\nak_map_findm(AkMap *map, void *id) {\n  void       *mem;\n  AkResult    ret;\n\n  ret = ak_heap_getMemById(map->heap, id, &mem);\n  if (ret == AK_OK)\n    return mem;\n\n  return NULL;\n}\n\nvoid\nak_map_add(AkMap *map,\n           void  *value,\n           void  *id) {\n  AkHeapNode *hnode;\n  AkMapItem  *mi;\n  AkResult    ret;\n\n  ret = ak_heap_getNodeById(map->heap, id, &hnode);\n  if (ret == AK_OK)\n    return;\n\n  mi = ak_heap_alloc(map->heap, NULL, sizeof(*mi));\n  mi->data = value;\n\n  ak_heap_setId(map->heap, ak__alignof(mi), id);\n\n  mi->prev = NULL;\n  mi->next = map->root;\n  if (map->root)\n    map->root->prev = mi;\n  map->root = mi;\n}\n\nvoid\nak_multimap_add(AkMap *map,\n                void  *value,\n                void  *id) {\n  AkHeapNode *hnode;\n  AkMapItem  *mi;\n  AkMapItem  *subItm;\n  AkResult    ret;\n\n  ret = ak_heap_getNodeById(map->heap, id, &hnode);\n  if (ret == AK_OK) {\n    AkMapItem *mii;\n\n    mi     = ak__alignas(hnode);\n    subItm = ak_heap_calloc(map->heap, NULL, sizeof(*mi));\n    subItm->data = value;\n\n    mii = (AkMapItem *)mi->data;\n    if (mii)\n      mii->prev = subItm;\n\n    subItm->next = mii;\n    mi->data     = subItm;\n    return;\n  }\n\n  mi     = ak_heap_calloc(map->heap, NULL, sizeof(*mi));\n  subItm = ak_heap_calloc(map->heap, NULL, sizeof(*mi));\n\n  mi->isMapItem = true;\n  mi->data      = subItm;\n  subItm->data  = value;\n\n  ak_heap_setId(map->heap, ak__alignof(mi), id);\n\n  mi->prev = NULL;\n  mi->next = map->root;\n  if (map->root)\n    map->root->prev = mi;\n  map->root = mi;\n}\n\nvoid\nak_map_destroy(AkMap *map) {\n  ak_free(map);\n}\n"
  },
  {
    "path": "src/mat/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME}\n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/mat/mat.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../../include/ak/material.h\"\n\nAK_EXPORT\nAkMaterialVariant*\nak_materialVariantByName(AkDoc       * __restrict doc,\n                         const char  * __restrict name) {\n  AkMaterialVariant *it;\n\n  if (!doc || !name)\n    return NULL;\n\n  it = doc->materialVariants;\n  while (it) {\n    if (it->name && strcmp(it->name, name) == 0)\n      return it;\n    it = it->next;\n  }\n\n  return NULL;\n}\n\nAK_EXPORT\nAkEffect*\nak_effectForBindMaterial(AkBindMaterial      * __restrict bindMat,\n                         AkMeshPrimitive     * __restrict meshPrim,\n                         AkInstanceMaterial ** __restrict foundInstMat) {\n  AkMaterial         *material;\n  AkInstanceMaterial *materialInst;\n  AkGeometry         *geom;\n  AkMap              *materialMap;\n  AkMapItem          *mi;\n\n  if (!meshPrim || !meshPrim->mesh || !meshPrim->mesh->geom)\n    return NULL;\n\n  geom         = meshPrim->mesh->geom;\n  materialInst = bindMat->tcommon;\n  materialMap  = geom->materialMap;\n\n  while (materialInst) {\n    /* there is symbol, bind only to specified primitive */\n    if (materialInst->symbol) {\n      mi = ak_map_find(materialMap, (void *)materialInst->symbol);\n      while (mi) {\n        if ((AkMeshPrimitive *)mi->data == meshPrim) {\n          material = ak_instanceObject(&materialInst->base);\n          if (material && material->effect) {\n            *foundInstMat = materialInst;\n            return ak_instanceObject(&material->effect->base);\n          } else {\n            return NULL;\n          }\n        }\n        mi = mi->next;\n      }\n    }\n\n    /* bind to whole geometry, TODO: is this OK ? */\n    else {\n      material = ak_instanceObject(&materialInst->base);\n      if (material && material->effect) {\n        *foundInstMat = materialInst;\n        return ak_instanceObject(&material->effect->base);\n      } else {\n        return NULL;\n      }\n    }\n\n    materialInst = (AkInstanceMaterial *)materialInst->base.next;\n  }\n\n  return NULL;\n}\n"
  },
  {
    "path": "src/mem/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/mem/common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_memory_h\n#define ak_memory_h\n\n#include \"../../include/ak/memory.h\"\n#include \"../common.h\"\n#include <ds/allocator.h>\n\n#define ak__align_size 8\n#define ak__heapnd_sz  offsetof(AkHeapNode, data)\n\n#define ak__align(size) ((size + ak__align_size - 1)                          \\\n&~ (uintptr_t)(ak__align_size - 1))\n\n#define ak__alignof(p) ((AkHeapNode *)(((char *)p) - ak__heapnd_sz))\n#define ak__alignas(m) ((void *)(((char *)m) + ak__heapnd_sz))\n\n#define AK__BST_LEFT  0\n#define AK__BST_RIGHT 1\n\n/*\n * Binary Search Tree Node (Red Black)\n */\ntypedef struct AkHeapSrchNode {\n  void                  *key;\n  struct AkHeapSrchNode *chld[2];\n} AkHeapSrchNode;\n\nstruct AkHeapSrchCtx {\n  AkHeapSrchNode   *root;\n  AkHeapSrchNode   *nullNode;\n  AkHeapSrchCmpFn   cmp;\n  AkHeapSrchPrintFn print;\n};\n\ntypedef struct AkSIDNode {\n  /*\n   | offset0  | sid_ptr0  | ...\n   | uint16_t | uintptr_t | ...\n   */\n  void             *sids;\n  void             *refs;\n  const char       *sid;\n} AkSIDNode;\n\ntypedef struct AkUrlNode {\n  size_t  len;\n  void  **urls;\n} AkUrlNode;\n\ntypedef struct AkMemoryMapNode {\n  struct AkMemoryMapNode *prev;\n  struct AkMemoryMapNode *next;\n  void                   *mapped;\n  size_t                  sized;\n} AkMemoryMapNode;\n\n#define AK__HEAPNODE(X)                                                       \\\n  (((AkHeapNodeExt *)((char *)X - offsetof(AkHeapNodeExt, data)))->node)\n\n/*\n - prev - AkHeapNode - next -\n               |\n    AkHeapNode o AkHeapNodeExt\n               |\n             chld\n               |               */\nstruct AkHeapNode {\n  AkHeapNode *prev; /* parent */\n  AkHeapNode *next; /* right  */\n  void       *chld; /* left   */\n  uint32_t    heapid;\n  uint16_t    typeid;\n  uint16_t    flags;\n  char        data[];\n};\n\n\n/*\n - prev - AkHeapNode - next -\n              |\n        AkHeapNodeExt - data\n              |\n            chld\n              |\n\ndata: data must contain items with these order with these data types\n*-----------------------------------------------------------------------------*\n| id       | sid     | refc   | extra     | inf       | usr       | url       |\n| SrchNode | SidNode | size_t | uintptr_t | uintptr_t | uintptr_t | UrlNode   |\n\n */\ntypedef struct AkHeapNodeExt {\n  AkHeapNode *node;\n  AkHeapNode *chld;\n  char        data[];\n} AkHeapNodeExt;\n\nstruct AkHeap {\n  AkHeapAllocator *allocator;\n  AkHeapNode      *root;\n  AkHeapNode      *trash;\n  AkHeapSrchCtx   *srchctx;\n  AkHeap          *chld; /* attached heaps, free all with this */\n  AkHeap          *next;\n  void            *data;\n  AkHeap          *idheap;\n  uint32_t         heapid;\n  AkEnum           flags;\n};\n\nvoid\nak_sid_destroy(AkHeap * __restrict heap,\n               AkSIDNode * __restrict snode);\n\nvoid *\nak_heap_ext_get(AkHeapNode * __restrict hnode,\n                uint16_t                flag);\n\nvoid *\nak_heap_ext_add(AkHeap     * __restrict heap,\n                AkHeapNode * __restrict hnode,\n                uint16_t                flag);\n\nvoid\nak_heap_ext_rm(AkHeap     * __restrict heap,\n               AkHeapNode * __restrict hnode,\n               uint16_t                flag);\n\nvoid\nak_heap_ext_free(AkHeap     * __restrict heap,\n                 AkHeapNode * __restrict hnode);\n\nAK_HIDE\nvoid\nak_freeh(AkHeapNode * __restrict heapNode);\n\nvoid\nak_mem_init(void);\n\nvoid\nak_mem_deinit(void);\n\nAK_HIDE\nvoid\nak_dsSetAllocator(AkHeapAllocator * __restrict alc,\n                  DsAllocator     * __restrict dsalc);\n\n#endif /* ak_memory_h */\n"
  },
  {
    "path": "src/mem/ext.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n#include \"rb.h\"\n#include \"../bitwise/bitwise.h\"\n\nstatic uint8_t ak__heap_ext_sz[] = {\n  (uint8_t)sizeof(AkHeapSrchNode), /* AK_HEAP_NODE_FLAGS_SRCH  */\n  (uint8_t)sizeof(AkSIDNode),      /* AK_HEAP_NODE_FLAGS_SID   */\n  (uint8_t)sizeof(int),            /* AK_HEAP_NODE_FLAGS_REFC  */\n  (uint8_t)sizeof(uintptr_t),      /* AK_HEAP_NODE_FLAGS_EXTRA */\n  (uint8_t)sizeof(uintptr_t),      /* AK_HEAP_NODE_FLAGS_INF   */\n  (uint8_t)sizeof(AkUrlNode),      /* AK_HEAP_NODE_FLAGS_URL   */\n  (uint8_t)sizeof(uintptr_t),      /* AK_HEAP_NODE_FLAGS_USR   */\n  (uint8_t)0,                      /* AK_HEAP_NODE_FLAGS_USRF  */\n  (uint8_t)sizeof(uintptr_t)       /* AK_HEAP_NODE_FLAGS_MMAP  */\n};\n\nAK_INLINE\nuint32_t\nak_heap_ext_size(uint16_t flags) {\n  uint32_t sz, flag, i, ctz;\n\n  sz   = 0;\n  ctz  = (31 - ak_bitw_clz(flags));\n  flag = 1 << ctz;\n  i    = ctz - ak_bitw_ctz(AK_HEAP_NODE_FLAGS_EXT_FRST);\n\n  while (flag >= AK_HEAP_NODE_FLAGS_EXT_FRST) {\n    if (flags & flag)\n      sz += ak__heap_ext_sz[i];\n    flag >>= 1;\n    i--;\n  }\n\n  return sz;\n}\n\nAK_INLINE\nuint32_t\nak_heap_ext_off(uint16_t flags, uint16_t flag) {\n  uint32_t sz, i;\n\n  sz = 0;\n  i = ak_bitw_ctz(flag) - ak_bitw_ctz(AK_HEAP_NODE_FLAGS_EXT_FRST);\n\n  while ((flag >>= 1) >= AK_HEAP_NODE_FLAGS_EXT_FRST) {\n    i--;\n    if (flags & flag)\n      sz += ak__heap_ext_sz[i];\n  }\n\n  return sz;\n}\n\nAK_INLINE\nvoid\nak_heap_ext_freeurl(AkHeapNode * __restrict hnode) {\n  AkUrlNode  *urlNode;\n  void       *urlobj;\n  void       **it, *last;\n  size_t      len;\n\n  urlNode = ak_heap_ext_get(hnode, AK_HEAP_NODE_FLAGS_REFC);\n  len     = urlNode->len;\n  it      = urlNode->urls[0];\n  last    = urlNode->urls[len];\n\n  while (it != last) {\n    /* check if object is available */\n    if ((urlobj = ak_getObjectByUrl(*it)))\n      ak_release(urlobj);\n    it++;\n  }\n}\n\nvoid *\nak_heap_ext_get(AkHeapNode * __restrict hnode,\n                uint16_t                flag) {\n  AkHeapNodeExt *exnode;\n  uint32_t       ofst;\n\n  if (!(hnode->flags & flag))\n    return NULL;\n\n  exnode = hnode->chld;\n  ofst   = ak_heap_ext_off(hnode->flags & ~flag, flag);\n\n  return &exnode->data[ofst];\n}\n\nvoid *\nak_heap_ext_add(AkHeap     * __restrict heap,\n                AkHeapNode * __restrict hnode,\n                uint16_t                flag) {\n  AkHeapAllocator *alc;\n  AkHeapNodeExt   *exnode;\n  uint32_t         sz, ofst, isz, flag_off;\n\n  ofst = ak_heap_ext_off(hnode->flags, flag);\n\n  /* nothing to do */\n  if (hnode->flags & flag) {\n    exnode = hnode->chld;\n    return &exnode->data[ofst];\n  }\n\n  flag_off = ak_bitw_ctz(AK_HEAP_NODE_FLAGS_EXT_FRST);\n\n  alc = heap->allocator;\n  sz  = ak_heap_ext_size(hnode->flags | flag);\n  isz = ak__heap_ext_sz[ak_bitw_ctz(flag >> flag_off)];\n\n  if (!(hnode->flags & AK_HEAP_NODE_FLAGS_EXT)) {\n    exnode        = alc->malloc(sizeof(*exnode) + sz);\n    exnode->node  = hnode;\n    exnode->chld  = hnode->chld;\n    hnode->flags |= AK_HEAP_NODE_FLAGS_EXT;\n  } else {\n    AkHeapSrchNode *curr, *parent;\n    int side, tomove;\n\n    exnode = hnode->chld;\n    parent = NULL;\n    side   = 1;\n\n    /* save link */\n    if (hnode->flags & AK_HEAP_NODE_FLAGS_SRCH) {\n      curr = (AkHeapSrchNode *)exnode->data;\n      side = ak_heap_rb_parent(heap->srchctx, curr->key, &parent);\n    }\n\n    exnode = alc->realloc(exnode, sizeof(*exnode) + sz);\n    tomove = sz - isz - ofst;\n    \n    if (tomove > 0)\n      memmove(&exnode->data[ofst + isz],\n              &exnode->data[ofst],\n              tomove);\n\n    /* update link */\n    if (parent) {\n      curr = (AkHeapSrchNode *)exnode->data;\n      parent->chld[side] = curr;\n    }\n  }\n\n  memset(&exnode->data[ofst], 0, isz);\n  hnode->chld = exnode;\n\n  hnode->flags |= flag;\n\n  return &exnode->data[ofst];\n}\n\nvoid\nak_heap_ext_rm(AkHeap     * __restrict heap,\n               AkHeapNode * __restrict hnode,\n               uint16_t                flag) {\n  AkHeapAllocator *alc;\n  AkHeapNodeExt   *exnode;\n  uint32_t         sz, ofst, isz, flag_off;\n\n  /* nothing to do */\n  if (!(hnode->flags & flag))\n    return;\n\n  flag_off = ak_bitw_ctz(AK_HEAP_NODE_FLAGS_EXT_FRST);\n\n  alc    = heap->allocator;\n  sz     = ak_heap_ext_size(hnode->flags & ~flag);\n  ofst   = ak_heap_ext_off(hnode->flags, flag);\n  isz    = ak__heap_ext_sz[ak_bitw_ctz(flag >> flag_off)];\n  exnode = hnode->chld;\n\n  /* free items */\n  switch (flag) {\n    case AK_HEAP_NODE_FLAGS_SRCH:\n      ak_heap_rb_remove(heap->srchctx,\n                        (AkHeapSrchNode *)&exnode->data[ofst]);\n      hnode->flags &= ~AK_HEAP_NODE_FLAGS_RED;\n      break;\n    case AK_HEAP_NODE_FLAGS_SID:\n      ak_sid_destroy(heap, (AkSIDNode *)&exnode->data[ofst]);\n      break;\n    case AK_HEAP_NODE_FLAGS_EXTRA:\n    case AK_HEAP_NODE_FLAGS_INF:\n      /* ak_free(&exnode->data[ofst]); */\n      break;\n    case AK_HEAP_NODE_FLAGS_USR:\n      if (hnode->flags & AK_HEAP_NODE_FLAGS_USRF)\n        alc->free(&exnode->data[ofst]);\n      break;\n    case AK_HEAP_NODE_FLAGS_URL:\n      ak_heap_ext_freeurl(hnode);\n      break;\n  }\n\n  hnode->flags &= ~flag;\n  if (sz > 0) {\n    AkHeapSrchNode *curr, *parent;\n    int side;\n\n    parent = NULL;\n    side   = 1;\n\n    /* save link */\n    if (hnode->flags & AK_HEAP_NODE_FLAGS_SRCH) {\n      curr = (AkHeapSrchNode *)exnode->data;\n      side = ak_heap_rb_parent(heap->srchctx,\n                               curr->key,\n                               &parent);\n    }\n\n    if (sz > ofst)\n      memmove(&exnode->data[ofst] + isz,\n              &exnode->data[ofst],\n              sz - ofst);\n\n    exnode = alc->realloc(exnode, sz + sizeof(*exnode));\n\n    /* update link */\n    if (parent) {\n      curr = (AkHeapSrchNode *)exnode->data;\n      parent->chld[side] = curr;\n    }\n\n    hnode->chld = exnode;\n  } else {\n    hnode->chld = exnode->chld;\n    alc->free(exnode);\n\n    hnode->flags &= ~AK_HEAP_NODE_FLAGS_EXT_ALL;\n  }\n}\n\nvoid\nak_heap_ext_free(AkHeap     * __restrict heap,\n                 AkHeapNode * __restrict hnode) {\n  AkHeapAllocator *alc;\n  AkHeapNodeExt   *exnode;\n  uint32_t         ofst;\n\n  /* nothing to do */\n  if (!(hnode->flags & AK_HEAP_NODE_FLAGS_EXT))\n    return;\n\n  alc    = heap->allocator;\n  exnode = hnode->chld;\n\n  /* free items */\n  if (hnode->flags & AK_HEAP_NODE_FLAGS_SRCH) {\n    ofst = ak_heap_ext_off(hnode->flags, AK_HEAP_NODE_FLAGS_SRCH);\n    ak_heap_rb_remove(heap->srchctx,\n                      (AkHeapSrchNode *)&exnode->data[ofst]);\n  }\n\n  if (hnode->flags & AK_HEAP_NODE_FLAGS_SID) {\n    ofst = ak_heap_ext_off(hnode->flags, AK_HEAP_NODE_FLAGS_SID);\n    ak_sid_destroy(heap, (AkSIDNode *)&exnode->data[ofst]);\n  }\n\n  if (hnode->flags & AK_HEAP_NODE_FLAGS_EXTRA) {\n    /* ofst = ak_heap_ext_off(hnode->flags, AK_HEAP_NODE_FLAGS_EXTRA);\n       ak_free(&exnode->data[ofst]); */\n  }\n\n  if (hnode->flags & AK_HEAP_NODE_FLAGS_INF) {\n    /* ofst = ak_heap_ext_off(hnode->flags, AK_HEAP_NODE_FLAGS_INF);\n       ak_free(&exnode->data[ofst]); */\n  }\n\n  if (hnode->flags & AK_HEAP_NODE_FLAGS_URL)\n    ak_heap_ext_freeurl(hnode);\n\n  if (hnode->flags & AK_HEAP_NODE_FLAGS_USR) {\n    ofst = ak_heap_ext_off(hnode->flags, AK_HEAP_NODE_FLAGS_INF);\n    if (hnode->flags & AK_HEAP_NODE_FLAGS_USRF)\n      alc->free(&exnode->data[ofst]);\n  }\n  \n  if (hnode->flags & AK_HEAP_NODE_FLAGS_MMAP)\n    ak_unmmap_attached(ak__alignas(hnode));\n\n  hnode->chld = exnode->chld;\n  alc->free(exnode);\n\n  hnode->flags &= ~AK_HEAP_NODE_FLAGS_EXT_ALL;\n}\n"
  },
  {
    "path": "src/mem/intr.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n\nAK_HIDE\nvoid\nak_dsSetAllocator(AkHeapAllocator * __restrict alc,\n                  DsAllocator     * __restrict dsalc) {\n  dsalc->calloc   = alc->calloc;\n  dsalc->free     = alc->free;\n  dsalc->malloc   = alc->malloc;\n  dsalc->memalign = alc->memalign;\n  dsalc->realloc  = alc->realloc;\n  dsalc->size     = alc->size;\n  dsalc->strdup   = alc->strdup;\n}\n"
  },
  {
    "path": "src/mem/lt.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"lt.h\"\n#include <assert.h>\n#include <string.h>\n\nstatic AkHeapBucket ak__heap_bucket = {\n  .heapEntry       = NULL,\n  .firstAvailEntry = 1,\n  .count           = 1,\n  .bucketIndex     = 0\n};\n\nstatic AkHeapLookupTable ak__heap_lt = {\n  .rootBucket       = &ak__heap_bucket,\n  .lastBucket       = &ak__heap_bucket,\n  .firstAvailBucket = &ak__heap_bucket,\n  .size             = 1,\n  .bucketSize       = 4,\n};\n\nstatic\nAkHeapBucket *\nak__heap_lt_find_bucket(uint32_t bucketIndex,\n                        AkHeapBucket ** __restrict prev) {\n  AkHeapBucket *bucket, *prevBucket;\n\n  bucket     = ak__heap_lt.rootBucket;\n  prevBucket = NULL;\n\n  while (bucket && bucket->bucketIndex < bucketIndex) {\n    prevBucket = bucket;\n    bucket     = bucket->next;\n  }\n\n  if (prev)\n    *prev = prevBucket;\n\n  if (!bucket || bucket->bucketIndex != bucketIndex)\n    return NULL;\n\n  return bucket;\n}\n\nstatic\nAkHeapBucket *\nak__heap_lt_find_avail_from(AkHeapBucket * __restrict bucket) {\n  while (bucket && AK__LT_BUCKET_IS_FULL(bucket))\n    bucket = bucket->next;\n\n  return bucket;\n}\n\nstatic\nbool\nak__heap_lt_entry_in_bucket(AkHeapBucket      * __restrict bucket,\n                            AkHeapBucketEntry * __restrict entry) {\n  return entry >= bucket->heapEntry\n         && entry < bucket->heapEntry + ak__heap_lt.bucketSize;\n}\n\nstatic\nvoid\nak__heap_lt_clear_last_used(AkHeapBucket      * __restrict bucket,\n                            AkHeapBucketEntry * __restrict entry,\n                            bool                            clearBucket) {\n  if (!ak__heap_lt.lastUsedEntry)\n    return;\n\n  if (ak__heap_lt.lastUsedEntry == entry\n      || (clearBucket\n          && ak__heap_lt_entry_in_bucket(bucket, ak__heap_lt.lastUsedEntry)))\n    ak__heap_lt.lastUsedEntry = NULL;\n}\n\nvoid\nak_heap_lt_init(AkHeap * __restrict initialHeap) {\n  assert(initialHeap && \"heap cannot be null!\");\n  ak__heap_bucket.heapEntry = calloc(ak__heap_lt.bucketSize,\n                                     sizeof(AkHeapBucketEntry));\n\n  assert(ak__heap_bucket.heapEntry && \"malloc failed\");\n\n  ak__heap_bucket.heapEntry[0] = (AkHeapBucketEntry){\n    .heap   = initialHeap,\n    .heapid = 0\n  };\n\n  ak__heap_lt.lastUsedEntry = ak__heap_lt.rootBucket->heapEntry;\n}\n\nvoid\nak_heap_lt_insert(AkHeap * __restrict heap) {\n  AkHeapBucket      *bucket;\n  AkHeapBucketEntry *bucketEntry;\n  uint32_t           entryIndex;\n  uint32_t           heapid;\n\n  bucket = ak__heap_lt.firstAvailBucket;\n\n  /* all buckets are full */\n  if (!bucket || AK__LT_BUCKET_IS_FULL(bucket)) {\n    bucket = calloc(1, sizeof(*bucket));\n    assert(bucket && \"malloc failed\");\n\n    bucket->heapEntry = calloc(ak__heap_lt.bucketSize,\n                               sizeof(*bucket->heapEntry));\n    assert(bucket->heapEntry && \"malloc failed\");\n\n    bucket->bucketIndex = ak__heap_lt.lastBucket->bucketIndex + 1;\n\n    ak__heap_lt.size++;\n    ak__heap_lt.lastBucket->next = bucket;\n    ak__heap_lt.lastBucket       = bucket;\n    ak__heap_lt.firstAvailBucket = bucket;\n  }\n\n  entryIndex  = bucket->firstAvailEntry;\n  bucketEntry = &bucket->heapEntry[entryIndex];\n  bucketEntry->heap = heap;\n\n  heapid = bucket->bucketIndex * ak__heap_lt.bucketSize + entryIndex;\n  bucketEntry->heapid = heapid;\n\n  heap->heapid = heapid;\n\n  /* find next avail entry */\n  while (++bucket->firstAvailEntry < ak__heap_lt.bucketSize) {\n    if (!bucket->heapEntry[bucket->firstAvailEntry].heap)\n      break;\n  }\n\n  if (AK__LT_BUCKET_IS_FULL(bucket))\n    ak__heap_lt.firstAvailBucket = NULL;\n\n  bucket->count++;\n\n  ak__heap_lt.lastUsedEntry = bucketEntry;\n}\n\nAkHeap *\nak_heap_lt_find(uint32_t heapid) {\n  AkHeapBucket      *bucket;\n  AkHeapBucketEntry *entry;\n  uint32_t           bucketIndex;\n  uint32_t           entryIndex;\n\n  if (ak__heap_lt.lastUsedEntry\n      && ak__heap_lt.lastUsedEntry->heap\n      && ak__heap_lt.lastUsedEntry->heapid == heapid)\n    return ak__heap_lt.lastUsedEntry->heap;\n\n  bucketIndex = heapid / ak__heap_lt.bucketSize;\n  entryIndex  = heapid % ak__heap_lt.bucketSize;\n\n  bucket = ak__heap_lt_find_bucket(bucketIndex, NULL);\n  if (!bucket)\n    return NULL;\n\n  entry = &bucket->heapEntry[entryIndex];\n  if (!entry->heap || entry->heapid != heapid)\n    return NULL;\n\n  ak__heap_lt.lastUsedEntry = entry;\n  return entry->heap;\n}\n\nvoid\nak_heap_lt_remove(uint32_t heapid) {\n  AkHeapBucket      *prevBucket;\n  AkHeapBucket      *bucket;\n  AkHeapBucketEntry *entry;\n  uint32_t           bucketIndex;\n  uint32_t           entryIndex;\n  bool               destroyBucket;\n\n  bucketIndex = heapid / ak__heap_lt.bucketSize;\n  entryIndex  = heapid % ak__heap_lt.bucketSize;\n\n  bucket = ak__heap_lt_find_bucket(bucketIndex, &prevBucket);\n  if (!bucket)\n    return;\n\n  entry = &bucket->heapEntry[entryIndex];\n  if (entry && entry->heap && entry->heapid == heapid) {\n    memset(&bucket->heapEntry[entryIndex],\n           '\\0',\n           sizeof(AkHeapBucketEntry));\n    bucket->count--;\n\n    if (!AK__LT_BUCKET_IS_FULL(bucket))\n      bucket->firstAvailEntry = entryIndex;\n\n    destroyBucket = bucket->count < 1 && bucket != &ak__heap_bucket;\n    ak__heap_lt_clear_last_used(bucket, entry, destroyBucket);\n\n    if (destroyBucket) {\n      if (ak__heap_lt.firstAvailBucket == bucket) {\n        ak__heap_lt.firstAvailBucket = ak__heap_lt_find_avail_from(bucket->next);\n      }\n\n      if (ak__heap_lt.lastBucket == bucket)\n        ak__heap_lt.lastBucket = prevBucket;\n\n      /* we know that prevBucket cannot be null because rootBucket is static */\n      prevBucket->next = bucket->next;\n      free(bucket->heapEntry);\n      free(bucket);\n\n      ak__heap_lt.size--;\n    } else {\n      if (!ak__heap_lt.firstAvailBucket\n          || bucket->bucketIndex < ak__heap_lt.firstAvailBucket->bucketIndex)\n        ak__heap_lt.firstAvailBucket = bucket;\n    }\n  }\n}\n\nvoid\nak_heap_lt_cleanup(void) {\n  AkHeapBucket *bucket;\n  AkHeapBucket *toFree;\n\n  bucket = ak__heap_lt.rootBucket->next;\n  while (bucket) {\n    toFree = bucket;\n    bucket = bucket->next;\n\n    free(toFree->heapEntry);\n    free(toFree);\n  }\n\n  free(ak__heap_bucket.heapEntry);\n}\n"
  },
  {
    "path": "src/mem/lt.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_hashtable_h\n#define ak_hashtable_h\n\n#include \"common.h\"\n\n#define AK__LT_BUCKET_IS_FULL(X) (X->count == ak__heap_lt.bucketSize)\n\ntypedef struct AkHeapBucketEntry {\n  AkHeap  *heap;\n  uint32_t heapid;\n} AkHeapBucketEntry;\n\ntypedef struct AkHeapBucket {\n  struct AkHeapBucket *next;\n  AkHeapBucketEntry   *heapEntry;\n  uint32_t             bucketIndex;\n  uint32_t             count;\n  uint32_t             firstAvailEntry;\n} AkHeapBucket;\n\ntypedef struct AkHeapLookupTable {\n  AkHeapBucket      *rootBucket;\n  AkHeapBucket      *lastBucket;\n  AkHeapBucket      *firstAvailBucket;\n  AkHeapBucketEntry *lastUsedEntry; /* cache last */\n  size_t             size;\n  uint32_t           bucketSize; /* default = 4 */\n} AkHeapLookupTable;\n\nvoid\nak_heap_lt_init(AkHeap * __restrict initialHeap);\n\nAkHeap *\nak_heap_lt_find(uint32_t heapid);\n\nvoid\nak_heap_lt_remove(uint32_t heapid);\n\nvoid\nak_heap_lt_insert(AkHeap * __restrict heap);\n\nvoid\nak_heap_lt_cleanup(void);\n\n#endif /* ak_hashtable_h */\n"
  },
  {
    "path": "src/mem/mem.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n#include \"rb.h\"\n#include \"lt.h\"\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <assert.h>\n\nstatic\nint\nak__heap_srch_cmp(void * __restrict key1,\n                  void * __restrict key2);\n\nstatic\nvoid\nak__heap_srch_print(void * __restrict key);\n\nstatic\nchar*\nak__heap_strdup_def(const char * str);\n\nAK_HIDE\nvoid\nak_heap_moveh_chld(AkHeap     * __restrict heap,\n                   AkHeap     * __restrict newheap,\n                   AkHeapNode * __restrict heapNode);\n\nstatic void * ak__emptystr = \"\";\n\nAkHeapAllocator ak__allocator = {\n  .malloc   = malloc,\n  .calloc   = calloc,\n  .realloc  = realloc,\n  .free     = free,\n#ifndef _WIN32\n  .memalign = posix_memalign,\n#endif\n  .strdup   = ak__heap_strdup_def\n};\n\nstatic AkHeap ak__heap = {\n  .allocator  = &ak__allocator,\n  .srchctx    = NULL,\n  .root       = NULL,\n  .trash      = NULL,\n  .flags      = 0\n};\n\nstatic RBTree *ak__heap_sub = NULL;\n\nstatic\nint\nak__heap_srch_cmp(void * __restrict key1,\n                  void * __restrict key2) {\n  return strcmp((char *)key1, (char *)key2);\n}\n\nstatic\nvoid\nak__heap_srch_print(void * __restrict key) {\n  printf(\"\\t'%s'\\n\", (const char *)key);\n}\n\nstatic\nchar*\nak__heap_strdup_def(const char * str) {\n  void  *memptr;\n  size_t memsize;\n\n  memsize = strlen(str);\n  memptr  = ak__heap.allocator->malloc(memsize + 1);\n  memcpy(memptr, str, memsize);\n\n  /* NULL */\n  memset((char *)memptr + memsize, '\\0', 1);\n\n  return memptr;\n}\n\nAK_EXPORT\nchar*\nak_heap_strdup(AkHeap * __restrict heap,\n               void   * __restrict parent,\n               const char * str) {\n  void  *memptr;\n  size_t memsize;\n\n  if (!str)\n    return NULL;\n\n  memsize = strlen(str);\n  memptr  = ak_heap_alloc(heap, parent, memsize + 1);\n\n  memcpy(memptr, str, memsize);\n\n  /* NULL */\n  memset((char *)memptr + memsize, '\\0', 1);\n\n  return memptr;\n}\n\nAK_EXPORT\nchar*\nak_heap_strndup(AkHeap     * __restrict heap,\n                void       * __restrict parent,\n                const char *            str,\n                size_t size) {\n  void *memptr;\n\n  memptr  = ak_heap_alloc(heap, parent, size + 1);\n  memcpy(memptr, str, size);\n\n  /* NULL */\n  memset((char *)memptr + size, '\\0', 1);\n\n  return memptr;\n}\n\nAK_EXPORT\nAkHeapAllocator *\nak_heap_allocator(AkHeap * __restrict heap) {\n  return heap->allocator;\n}\n\nAK_EXPORT\nAkHeap *\nak_heap_default(void) {\n  return &ak__heap;\n}\n\nAK_EXPORT\nAkHeap *\nak_heap_getheap(void * __restrict memptr) {\n  AkHeapNode *heapNode;\n  heapNode = ak__alignof(memptr);\n  return ak_heap_lt_find(heapNode->heapid);\n}\n\nAK_EXPORT\nAkHeap *\nak_heap_new(AkHeapAllocator  *allocator,\n            AkHeapSrchCmpFn   cmp,\n            AkHeapSrchPrintFn print) {\n  AkHeapAllocator *alc;\n  AkHeap          *heap;\n\n  alc = allocator ? allocator : &ak__allocator;\n\n  heap = alc->malloc(sizeof(*heap));\n  assert(heap && \"malloc failed\");\n\n  heap->flags = AK_HEAP_FLAGS_DYNAMIC;\n  ak_heap_init(heap, allocator, cmp, print);\n\n  return heap;\n}\n\nAK_EXPORT\nvoid\nak_heap_attach(AkHeap * __restrict parent,\n               AkHeap * __restrict chld) {\n  chld->next   = parent->chld;\n  parent->chld = chld;\n}\n\nAK_EXPORT\nvoid\nak_heap_dettach(AkHeap * __restrict parent,\n                AkHeap * __restrict chld) {\n  AkHeap *heap;\n\n  heap = parent->chld;\n  if (heap == chld) {\n    parent->chld = chld->next;\n    chld->next   = NULL;\n    return;\n  }\n\n  while (heap) {\n    if (heap->next == chld) {\n      heap->next = chld->next;\n      chld->next = NULL;\n      break;\n    }\n\n    heap = heap->next;\n  }\n}\n\nAK_EXPORT\nvoid\nak_heap_setdata(AkHeap * __restrict heap,\n                void * __restrict memptr) {\n  heap->data = memptr;\n}\n\nAK_EXPORT\nvoid*\nak_heap_data(AkHeap * __restrict heap) {\n  return heap->data;\n}\n\nAK_EXPORT\nvoid\nak_heap_init(AkHeap          * __restrict heap,\n             AkHeapAllocator * __restrict allocator,\n             AkHeapSrchCmpFn              cmp,\n             AkHeapSrchPrintFn            print) {\n  AkHeapAllocator *alc;\n  AkHeapSrchCtx   *srchctx;\n  AkHeapNode      *rootNode,     *nullNode;\n  AkHeapNodeExt   *rootNodeExt,  *nullNodeExt;\n  AkHeapSrchNode  *rootSrchNode, *nullSrchNode;\n\n  if (heap->flags & AK_HEAP_FLAGS_INITIALIZED)\n    return;\n\n  alc = allocator ? allocator : &ak__allocator;\n\n  srchctx     = alc->malloc(sizeof(*srchctx));\n  rootNode    = alc->calloc(1, ak__heapnd_sz);\n  nullNode    = alc->calloc(1, ak__heapnd_sz);\n  rootNodeExt = alc->calloc(1, sizeof(*rootNodeExt) + sizeof(AkHeapSrchNode));\n  nullNodeExt = alc->calloc(1, sizeof(*nullNodeExt) + sizeof(AkHeapSrchNode));\n\n  assert(srchctx\n         && rootNode\n         && nullNode\n         && rootNodeExt\n         && nullNodeExt\n         && \"malloc failed\");\n\n  rootNode->chld    = rootNodeExt;\n  rootNodeExt->node = rootNode;\n  rootSrchNode      = (AkHeapSrchNode *)rootNodeExt->data;\n\n  nullNode->chld    = nullNodeExt;\n  nullNodeExt->node = nullNode;\n  nullSrchNode      = (AkHeapSrchNode *)nullNodeExt->data;\n\n  nullSrchNode->key = ak__emptystr;\n  nullSrchNode->chld[AK__BST_LEFT]  = nullSrchNode;\n  nullSrchNode->chld[AK__BST_RIGHT] = nullSrchNode;\n\n  rootSrchNode->key = ak__emptystr;\n  rootSrchNode->chld[AK__BST_LEFT]  = nullSrchNode;\n  rootSrchNode->chld[AK__BST_RIGHT] = nullSrchNode;\n\n  rootNode->flags   = (AK_HEAP_NODE_FLAGS_EXT | AK_HEAP_NODE_FLAGS_SRCH);\n  nullNode->flags   = (AK_HEAP_NODE_FLAGS_EXT | AK_HEAP_NODE_FLAGS_SRCH);\n\n  srchctx->cmp      = cmp   ? cmp   : ak__heap_srch_cmp;\n  srchctx->print    = print ? print : ak__heap_srch_print;\n  srchctx->root     = rootSrchNode;\n  srchctx->nullNode = nullSrchNode;\n\n  heap->chld      = NULL;\n  heap->next      = NULL;\n  heap->root      = NULL;\n  heap->trash     = NULL;\n  heap->data      = NULL;\n  heap->idheap    = NULL;\n  heap->heapid    = 0;\n  heap->allocator = alc;\n  heap->srchctx   = srchctx;\n  heap->flags    |= AK_HEAP_FLAGS_INITIALIZED;\n\n  if (heap != &ak__heap)\n    ak_heap_lt_insert(heap);\n}\n\nAK_EXPORT\nvoid\nak_heap_destroy(AkHeap * __restrict heap) {\n  AkHeapAllocator *alc;\n  AkHeapNode      *rootNode, *nullNode;\n  AkHeap          *it, *toDestroy;\n\n  if (!(heap->flags & AK_HEAP_FLAGS_INITIALIZED))\n    return;\n\n  alc = heap->allocator;\n\n  /* first destroy all attached heaps */\n  if (heap->chld) {\n    it = heap->chld;\n    do {\n      toDestroy = it;\n      it = it->next;\n      ak_heap_destroy(toDestroy);\n    } while (it);\n  }\n\n  ak_heap_cleanup(heap);\n\n  rootNode = AK__HEAPNODE(heap->srchctx->root);\n  nullNode = AK__HEAPNODE(heap->srchctx->nullNode);\n\n  alc->free(rootNode->chld);\n  alc->free(nullNode->chld);\n  alc->free(rootNode);\n  alc->free(nullNode);\n  alc->free(heap->srchctx);\n\n  heap->data = NULL;\n  ak_heap_lt_remove(heap->heapid);\n\n  if (heap->flags & AK_HEAP_FLAGS_DYNAMIC\n      && heap != &ak__heap)\n    alc->free(heap);\n}\n\nAK_EXPORT\nvoid*\nak_heap_alloc(AkHeap * __restrict heap,\n              void   * __restrict parent,\n              size_t              size) {\n  AkHeapNode *currNode;\n  AkHeapNode *parentNode;\n  size_t      memsize;\n\n  assert((!parent || heap->heapid == ak__alignof(parent)->heapid)\n         && \"parent and child mem must use same heap\");\n\n  memsize  = ak__heapnd_sz + size;\n  memsize  = ak__align(memsize);\n  currNode = heap->allocator->malloc(memsize);\n  assert(currNode && \"malloc failed\");\n\n  currNode->flags  = 0;\n  currNode->typeid = 0;\n  currNode->chld   = NULL;\n  currNode->heapid = heap->heapid;\n\n  if (parent) {\n    AkHeapNode *chldNode;\n\n    parentNode = ak__alignof(parent);\n    chldNode   = ak_heap_chld(parentNode);\n\n    ak_heap_chld_set(parentNode, currNode);\n    if (chldNode)\n      chldNode->prev = currNode;\n\n    currNode->next = chldNode;\n  } else {\n    if (heap->root)\n      heap->root->prev = currNode;\n\n    currNode->next = heap->root;\n    currNode->prev = NULL;\n    heap->root     = currNode;\n  }\n\n  return ak__alignas(currNode);\n}\n\nAK_EXPORT\nvoid*\nak_heap_calloc(AkHeap * __restrict heap,\n               void   * __restrict parent,\n               size_t              size) {\n  void *memptr;\n\n  memptr = ak_heap_alloc(heap,\n                         parent,\n                         size);\n  memset(memptr, '\\0', size);\n\n  return memptr;\n}\n\nAK_EXPORT\nvoid*\nak_heap_realloc(AkHeap * __restrict heap,\n                void   * __restrict parent,\n                void   * __restrict memptr,\n                size_t              newsize) {\n  AkHeapNode *oldNode;\n  AkHeapNode *newNode;\n  AkHeapNode *chld;\n\n  if (!memptr)\n    return ak_heap_alloc(heap,\n                         parent,\n                         newsize);\n\n  oldNode = ak__alignof(memptr);\n\n  if (newsize == 0) {\n    ak_heap_free(heap, oldNode);\n    return NULL;\n  }\n\n  newsize = ak__heapnd_sz + newsize;\n  newsize = ak__align(newsize);\n  newNode = heap->allocator->realloc(oldNode, newsize);\n  assert(newNode && \"realloc failed\");\n\n  if (heap->root == oldNode)\n    heap->root = newNode;\n\n  if (heap->trash == oldNode)\n    heap->trash = newNode;\n\n  chld = newNode->chld;\n  if (chld) {\n    if (newNode->flags & AK_HEAP_NODE_FLAGS_EXT) {\n      AkHeapNodeExt *exnode;\n      exnode       = newNode->chld;\n      exnode->node = newNode;\n\n      if (exnode->chld)\n        exnode->chld->prev = newNode;\n    } else {\n      chld->prev = newNode;\n    }\n  }\n\n  if (newNode->next)\n    newNode->next->prev = newNode;\n\n  if (newNode->prev) {\n    chld = ak_heap_chld(newNode->prev);\n\n    if (chld == oldNode)\n      ak_heap_chld_set(newNode->prev, newNode);\n    else\n      newNode->prev->next = newNode;\n  }\n\n  return ak__alignas(newNode);\n}\n\nAK_EXPORT\nvoid *\nak_heap_chld(AkHeapNode *heapNode) {\n  if (heapNode->flags & AK_HEAP_NODE_FLAGS_EXT)\n    return ((AkHeapNodeExt *)heapNode->chld)->chld;\n\n  return heapNode->chld;\n}\n\nAK_EXPORT\nvoid\nak_heap_chld_set(AkHeapNode * __restrict heapNode,\n                 AkHeapNode * __restrict chldNode) {\n  if (heapNode->flags & AK_HEAP_NODE_FLAGS_EXT)\n    ((AkHeapNodeExt *)heapNode->chld)->chld = chldNode;\n  else\n    heapNode->chld = chldNode;\n\n  if (chldNode)\n    chldNode->prev = heapNode;\n}\n\nAK_EXPORT\nAkHeapNode *\nak_heap_parent(AkHeapNode *heapNode) {\n  AkHeapNode *p, *it;\n\n  p  = NULL;\n  it = heapNode;\n\n  while (it->prev) {\n    /* we found near parent */\n    if (ak_heap_chld(it->prev) == it) {\n      p = it->prev;\n      break;\n    }\n\n    it = it->prev;\n  }\n\n  return p;\n}\n\nAK_EXPORT\nvoid\nak_heap_setp(AkHeapNode * __restrict heapNode,\n             AkHeapNode * __restrict newParent) {\n  AkHeap *oldheap, *newheap;\n\n  oldheap = ak_heap_lt_find(heapNode->heapid);\n  newheap = ak_heap_lt_find(newParent->heapid);\n\n  if (heapNode->prev) {\n    if (ak_heap_chld(heapNode->prev) == heapNode)\n      ak_heap_chld_set(heapNode->prev, heapNode->next);\n    else\n      heapNode->prev->next = heapNode->next;\n\n    if (heapNode->next)\n      heapNode->next->prev = heapNode->prev;\n\n    heapNode->prev = NULL;\n  } else if (heapNode == oldheap->root) { /* root->prev = NULL */\n    oldheap->root = heapNode->next;\n\n    if (heapNode->next)\n      heapNode->next->prev = NULL;\n  }\n\n  heapNode->next = NULL;\n\n  if (heapNode == oldheap->trash)\n    oldheap->trash = heapNode->next;\n\n  /* move all ids to new heap (if it is different) */\n  if (newParent->heapid != heapNode->heapid)\n    ak_heap_moveh(heapNode, newheap);\n\n  heapNode->next = ak_heap_chld(newParent);\n  ak_heap_chld_set(newParent, heapNode);\n\n  if (heapNode->next)\n    heapNode->next->prev = heapNode;\n}\n\nAK_HIDE\nvoid\nak_heap_moveh_chld(AkHeap     * __restrict heap,\n                   AkHeap     * __restrict newheap,\n                   AkHeapNode * __restrict heapNode) {\n  do {\n    AkHeapNode *chld;\n\n    if (heapNode->flags & AK_HEAP_NODE_FLAGS_SRCH) {\n      AkHeapSrchNode *srchNode;\n      srchNode = (AkHeapSrchNode *)((AkHeapNodeExt *)heapNode->chld)->data;\n\n      ak_heap_rb_remove(heap->srchctx, srchNode);\n      ak_heap_rb_insert(newheap->srchctx, srchNode);\n    }\n\n    heapNode->heapid = newheap->heapid;\n    chld = ak_heap_chld(heapNode);\n    if (chld)\n      ak_heap_moveh_chld(heap, newheap, chld);\n\n    heapNode = heapNode->next;\n  } while (heapNode);\n}\n\nAK_EXPORT\nvoid\nak_heap_moveh(AkHeapNode * __restrict heapNode,\n              AkHeap     * __restrict newheap) {\n  AkHeapNode *chld;\n  AkHeap     *heap;\n\n  heap = ak_heap_lt_find(heapNode->heapid);\n  if (heapNode->flags & AK_HEAP_NODE_FLAGS_SRCH) {\n    AkHeapSrchNode *srchNode;\n    srchNode = (AkHeapSrchNode *)((AkHeapNodeExt *)heapNode->chld)->data;\n\n    ak_heap_rb_remove(heap->srchctx, srchNode);\n    ak_heap_rb_insert(newheap->srchctx, srchNode);\n  }\n\n  heapNode->heapid = newheap->heapid;\n  chld = ak_heap_chld(heapNode);\n  if (chld)\n    ak_heap_moveh_chld(heap, newheap, chld);\n}\n\nAK_EXPORT\nvoid\nak_heap_setpm(void * __restrict memptr,\n              void * __restrict newparent) {\n  ak_heap_setp(ak__alignof(memptr),\n               ak__alignof(newparent));\n}\n\nAK_HIDE\nvoid\nak_freeh(AkHeapNode * __restrict heapNode) {\n  if (heapNode->flags & AK_HEAP_NODE_FLAGS_HEAP_CHLD) {\n    AkHeap *attachedHeap;\n    attachedHeap = ak_attachedHeap(ak__alignas(heapNode));\n    if (attachedHeap) {\n      ak_heap_destroy(attachedHeap);\n      ak_setAttachedHeap(ak__alignas(heapNode), NULL);\n    }\n  }\n}\n\nAK_EXPORT\nvoid\nak_heap_free(AkHeap     * __restrict heap,\n             AkHeapNode * __restrict heapNode) {\n  AkHeapAllocator * __restrict alc;\n  alc = heap->allocator;\n\n  if (heapNode->flags & AK_HEAP_NODE_FLAGS_EXT)\n    ak_heap_ext_free(heap, heapNode);\n\n  /* free attached heap */\n  if (heapNode->flags & AK_HEAP_NODE_FLAGS_HEAP_CHLD)\n    ak_freeh(heapNode);\n\n  /* free all child nodes */\n  if (heapNode->chld) {\n    AkHeapNode *toFree;\n    AkHeapNode *nextFree;\n\n    toFree = heapNode->chld;\n\n    do {\n      nextFree = toFree->next;\n\n      if (toFree->flags & AK_HEAP_NODE_FLAGS_EXT)\n        ak_heap_ext_free(heap, toFree);\n\n      /* free attached heap */\n      if (toFree->flags & AK_HEAP_NODE_FLAGS_HEAP_CHLD)\n        ak_freeh(toFree);\n      \n      if (toFree->chld) {\n        if (heap->trash) {\n          AkHeapNode *lastNode;\n\n          lastNode = toFree->chld;\n          while (lastNode->next)\n            lastNode = lastNode->next;\n\n          lastNode->next = heap->trash;\n        }\n\n        heap->trash = toFree->chld;\n      }\n\n      alc->free(toFree);\n      toFree = nextFree;\n\n      /* empty trash */\n      if (!toFree && heap->trash) {\n        toFree = heap->trash;\n        heap->trash = NULL;\n      }\n\n    } while (toFree);\n  }\n\n  if (heapNode->prev) {\n    if (ak_heap_chld(heapNode->prev) == heapNode)\n      ak_heap_chld_set(heapNode->prev, heapNode->next);\n    else\n      heapNode->prev->next = heapNode->next;\n  }\n\n  /* heap->root == heapNode\n     we know that root->prev always is NULL */\n  else {\n    heap->root = heapNode->next;\n  }\n\n  if (heapNode->next)\n    heapNode->next->prev = heapNode->prev;\n\n  alc->free(heapNode);\n}\n\nAK_EXPORT\nvoid\nak_heap_cleanup(AkHeap * __restrict heap) {\n  while (heap->root)\n    ak_heap_free(heap, heap->root);\n}\n\nAK_EXPORT\nvoid *\nak_heap_getId(AkHeap     * __restrict heap,\n              AkHeapNode * __restrict heapNode) {\n  AkHeapSrchNode *snode;\n\n  if (!(heapNode->flags & AK_HEAP_NODE_FLAGS_SRCH))\n    return NULL;\n\n  snode = (AkHeapSrchNode *)((AkHeapNodeExt *)heapNode->chld)->data;\n  return snode->key;\n\n  AK__UNUSED(heap);\n}\n\nAK_EXPORT\nvoid\nak_heap_setId(AkHeap     * __restrict heap,\n              AkHeapNode * __restrict heapNode,\n              void       * __restrict memId) {\n  AkHeapSrchNode *snode;\n\n  if (!memId) {\n    ak_heap_ext_rm(heap,\n                   heapNode,\n                   AK_HEAP_NODE_FLAGS_SRCH);\n    return;\n  }\n\n  snode = ak_heap_ext_add(heap,\n                          heapNode,\n                          AK_HEAP_NODE_FLAGS_SRCH);\n\n  if (snode->key)\n    ak_heap_rb_remove(heap->srchctx, snode);\n\n  heapNode->flags |= AK_HEAP_NODE_FLAGS_RED;\n\n  snode->chld[AK__BST_LEFT]  = heap->srchctx->nullNode;\n  snode->chld[AK__BST_RIGHT] = heap->srchctx->nullNode;\n  snode->key                 = memId;\n\n  ak_heap_rb_insert(heap->srchctx, snode);\n}\n\nAK_EXPORT\nAkResult\nak_heap_getNodeById(AkHeap      * __restrict heap,\n                    void        * __restrict memId,\n                    AkHeapNode ** __restrict dest) {\n  AkHeapSrchNode *srchNode;\n\n  srchNode = ak_heap_rb_find(heap->srchctx, memId);\n  if (!srchNode || srchNode == heap->srchctx->nullNode) {\n    *dest = NULL;\n    return AK_EFOUND;\n  }\n\n  if ((*dest = AK__HEAPNODE(srchNode)))\n    return AK_OK;\n\n  return AK_EFOUND;\n}\n\nAK_EXPORT\nAkResult\nak_heap_getNodeByURL(AkHeap       * __restrict heap,\n                     struct AkURL * __restrict url,\n                     AkHeapNode  ** __restrict dest) {\n  if (url->doc)\n    return ak_heap_getNodeById(heap,\n                               (char *)url->url + 1,\n                               dest);\n\n  return AK_EFOUND;\n}\n\nAK_EXPORT\nAkResult\nak_heap_getMemById(AkHeap * __restrict heap,\n                   void   * __restrict memId,\n                   void  ** __restrict dest) {\n  AkHeapSrchNode *srchNode;\n\n  srchNode = ak_heap_rb_find(heap->srchctx, memId);\n  if (!srchNode || srchNode == heap->srchctx->nullNode) {\n    *dest = NULL;\n    return AK_EFOUND;\n  }\n\n  if ((*dest = ak__alignas(AK__HEAPNODE(srchNode))))\n    return AK_OK;\n\n  return AK_EFOUND;\n}\n\nAK_EXPORT\nint\nak_heap_refc(AkHeapNode * __restrict heapNode) {\n  int *refc;\n\n  refc = ak_heap_ext_get(heapNode, AK_HEAP_NODE_FLAGS_REFC);\n  if (!refc)\n    return -1;\n\n  return *refc;\n}\n\nAK_EXPORT\nint\nak_heap_retain(AkHeapNode * __restrict heapNode) {\n  int *refc;\n\n  if (!(refc = ak_heap_ext_get(heapNode, AK_HEAP_NODE_FLAGS_REFC)))\n    refc = ak_heap_ext_add(ak_heap_getheap(ak__alignas(heapNode)),\n                           heapNode,\n                           AK_HEAP_NODE_FLAGS_REFC);\n\n  return ++(*refc);\n}\n\nAK_EXPORT\nvoid\nak_heap_release(AkHeapNode * __restrict heapNode) {\n  int *refc;\n\n  refc = ak_heap_ext_get(heapNode, AK_HEAP_NODE_FLAGS_REFC);\n  if (!refc || !(*refc))\n    goto fr;\n\n  if (--(*refc) > 0)\n    return;\n\nfr:\n  ak_free(ak__alignas(heapNode));\n}\n\nAK_EXPORT\nvoid\nak_heap_printKeys(AkHeap * __restrict heap) {\n  ak_heap_rb_print(heap->srchctx);\n}\n\nAK_EXPORT\nAkHeap*\nak_attachedHeap(void * __restrict memptr) {\n  return rb_find(ak__heap_sub, ak__alignof(memptr));\n}\n\nAK_EXPORT\nvoid\nak_setAttachedHeap(void   * __restrict memptr,\n                   AkHeap * __restrict heap) {\n  RBNode     *found;\n  AkHeapNode *heapNode;\n\n  heapNode = ak__alignof(memptr);\n\n  if (!heap) {\n    rb_remove(ak__heap_sub, heapNode);\n    heapNode->flags &= ~AK_HEAP_NODE_FLAGS_HEAP_CHLD;\n    return;\n  }\n\n  found = rb_find_node(ak__heap_sub, heapNode);\n  if (found) {\n    found->val = heap;\n    return;\n  }\n\n  rb_insert(ak__heap_sub, heapNode, heap);\n\n  heapNode->flags |= AK_HEAP_NODE_FLAGS_HEAP_CHLD;\n}\n\nAK_EXPORT\nAkHeapAllocator *\nak_mem_allocator(void) {\n  return ak__heap.allocator;\n}\n\nAK_EXPORT\nvoid\nak_mem_printKeys(void) {\n  ak_heap_rb_print(ak__heap.srchctx);\n}\n\nAK_EXPORT\nvoid *\nak_mem_getId(void * __restrict memptr) {\n  AkHeap     *heap;\n  AkHeapNode *heapNode;\n\n  heapNode = ak__alignof(memptr);\n  if (heapNode->heapid == 0)\n    heap = &ak__heap;\n  else\n    heap = ak_heap_lt_find(heapNode->heapid);\n\n  return ak_heap_getId(heap, heapNode);\n}\n\nAK_EXPORT\nvoid\nak_mem_setId(void * __restrict memptr,\n             void * __restrict memId) {\n  AkHeap     *heap;\n  AkHeapNode *heapNode;\n\n  heapNode = ak__alignof(memptr);\n  if (heapNode->heapid == 0)\n    heap = &ak__heap;\n  else\n    heap = ak_heap_lt_find(heapNode->heapid);\n\n  ak_heap_setId(heap,\n                heapNode,\n                memId);\n}\n\nAK_EXPORT\nAkResult\nak_mem_getMemById(void  * __restrict ctx,\n                  void  * __restrict memId,\n                  void ** __restrict dest) {\n  AkHeap     *heap;\n  AkHeapNode *heapNode;\n\n  heapNode = ak__alignof(ctx);\n  if (heapNode->heapid == 0)\n    heap = &ak__heap;\n  else\n    heap = ak_heap_lt_find(heapNode->heapid);\n\n  return ak_heap_getMemById(heap,\n                            memId,\n                            dest);\n}\n\nAK_EXPORT\nvoid\nak_mem_setp(void * __restrict memptr,\n            void * __restrict newparent) {\n  ak_heap_setp(ak__alignof(memptr),\n               ak__alignof(newparent));\n}\n\nAK_EXPORT\nvoid *\nak_mem_parent(void *mem) {\n  AkHeapNode *hnode;\n  if (!mem)\n    return NULL;\n\n  hnode = ak_heap_parent(ak__alignof(mem));\n  if (!hnode)\n    return NULL;\n\n  return ak__alignas(hnode);\n}\n\nAK_EXPORT\nvoid*\nak_malloc(void * __restrict parent,\n          size_t size) {\n  return ak_heap_alloc(&ak__heap,\n                       parent,\n                       size);\n}\n\nAK_EXPORT\nvoid*\nak_calloc(void * __restrict parent,\n          size_t size) {\n  void *memptr;\n\n  memptr = ak_heap_alloc(&ak__heap,\n                         parent,\n                         size);\n  memset(memptr, '\\0', size);\n\n  return memptr;\n}\n\nAK_EXPORT\nvoid*\nak_realloc(void * __restrict parent,\n           void * __restrict memptr,\n           size_t            newsize) {\n  return ak_heap_realloc(&ak__heap,\n                          parent,\n                          memptr,\n                          newsize);\n}\n\nAK_EXPORT\nchar*\nak_strdup(void       * __restrict parent,\n          const char * __restrict str) {\n  AkHeap *heap;\n  void   *memptr;\n  size_t  memsize;\n\n  if (parent) { heap = ak_heap_getheap(parent); }\n  else        { heap = &ak__heap;               }\n\n  memsize = strlen(str);\n  memptr  = ak_heap_alloc(heap, parent, memsize + 1);\n\n  memcpy(memptr, str, memsize);\n\n  /* NULL */\n  memset((char *)memptr + memsize, '\\0', 1);\n\n  return memptr;\n}\n\nAK_EXPORT\nint\nak_refc(void * __restrict mem) {\n  return ak_heap_refc(ak__alignof(mem));\n}\n\nAK_EXPORT\nint\nak_retain(void * __restrict mem) {\n  if (!mem) return 0;\n  return ak_heap_retain(ak__alignof(mem));\n}\n\nAK_EXPORT\nvoid\nak_release(void * __restrict mem) {\n  if (!mem) return;\n  ak_heap_release(ak__alignof(mem));\n}\n\nAK_EXPORT\nvoid\nak_free(void * __restrict memptr) {\n  AkHeap     *heap;\n  AkHeapNode *heapNode;\n\n  if (!memptr)\n    return;\n\n  heap     = &ak__heap;\n  heapNode = ak__alignof(memptr);\n\n  if (heapNode->heapid != heap->heapid)\n    heap = ak_heap_lt_find(heapNode->heapid);\n\n  if (!heap)\n    return;\n\n  /* free heap self instead of single free if this node attached to heap */\n  if (heap->data == memptr)\n    ak_heap_destroy(heap);\n  else\n    ak_heap_free(heap, heapNode);\n}\n\nAK_EXPORT\nAkObject*\nak_objAlloc(AkHeap * __restrict heap,\n            void   * __restrict memParent,\n            size_t              typeSize,\n            AkEnum              typeEnum,\n            bool                zeroed) {\n  AkObject * obj;\n\n  assert(typeSize > 0 && \"invalid parameter value\");\n\n  obj = ak_heap_alloc(heap,\n                      memParent,\n                      sizeof(*obj) + typeSize);\n\n  obj->size  = typeSize;\n  obj->type  = typeEnum;\n  obj->next  = NULL;\n  obj->pData = (void *)((char *)obj + offsetof(AkObject, data));\n\n  if (zeroed)\n    memset(obj->pData, '\\0', typeSize);\n\n  ak_setypeid(obj, AKT_OBJECT);\n\n  return obj;\n}\n\nAK_EXPORT\nvoid*\nak_userData(void * __restrict mem) {\n  void      *r;\n  uintptr_t  tmp;\n\n  if (!mem)\n    return NULL;\n\n  /* Keep `r` as void* (not uintptr_t*) — the storage may be misaligned\n     for uintptr_t, and UBSan flags the typed pointer even when the only\n     read is a memcpy. memcpy on void* is alignment-agnostic. */\n  if ((r = ak_heap_ext_get(ak__alignof(mem), AK_HEAP_NODE_FLAGS_USR))) {\n    memcpy(&tmp, r, sizeof(tmp));\n    return (void *)tmp;\n  }\n\n  return NULL;\n}\n\nAK_EXPORT\nvoid*\nak_heap_setUserData(AkHeap * __restrict heap,\n                    void   * __restrict mem,\n                    void   * __restrict userData) {\n  uintptr_t tmp;\n  void     *ext;\n  \n  if (!mem)\n    return NULL;\n  \n  if (!(ext = ak_heap_ext_add(heap, ak__alignof(mem), AK_HEAP_NODE_FLAGS_USR)))\n    return NULL;\n  \n  tmp = (uintptr_t)userData;\n  memcpy(ext, &tmp, sizeof(void *));\n\n  return ext;\n}\n\nAK_EXPORT\nvoid*\nak_setUserData(void * __restrict mem, void * __restrict userData) {\n  AkHeap *heap;\n  \n  if (!mem || !(heap = ak_heap_getheap(mem)))\n    return NULL;\n\n  return ak_heap_setUserData(heap, mem, userData);\n}\n\nAK_EXPORT\nAkObject*\nak_objFrom(void * __restrict memptr) {\n  AkObject *obj;\n  obj = (void *)((char *)memptr - offsetof(AkObject, data));\n  assert(obj->pData == memptr && \"invalid cas to AkObject\");\n  return obj;\n}\n\nvoid\nak_mem_init(void) {\n  ak__heap_sub = rb_newtree_ptr();\n  ak_heap_init(&ak__heap, NULL, NULL, NULL);\n  ak_heap_lt_init(&ak__heap);\n\n  ak_dsSetAllocator(ak__heap.allocator, ak__heap_sub->alc);\n}\n\nvoid\nak_mem_deinit(void) {\n  ak_heap_destroy(&ak__heap);\n  ak_heap_lt_cleanup();\n  rb_destroy(ak__heap_sub);\n}\n"
  },
  {
    "path": "src/mem/mmap.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n\n#ifndef AK_WINAPI\n#  include <sys/mman.h>\n#else\n#  define WIN32_LEAN_AND_MEAN\n#  include <windows.h>\n#  include <io.h>\n#endif\n\nAK_EXPORT\nvoid*\nak_mmap_rdonly(int fd, size_t size) {\n  void *mapped;\n  \n  mapped = NULL;\n\n#ifndef AK_WINAPI\n  mapped = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0);\n  if (!mapped || mapped == MAP_FAILED)\n    return NULL;\n\n  madvise(mapped, size, MADV_SEQUENTIAL);\n#else\n  HANDLE hmap;\n  if (!((hmap = CreateFileMapping((HANDLE)_get_osfhandle(fd), 0, PAGE_READONLY, 0, 0, 0))\n       && (mapped = MapViewOfFileEx(hmap, FILE_MAP_READ, 0, 0, size, 0))))\n    return NULL;\n\n  CloseHandle(hmap);\n#endif\n  \n  return mapped;\n}\n\nAK_EXPORT\nvoid\nak_unmap(void *file, size_t size) {\n#ifndef AK_WINAPI\n  munmap(file, size);\n#else\n  AK__UNUSED(size);\n  UnmapViewOfFile(file);\n#endif\n}\n\nAK_EXPORT\nvoid\nak_mmap_attach(void * __restrict obj, void * __restrict mapped, size_t sized) {\n  AkHeap          *heap;\n  AkHeapNode      *hnode;\n  AkMemoryMapNode **mmapNode, *mmapNodeNew;\n\n  heap     = ak_heap_getheap(obj);\n  hnode    = ak__alignof(obj);\n  mmapNode = (AkMemoryMapNode **)ak_heap_ext_add(heap, hnode, AK_HEAP_NODE_FLAGS_MMAP);\n\n  if (mmapNode) {\n    mmapNodeNew         = ak_heap_calloc(heap, obj, sizeof(*mmapNodeNew));\n    mmapNodeNew->mapped = mapped;\n    mmapNodeNew->sized  = sized;\n    \n    if (*mmapNode) {\n      mmapNodeNew->next = *mmapNode;\n      (*mmapNode)->prev = mmapNodeNew;\n    }\n    *mmapNode = mmapNodeNew;\n  }\n}\n\nAK_EXPORT\nvoid\nak_unmmap_attached(void * __restrict obj) {\n  AkHeapNode      *hnode;\n  AkMemoryMapNode **mmapNode, *it;\n  void            *tofree;\n\n  hnode = ak__alignof(obj);\n  if ((mmapNode = ak_heap_ext_get(hnode, AK_HEAP_NODE_FLAGS_MMAP))\n      && (it = *mmapNode)) {\n    while (it) {\n      ak_unmap(it->mapped, it->sized);\n      tofree = it;\n      it = it->next;\n      ak_free(tofree);\n    }\n    \n    *mmapNode = NULL;\n  }\n}\n"
  },
  {
    "path": "src/mem/rb.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n * \n * ---\n * \n * Insertion and Deletion are Top-Down\n *\n * X:  Current Node\n * P:  Parent Node         (Parent of X)\n * T:  X's Sibling Node\n * G:  Grand Parent Node   (Parent of P)\n * Q:  Great Parent Node   (Parent of Grand Parent)\n *\n * Y:  X's left child\n * Z:  X's right child\n *\n * sX: side of X           (if X is left then sX=0, right sX=1)\n * sP: side of P\n * sG: side of GD\n * sN: side of Next X      (Side of Next Current Node)\n *\n * cX: color of X\n */\n\n#include \"rb.h\"\n#include <stdio.h>\n\nvoid\nak_heap_rb_printNode(AkHeapSrchCtx * __restrict srchctx,\n                     AkHeapSrchNode * __restrict srchNode);\n\n/*\n simple assertion for rbtree\n source: Eternally Confuzzled\n */\nint\nak_heap_rb_assert(AkHeapSrchCtx * srchctx,\n                  AkHeapSrchNode * root) {\n  int lh, rh;\n  if (root == srchctx->nullNode) {\n    return 1;\n  } else {\n    struct AkHeapSrchNode *ln = root->chld[0];\n    struct AkHeapSrchNode *rn = root->chld[1];\n\n    /* Consecutive red links */\n    if (AK__RB_ISRED(root)) {\n      if (AK__RB_ISRED(ln) || AK__RB_ISRED(rn)) {\n        puts(\"Red violation\");\n        exit(0);\n        return 0;\n      }\n    }\n\n    lh = ak_heap_rb_assert(srchctx, ln);\n    rh = ak_heap_rb_assert(srchctx, rn);\n\n    /* Invalid binary search tree */\n    if ((ln != srchctx->nullNode\n         && srchctx->cmp(ln->key, root->key) > 0)\n        || (rn != srchctx->nullNode\n            && srchctx->cmp(rn->key, root->key) < 0)) {\n          puts(\"Binary tree violation\");\n          exit(0);\n          return 0;\n        }\n\n    /* Black height mismatch */\n    if (lh != 0 && rh != 0 && lh != rh) {\n      puts(\"Black violation\");\n      exit(0);\n      return 0;\n    }\n\n    /* Only count black links */\n    if (lh != 0 && rh != 0)\n      return AK__RB_ISRED(root) ? lh : lh + 1;\n\n    return 0;\n  }\n}\n\nvoid\nak_heap_rb_insert(AkHeapSrchCtx * __restrict srchctx,\n                  AkHeapSrchNode * __restrict srchNode) {\n  AkHeapSrchNode *X, *P, *G, *Q, *W;\n  int sQ, sG, sP, sX;\n\n  if (srchctx->root->chld[AK__BST_RIGHT] == srchctx->nullNode) {\n    AK__RB_MKBLACK(srchNode);\n    srchctx->root->chld[AK__BST_RIGHT] = srchNode;\n    return;\n  }\n\n  sQ = sG = sP = sX = 1;\n\n  W = P = G = Q = srchctx->root;\n  X = P->chld[AK__BST_RIGHT];\n\n  /* Top-Down Insert */\n  do {\n    /* main case : two children are red */\n    if (AK__RB_ISRED(X->chld[AK__BST_LEFT])\n        && AK__RB_ISRED(X->chld[AK__BST_RIGHT])) {\n\n      /* case 1: P is black */\n      if (!AK__RB_ISRED(P)) {\n        /* make X red */\n        AK__RB_MKRED(X);\n\n        /* make two children black */\n        AK__RB_MKBLACK(X->chld[AK__BST_LEFT]);\n        AK__RB_MKBLACK(X->chld[AK__BST_RIGHT]);\n      }\n\n      /* P is red */\n      else {\n        AK__RB_MKRED(G);\n\n        /* case 2: X and P are both left/right children */\n        if (sX == sP){\n          /* single rotation */\n\n          AK__RB_MKRED(X);\n          AK__RB_MKBLACK(P);\n          AK__RB_MKBLACK(X->chld[AK__BST_LEFT]);\n          AK__RB_MKBLACK(X->chld[AK__BST_RIGHT]);\n\n          Q->chld[sG]  = P;\n          G->chld[sP]  = P->chld[!sP];\n          P->chld[!sP] = G;\n\n          G = Q;\n          Q = W;\n        }\n\n        /* case 3: X and P are opposide side */\n        else {\n          AK__RB_MKBLACK(X);\n          AK__RB_MKBLACK(X->chld[AK__BST_LEFT]);\n          AK__RB_MKBLACK(X->chld[AK__BST_RIGHT]);\n\n          Q->chld[sG] = X;\n          P->chld[sX] = X->chld[sP];\n          G->chld[sP] = X->chld[sX];\n          X->chld[sP] = P;\n          X->chld[sX] = G;\n\n          G  = W;\n          P  = Q;\n          sX = sG;\n          sP = sQ;\n        }\n      }\n    }\n\n    sQ = sG;\n    sG = sP;\n    sP = sX;\n    sX = !(srchctx->cmp(srchNode->key, X->key) < 0);\n    W  = Q;\n    Q  = G;\n    G  = P;\n    P  = X;\n    X  = X->chld[sX];\n  } while (X != srchctx->nullNode);\n\n  X = P->chld[sX] = srchNode;\n\n  /* make current red */\n  AK__RB_MKRED(X);\n\n  /* check for red violation, we know uncle is black */\n  if (AK__RB_ISRED(P)) {\n    AK__RB_MKRED(G);\n\n    /* double rotation */\n    if (sX != sP){\n      AK__RB_MKBLACK(X);\n\n      Q->chld[sG]  = X;\n      P->chld[sX]  = X->chld[!sX];\n      G->chld[sP]  = X->chld[sX];\n      X->chld[!sX] = P;\n      X->chld[sX]  = G;\n    }\n\n    /* single rotation */\n    else {\n      AK__RB_MKBLACK(P);\n\n      G->chld[sP]  = P->chld[!sP];\n      P->chld[!sP] = G;\n      Q->chld[sG]  = P;\n    }\n  }\n\n  /* make root black */\n  AK__RB_MKBLACK(srchctx->root->chld[AK__BST_RIGHT]);\n}\n\nvoid\nak_heap_rb_remove(AkHeapSrchCtx * __restrict srchctx,\n                  AkHeapSrchNode * __restrict srchNode) {\n  AkHeapSrchNode *X, *P, *T, *G, *toDel, *toDelP;\n  int            sP, sX, cmpRet, sDel;\n  int            c2b;\n\n  if (srchNode == srchctx->nullNode)\n    return;\n\n  sX     = AK__BST_RIGHT;\n  G      = srchctx->root;\n  P      = G;\n  X      = P->chld[AK__BST_RIGHT];\n  toDel  = srchctx->nullNode;\n  toDelP = srchctx->nullNode;\n  sDel   = 0;\n\n  /* step 1: examine the root */\n  if (AK__RB_ISBLACK(X->chld[AK__BST_LEFT])\n      && AK__RB_ISBLACK(X->chld[AK__BST_RIGHT])) {\n    AK__RB_MKRED(X);\n    c2b = 0;\n  } else {\n    /* step 2B */\n    c2b = 1;\n  }\n\n  goto l0;\n\n  /* Top-Down Deletion */\n  do {\n    /* case 2b continue: check new X */\n    if (c2b) {\n      c2b = 0;\n\n      /* if new X is red continue down again */\n      if (AK__RB_ISRED(X))\n        goto l0;\n\n      G->chld[sP]  = T;\n      P->chld[!sX] = T->chld[sX];\n      T->chld[sX]  = P;\n\n      AK__RB_MKRED(P);\n      AK__RB_MKBLACK(T);\n\n      if (toDelP == G) {\n        toDelP = T;\n        sDel = sX;\n      }\n\n      G  = T;\n      T  = P->chld[!sX];\n      sP = sX;\n      /* if new X is black back to case 2 */\n    }\n\n    /* case 2: X has two black children */\n    if (AK__RB_ISBLACK(X->chld[AK__BST_LEFT])\n        && AK__RB_ISBLACK(X->chld[AK__BST_RIGHT])) {\n\n      /* case 1.a: T has two black children */\n      if (T != srchctx->nullNode\n          && AK__RB_ISBLACK(T->chld[AK__BST_LEFT])\n          && AK__RB_ISBLACK(T->chld[AK__BST_RIGHT])) {\n\n        /* color flip */\n        AK__RB_MKRED(X);\n        AK__RB_MKRED(T);\n        AK__RB_MKBLACK(P);\n      }\n\n      /* case 1.b: T's left child is red */\n      else if (AK__RB_ISRED(T->chld[sX])) {\n        AkHeapSrchNode *R;\n\n        R = T->chld[sX];\n\n        /* double rotate:\n           rotate R around T, then R around P\n         */\n        T->chld[sX]  = R->chld[!sX];\n        P->chld[!sX] = R->chld[sX];\n        R->chld[sX]  = P;\n        R->chld[!sX] = T;\n        G->chld[sP]  = R;\n\n        AK__RB_MKRED(X);\n        AK__RB_MKBLACK(P);\n\n        if (toDelP == G) {\n          toDelP = R;\n          sDel   = sX;\n        }\n      }\n\n      /* case 1.c: T's right child is red */\n      else if (AK__RB_ISRED(T->chld[!sX])) {\n        AkHeapSrchNode *R;\n\n        R = T->chld[!sX];\n\n        /* single rotate\n           rotate T around P\n         */\n        P->chld[!sX] = T->chld[sX];\n        T->chld[sX]  = P;\n        G->chld[sP]  = T;\n\n        AK__RB_MKRED(X);\n        AK__RB_MKRED(T);\n        AK__RB_MKBLACK(P);\n        AK__RB_MKBLACK(R);\n\n        if (toDelP == G) {\n          toDelP = T;\n          sDel   = sX;\n        }\n      }\n    } else {\n      /* case 2b: X's one child is red, advence to next level */\n      c2b = 1;\n    }\n\n  l0:\n    sP = sX;\n    if (toDel != srchctx->nullNode) {\n      sX = toDel->chld[AK__BST_RIGHT] == srchctx->nullNode;\n    } else {\n      cmpRet = srchctx->cmp(srchNode->key, X->key);\n\n      if (cmpRet != 0) {\n        sX = !(cmpRet < 0);\n      } else {\n        toDelP = P;\n        toDel  = X;\n        sDel   = sP;\n        sX     = toDel->chld[AK__BST_RIGHT] != srchctx->nullNode;\n      }\n    }\n\n    G  = P;\n    P  = X;\n    X  = P->chld[sX];\n    T  = P->chld[!sX];\n  } while (X != srchctx->nullNode);\n\n  /* make root black */\n  AK__RB_MKBLACK(srchctx->root->chld[AK__BST_RIGHT]);\n\n  if (toDel == srchctx->nullNode)\n    return;\n\n  /* toDel has least one child */\n  if (P != toDel) {\n    /* P is black, save black height */\n    if (c2b)\n      AK__RB_MKBLACK(P->chld[!sX]);\n\n    /* change color */\n    if (AK__RB_ISRED(toDel))\n      AK__RB_MKRED(P);\n    else\n      AK__RB_MKBLACK(P);\n\n    /* replace P with its left child */\n    G->chld[sP] = P->chld[!sX];\n\n    /* replace X with in-order predecessor */\n    P->chld[AK__BST_RIGHT] = toDel->chld[AK__BST_RIGHT];\n    P->chld[AK__BST_LEFT]  = toDel->chld[AK__BST_LEFT];\n    toDelP->chld[sDel]     = P;\n  }\n\n  /* P is toDel; there is no child */\n  else {\n    G->chld[sP] = srchctx->nullNode;\n  }\n}\n\nAkHeapSrchNode *\nak_heap_rb_find(AkHeapSrchCtx * __restrict srchctx,\n                void * __restrict key) {\n  AkHeapSrchNode *iter;\n\n  iter = srchctx->root->chld[AK__BST_RIGHT];\n\n  while (iter != srchctx->nullNode) {\n    int cmpRet;\n\n    cmpRet = srchctx->cmp(iter->key, key);\n\n    if (cmpRet == 0)\n      break;\n\n    iter = iter->chld[cmpRet < 0];\n  }\n\n  return iter;\n}\n\nint\nak_heap_rb_parent(AkHeapSrchCtx * __restrict srchctx,\n                  void * __restrict key,\n                  AkHeapSrchNode ** dest) {\n   AkHeapSrchNode *iter, *parent;\n   int side, cmpRet;\n\n   side   = AK__BST_RIGHT;\n   iter   = srchctx->root->chld[side];\n   parent = srchctx->root;\n   cmpRet = -1;\n\n   while (iter != srchctx->nullNode) {\n      cmpRet = srchctx->cmp(iter->key, key);\n\n      if (cmpRet == 0)\n         break;\n\n      side   = cmpRet < 0;\n      parent = iter;\n      iter   = iter->chld[side];\n   }\n\n   if (cmpRet != 0)\n      *dest = NULL;\n   else\n      *dest = parent;\n\n   return side;\n}\n\nvoid\nak_heap_rb_printNode(AkHeapSrchCtx * __restrict srchctx,\n                     AkHeapSrchNode * __restrict srchNode) {\n  if(srchNode != srchctx->nullNode) {\n    ak_heap_rb_printNode(srchctx,\n                         srchNode->chld[AK__BST_LEFT]);\n    srchctx->print(srchNode->key);\n    ak_heap_rb_printNode(srchctx,\n                         srchNode->chld[AK__BST_RIGHT]);\n  }\n}\n\nvoid\nak_heap_rb_print(AkHeapSrchCtx * __restrict srchctx) {\n  printf(\"\\nAssetKit Memory Id Dump:\\n\");\n  printf(\"------------------------\\n\");\n\n  if(srchctx->root->chld[AK__BST_RIGHT] == srchctx->nullNode)\n    printf(\"Empty tree\\n\");\n  else\n    ak_heap_rb_printNode(srchctx,\n                         srchctx->root->chld[AK__BST_RIGHT]);\n\n  printf(\"------------------------\\n\");\n}\n"
  },
  {
    "path": "src/mem/rb.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_memory_redblack_h\n#define ak_memory_redblack_h\n\n#include \"common.h\"\n\n#define AK__RB_ISBLACK(X) !((AK__HEAPNODE(X)->flags & AK_HEAP_NODE_FLAGS_RED))\n#define AK__RB_ISRED(X)     (AK__HEAPNODE(X)->flags & AK_HEAP_NODE_FLAGS_RED)\n#define AK__RB_MKRED(X)    AK__HEAPNODE(X)->flags |= AK_HEAP_NODE_FLAGS_RED\n#define AK__RB_MKBLACK(X)  AK__HEAPNODE(X)->flags &= ~AK_HEAP_NODE_FLAGS_RED\n\nvoid\nak_heap_rb_insert(AkHeapSrchCtx * __restrict srchctx,\n                  AkHeapSrchNode * __restrict srchNode);\n\nvoid\nak_heap_rb_remove(AkHeapSrchCtx * __restrict srchctx,\n                  AkHeapSrchNode * __restrict srchNode);\n\nAkHeapSrchNode *\nak_heap_rb_find(AkHeapSrchCtx * __restrict srchctx,\n                void * __restrict key);\n\nint\nak_heap_rb_parent(AkHeapSrchCtx * __restrict srchctx,\n                  void * __restrict key,\n                  AkHeapSrchNode ** dest);\n\nvoid\nak_heap_rb_print(AkHeapSrchCtx * __restrict srchctx);\n\nint\nak_heap_rb_assert(AkHeapSrchCtx * srchctx,\n                  AkHeapSrchNode * root);\n\n#endif /* ak_memory_redblack_h */\n"
  },
  {
    "path": "src/mesh/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/mesh/duplicator.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"index.h\"\n#include \"edit_common.h\"\n#include <limits.h>\n\nAK_EXPORT\nAkDuplicator*\nak_meshDuplicatorForIndices(AkMesh          * __restrict mesh,\n                            AkMeshPrimitive * __restrict prim) {\n  AkHeap             *heap;\n  AkDoc              *doc;\n  AkObject           *meshobj;\n  AkDuplicator       *dupl;\n  AkDuplicatorRange  *dupr;\n  AkUIntArray        *dupc, *ind, *newind, *dupcsum;\n  uint32_t           *it, *it2, *posflgs, *inp;\n  AkAccessor         *posAcc;\n  uint8_t            *flg;\n  size_t              count, ccount, icount, chk_start,\n                      chk_end, inpsz, vertc, i, j;\n  uint32_t            chk, iter, st, vo, posno, idxp;\n\n  if (!prim->pos || !(posAcc = prim->pos->accessor))\n    return NULL;\n\n  vertc   = posAcc->count;\n  meshobj = ak_objFrom(mesh);\n  heap    = ak_heap_getheap(meshobj);\n  doc     = ak_heap_data(heap);\n\n  if ((dupl = rb_find(doc->reserved, prim))) {\n    rb_remove(doc->reserved, prim);\n    ak_free(dupl); /* or cache maybe if mesh is not edited ? */\n  }\n\n  dupl = ak_heap_calloc(heap, NULL, sizeof(*dupl));\n\n  /* TODO: cache this for multiple primitives */\n  dupc = ak_heap_calloc(heap,\n                        dupl,\n                        sizeof(AkUIntArray) + sizeof(AkUInt) * vertc * 3);\n  dupc->count = posAcc->count;\n\n  st      = prim->indexStride;\n  vo      = prim->pos->offset;\n  ind     = prim->indices;\n  icount  = (uint32_t)ind->count / st;\n  newind  = ak_meshIndicesArrayFor(mesh, prim, true);\n  chk_end = icount;\n  it      = ind->items;\n  it2     = newind->items;\n  inpsz   = sizeof(AkUInt) * st;\n\n  flg     = ak_heap_calloc(heap, dupl, sizeof(uint8_t) * icount);\n  posflgs = ak_heap_calloc(heap,\n                           dupl,\n                           sizeof(AkUInt) * vertc * (st + 1));\n\n  chk_start = ccount = count = posno = 0;\n  iter = chk = 1;\n  while (ccount < icount) {\n    /* nothing to check */\n    if (chk_start >= chk_end)\n      break;\n\n    j = chk_start;\n    i = j * st;\n\n    for (; j < chk_end; i += st, j++) {\n      if (flg[j] == chk)\n        continue;\n\n      idxp  = it[i + vo];\n      inp   = posflgs + idxp * (st + 1);\n\n      if (inp[0] < iter) {\n        /* skip first squence */\n        if (iter > 1) {\n          dupc->items[3 * idxp + 1]++;\n          count++;\n        } else {\n          dupc->items[3 * idxp]     = posno++;\n          dupc->items[3 * idxp + 2] = idxp + 1;\n        }\n\n        inp[0] = iter;\n        memcpy(&inp[1], &it[i], inpsz);\n      } else if (memcmp(&it[i], &inp[1], inpsz) != 0) {\n        it2[j]++;\n        continue;\n      }\n\n      ccount++;\n      flg[j] = chk;\n\n      /* shrink the check range for next iter */\n      if (j == chk_start)\n        chk_start++;\n      else if (j == chk_end)\n        chk_end--;\n    }\n\n    iter++;\n  }\n\n  dupcsum = ak_heap_calloc(heap,\n                           dupc,\n                           sizeof(AkUIntArray) + sizeof(AkUInt) * (posno + 1));\n  dupcsum->count = posno;\n\n  for (i = 0; i < dupc->count; i++) {\n    uint32_t pno, d;\n\n    if (dupc->items[3 * i + 2] == 0)\n      continue;\n\n    pno = dupc->items[i * 3];\n    d   = dupc->items[i * 3 + 1];\n\n    dupcsum->items[pno + 1] = d;\n  }\n\n  for (i = 1; i < dupcsum->count; i++)\n    dupcsum->items[i] += dupcsum->items[i - 1];\n\n  dupr             = ak_heap_alloc(heap, dupl, sizeof(*dupr));\n  dupr->dupc       = dupc;\n  dupr->startIndex = 0;\n  dupr->endIndex   = posAcc->count;\n  dupr->next       = NULL;\n  dupr->dupcsum    = dupcsum;\n\n  dupl->range      = dupr;\n  dupl->dupCount   = count;\n  dupl->bufCount   = posno;\n\n  ak_free(flg);\n  ak_free(posflgs);\n\n  rb_insert(doc->reserved, prim, dupl);\n\n  return dupl;\n}\n\nAK_EXPORT\nvoid\nak_meshFixIndexBuffer(AkMesh          * __restrict mesh,\n                      AkMeshPrimitive * __restrict prim,\n                      AkDuplicator    * __restrict duplicator) {\n  AkDuplicatorRange *dupr;\n  AkUIntArray       *dupc, *dupcsum, *newind;\n  AkUInt            *it, *it2;\n  uint32_t           i, j, c, st, vo, idxp, nidxp;\n\n  dupr    = duplicator->range;\n  dupc    = dupr->dupc;\n  dupcsum = dupr->dupcsum;\n\n  newind = ak_meshIndicesArrayFor(mesh, prim, true);\n  it     = prim->indices->items;\n  it2    = newind->items;\n  st     = prim->indexStride;\n  vo     = prim->pos->offset;\n  c      = (uint32_t)prim->indices->count;\n\n  if (duplicator->dupCount > 0) {\n    for (i = j = 0; i < c; i += st, j++) {\n      idxp   = it[i + vo];\n      nidxp  = dupc->items[idxp * 3];\n      it2[j] = it2[j] + nidxp + dupcsum->items[nidxp];\n    }\n  } else {\n    for (i = j = 0; i < c; i += st, j++) {\n      idxp   = it[i + vo];\n      nidxp  = dupc->items[idxp * 3];\n      it2[j] = nidxp;\n    }\n  }\n}\n"
  },
  {
    "path": "src/mesh/edit.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../skin/fix.h\"\n#include \"edit_common.h\"\n\nconst char*\nak_mesh_edit_assert1 = \"you must call ak_meshBeginEdit* before this op\";\n\nAK_EXPORT\nvoid\nak_meshBeginEdit(AkMesh * __restrict mesh) {\n  ak_meshBeginEditA(mesh,\n                    AK_GEOM_EDIT_FLAG_ARRAYS\n                    | AK_GEOM_EDIT_FLAG_INDICES);\n}\n\nAK_EXPORT\nvoid\nak_meshBeginEditA(AkMesh  * __restrict mesh,\n                  AkGeometryEditFlags  flags) {\n  AkHeap           *heap;\n  AkObject         *meshobj;\n  AkMeshEditHelper *edith;\n\n  edith   = mesh->edith;\n  meshobj = ak_objFrom(mesh);\n  heap    = ak_heap_getheap(meshobj);\n\n  if (edith && ak_retain(edith) > 1)\n    return;\n\n  if (!edith) {\n    mesh->edith = edith = ak_heap_calloc(heap,\n                                         ak_objFrom(mesh),\n                                         sizeof(*edith));\n    ak_heap_ext_add(heap,\n                    ak__alignof(edith),\n                    AK_HEAP_NODE_FLAGS_REFC);\n  }\n\n  if ((flags & AK_GEOM_EDIT_FLAG_ARRAYS)\n      && !edith->buffers) {\n    edith->buffers         = rb_newtree_ptr();\n    edith->inputBufferMap  = ak_map_new(ak_cmp_ptr);\n\n    ak_dsSetAllocator(heap->allocator, edith->buffers->alc);\n\n    edith->buffers->onFreeNode = ak_meshFreeRsvBuff;\n\n    edith->flags |= AK_GEOM_EDIT_FLAG_ARRAYS;\n  }\n\n  if ((flags & AK_GEOM_EDIT_FLAG_INDICES)\n      && !edith->indices) {\n    edith->indices = rb_newtree_ptr();\n    edith->flags  |= AK_GEOM_EDIT_FLAG_INDICES;\n    ak_dsSetAllocator(heap->allocator, edith->indices->alc);\n  }\n\n  ak_meshReIndexInputs(mesh);\n\n  ak_retain(edith);\n}\n\nAK_EXPORT\nvoid\nak_meshEndEdit(AkMesh * __restrict mesh) {\n  AkMeshEditHelper *edith;\n\n  edith = mesh->edith;\n  if (!edith)\n    return;\n\n  if (ak_refc(edith) > 1) {\n    ak_release(edith);\n    return;\n  }\n\n  /* fix skin weights */\n  ak_skinFixWeights(mesh);\n\n  /* finish edit */\n  ak_meshFillBuffers(mesh);\n  ak_moveIndices(mesh);\n  ak_meshMoveBuffers(mesh);\n\n  if (edith->buffers)\n    rb_destroy(edith->buffers);\n\n  if (edith->indices)\n    rb_destroy(edith->indices);\n\n  if (edith->inputBufferMap)\n    ak_map_destroy(edith->inputBufferMap);\n\n  ak_release(edith);\n  mesh->edith = NULL;\n}\n"
  },
  {
    "path": "src/mesh/edit_buff.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../accessor.h\"\n#include \"../id.h\"\n\n#include <assert.h>\n\n#include \"edit_common.h\"\n\nextern const char* ak_mesh_edit_assert1;\n\nvoid\nak_meshFreeRsvBuff(RBTree *tree, RBNode *node) {\n  AkSourceBuffState *buffstate;\n\n  if (node == tree->nullNode)\n    return;\n\n  buffstate = node->val;\n  ak_free(buffstate);\n}\n\nAK_EXPORT\nAkSourceBuffState*\nak_meshReserveBuffer(AkMesh * __restrict mesh,\n                     void   * __restrict buffid,\n                     size_t              itemSize,\n                     uint32_t            stride,\n                     size_t              acc_count) {\n  AkHeap            *heap;\n  AkSourceBuffState *buffstate;\n  AkBuffer          *buff;\n  AkMeshEditHelper  *edith;\n  AkObject          *meshobj;\n  size_t             newsize, count;\n\n  edith = mesh->edith;\n  assert(edith && ak_mesh_edit_assert1);\n\n  meshobj = ak_objFrom(mesh);\n  heap    = ak_heap_getheap(meshobj);\n  count   = acc_count * stride;\n\n  if (!(edith->flags & AK_GEOM_EDIT_FLAG_ARRAYS)\n      || !edith->buffers) {\n    edith->buffers = rb_newtree_str();\n    edith->flags |= AK_GEOM_EDIT_FLAG_ARRAYS;\n    ak_dsSetAllocator(heap->allocator, edith->buffers->alc);\n  }\n\n  buffstate = rb_find(edith->buffers, buffid);\n  newsize   = itemSize * count;\n\n  if (!buffstate) {\n    buffstate    = ak_heap_calloc(heap, meshobj, sizeof(*buffstate));\n    buff         = ak_heap_calloc(heap, meshobj, sizeof(*buff));\n    buff->length = newsize;\n    buff->data   = ak_heap_calloc(heap, buff, newsize);\n\n    buffstate->buff   = buff;\n    buffstate->count  = count;\n    buffstate->stride = stride;\n\n    rb_insert(edith->buffers, buffid, buffstate);\n    return buffstate;\n  }\n\n  buff = buffstate->buff;\n  if (buff->length < newsize) {\n    buff->data   = ak_heap_realloc(heap, meshobj, buff->data, newsize);\n    buff->length = newsize;\n  }\n\n  return buffstate;\n}\n\nAK_EXPORT\nvoid\nak_meshReserveBufferForInput(AkMesh   * __restrict mesh,\n                             AkInput  * __restrict input,\n                             size_t                count) {\n  AkHeap             *heap;\n  AkObject           *meshobj;\n  AkMeshEditHelper   *edith;\n  AkSourceEditHelper *srch;\n  AkSourceBuffState  *buffstate;\n  AkAccessor         *acci, *newacc;\n  AkBuffer           *buffi;\n  void               *buffid;\n\n  meshobj = ak_objFrom(mesh);\n  heap    = ak_heap_getheap(meshobj);\n\n  edith = mesh->edith;\n  assert(edith && ak_mesh_edit_assert1);\n\n  if (!(acci = input->accessor)\n      || !acci->buffer)\n    return;\n\n  /* generate new accesor for input */\n  newacc        = ak_accessor_dup(acci);\n  newacc->count = (uint32_t)count;\n\n  buffid    = input;\n  buffstate = ak_meshReserveBuffer(mesh,\n                                   buffid,\n                                   acci->bytesPerComponent,\n                                   acci->componentCount,\n                                   count);\n  buffi = buffstate->buff;\n\n  newacc->byteOffset    = 0;\n  /* New buffer is tightly-packed for this input alone (one accessor →\n     one buffer). ak_accessor_dup memcpy'd the SOURCE byteStride which\n     is wrong here when the source was interleaved (stride > fillByteSize):\n     ak_meshFillBuffers would then write at `newByteSt * newidx` past the\n     buffer end. Reset to fillByteSize so writes match the layout we\n     allocated for. */\n  newacc->byteStride    = newacc->fillByteSize;\n  srch                  = ak_heap_calloc(heap, meshobj, sizeof(*srch));\n  srch->oldsource       = acci;\n  srch->source          = newacc;\n  newacc->buffer        = buffi;\n  newacc->byteLength    = newacc->count * newacc->fillByteSize;\n\n  ak_heap_setpm(newacc, srch->source);\n\n  ak_map_add(edith->inputBufferMap, srch, input);\n}\n\nAK_EXPORT\nvoid\nak_meshReserveBuffers(AkMesh          * __restrict mesh,\n                      AkMeshPrimitive * __restrict prim,\n                      size_t                       count) {\n  AkInput *input;\n\n  input = prim->input;\n  while (input) {\n    ak_meshReserveBufferForInput(mesh, input, count);\n    input = input->next;\n  }\n}\n\nAK_EXPORT\nAkSourceEditHelper*\nak_meshSourceEditHelper(AkMesh  * __restrict mesh,\n                        AkInput * __restrict input) {\n  AkMeshEditHelper   *edith;\n  AkSourceEditHelper *srch;\n\n  edith = mesh->edith;\n  assert(edith && ak_mesh_edit_assert1);\n\n  srch = (AkSourceEditHelper *)ak_map_find(edith->inputBufferMap, input);\n\n  /* use old source as new */\n  if (!srch) {\n    /* TODO: */\n  }\n\n  return srch;\n}\n\nAK_EXPORT\nvoid\nak_meshMoveBuffers(AkMesh * __restrict mesh) {\n  AkHeap             *mapHeap;\n  AkMeshEditHelper   *edith;\n  AkSourceEditHelper *srch;\n  AkMapItem          *mi;\n  AkInput            *input;\n  AkMeshPrimitive    *prim;\n\n  edith   = mesh->edith;\n  mapHeap = edith->inputBufferMap->heap;\n  mi      = edith->inputBufferMap->root;\n\n  while (mi) {\n    input = ak_heap_getId(mapHeap, ak__alignof(mi));\n    srch  = (AkSourceEditHelper *)mi->data;\n    prim  = ak_mem_parent(input);\n\n    /* TODO */\n    // ak_release(input->accessor);\n\n    ak_release(srch->oldsource);\n    ak_retain(srch->source);\n\n    input->accessor = srch->source;\n\n    if (input->semantic == AK_INPUT_POSITION)\n      prim->pos = input;\n\n    mi = mi->next;\n  }\n}\n"
  },
  {
    "path": "src/mesh/edit_buff_fixup.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n\n#include <assert.h>\n\nextern const char* ak_mesh_edit_assert1;\n\nAK_EXPORT\nAkResult\nak_meshFillBuffers(AkMesh * __restrict mesh) {\n  AkMeshEditHelper   *edith;\n  AkInput            *input;\n  AkMeshPrimitive    *primi;\n  AkAccessor         *acc, *newacc;\n  AkUIntArray        *ind1, *ind2;\n  AkUInt             *ind1_it, *ind2_it;\n  AkBuffer           *oldbuff, *newbuff;\n  AkSourceBuffState  *buffstate;\n  AkSourceEditHelper *srch;\n  char               *olditms, *newitms;\n  size_t              icount, i;\n  AkUInt              oldidx, newidx, oldByteSt, newByteSt, fillSize, indxSt,\n                      inpOff;\n\n  edith = mesh->edith;\n  primi = mesh->primitive;\n \n  /* per-primitive inputs */\n  while (primi) {\n    ind1 = primi->indices;\n    ind2 = ak_meshIndicesArrayFor(mesh, primi, false);\n\n    /* same index buff */\n    if (!ind1 || ind1 == ind2) {\n      primi = primi->next;\n      continue;\n    }\n\n    ind1_it = ind1->items;\n    ind2_it = ind2->items;\n    input   = primi->input;\n\n    while (input) {\n      if (input->semantic == AK_INPUT_POSITION\n          || !(acc     = input->accessor)\n          || !(oldbuff = acc->buffer))\n        goto cont;\n\n      /* copy buff to mesh */\n      if ((buffstate = rb_find(edith->buffers, input))) {\n        srch      = ak_meshSourceEditHelper(mesh, input);\n        newbuff   = buffstate->buff;\n        newacc    = srch->source;\n        oldByteSt = (AkUInt)acc->byteStride;\n        newByteSt = (AkUInt)newacc->byteStride;\n        fillSize  = (AkUInt)acc->fillByteSize;\n\n        assert(newacc && \"accessor is needed!\");\n\n        inpOff  = input->offset;\n        indxSt  = primi->indexStride;\n        icount  = primi->indices->count / indxSt;\n        newitms = (char *)newbuff->data + newacc->byteOffset;\n        olditms = (char *)oldbuff->data + acc->byteOffset;\n        \n        for (i = 0; i < icount; i++) {\n          oldidx = ind1_it[i * indxSt + inpOff];\n          newidx = ind2_it[i];\n\n          memcpy(newitms + newByteSt * newidx,\n                 olditms + oldByteSt * oldidx,\n                 fillSize);\n        }\n\n        /* to prevent duplication operation for next time */\n        input->offset = 0;\n      }\n\n    cont:\n      input = input->next;\n    }\n\n    primi = primi->next;\n  }\n\n  return AK_OK;\n}\n"
  },
  {
    "path": "src/mesh/edit_common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_mesh_edit_common_h\n#define ak_mesh_edit_common_h\n\n#include \"../common.h\"\n\ntypedef struct AkPrimProxy {\n  struct AkPrimProxy *next;\n  AkMeshPrimitive    *orig;\n  uint8_t            *flg;\n  AkUIntArray        *ind;\n  AkUIntArray        *newind;\n  AkInput            *input;\n  uint32_t           *inpi;\n  uint32_t            chk_start;\n  uint32_t            chk_end;\n  uint32_t            vo;     /* vertOffset */\n  uint32_t            st;\n  uint32_t            count;\n  uint32_t            icount;\n  uint32_t            ccount; /* checked count */\n} AkPrimProxy;\n\ntypedef struct AkInputDesc {\n  struct AkInputDesc *next;\n  const char         *semantic;\n  AkURL              *source;\n  AkInput            *input;\n  AkPrimProxy        *pp;\n  uint32_t            set;\n  int32_t             index;\n} AkInputDesc;\n\nvoid\nak_meshFreeRsvBuff(RBTree *tree, RBNode *node);\n\n#endif /* ak_meh_edit_common_h */\n"
  },
  {
    "path": "src/mesh/edit_index.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n\n#include <assert.h>\n\nextern const char* ak_mesh_edit_assert1;\n\nAK_EXPORT\nAkUIntArray*\nak_meshIndicesArrayFor(AkMesh          * __restrict mesh,\n                       AkMeshPrimitive * __restrict prim,\n                       bool                         creat) {\n  AkHeap           *heap;\n  AkObject         *meshobj;\n  AkMeshEditHelper *edith;\n  AkUIntArray      *indices;\n  size_t            count;\n\n  edith = mesh->edith;\n  assert(edith && ak_mesh_edit_assert1);\n\n  meshobj = ak_objFrom(mesh);\n  heap    = ak_heap_getheap(meshobj);\n\n  if (!(edith->flags & AK_GEOM_EDIT_FLAG_INDICES)\n      || !edith->indices) {\n    edith->indices = rb_newtree_ptr();\n    edith->flags  |= AK_GEOM_EDIT_FLAG_INDICES;\n    ak_dsSetAllocator(heap->allocator, edith->indices->alc);\n  }\n\n  indices = rb_find(edith->indices, prim);\n  if (!indices) {\n    if (!creat)\n      return prim->indices;\n\n    if (prim->indices) {\n      count = prim->indices->count / prim->indexStride;\n    } else {\n      AkAccessor *posacc;\n\n      if (!prim->pos\n          || !(posacc  = prim->pos->accessor)\n          || !posacc->buffer)\n        return NULL;\n\n      count = posacc->componentCount * posacc->count;\n    }\n\n    indices = ak_heap_calloc(heap,\n                             prim,\n                             sizeof(*indices)\n                             + sizeof(AkUInt) * count);\n    indices->count = count;\n\n    rb_insert(edith->indices, prim, indices);\n    return indices;\n  }\n\n  return indices;\n}\n\nAK_EXPORT\nvoid\nak_moveIndices(AkMesh * __restrict mesh) {\n  AkMeshPrimitive *prim;\n  AkInput         *input;\n\n  /* fix indices */\n  prim = mesh->primitive;\n  while (prim) {\n    AkUIntArray *indices;\n\n    indices = ak_meshIndicesArrayFor(mesh, prim, false);\n    /* same index buff */\n    if (!indices || indices == prim->indices) {\n      prim = prim->next;\n      continue;\n    }\n\n    ak_free(prim->indices);\n\n    prim->indices = indices;\n\n    /* mark primitive as single index */\n    prim->indexStride = 1;\n\n    /* make all offsets 0 */\n    input = prim->input;\n    while (input) {\n      input->offset = 0;\n      input = input->next;\n    }\n\n    prim = prim->next;\n  }\n}\n"
  },
  {
    "path": "src/mesh/index.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"index.h\"\n#include \"../../include/ak/trash.h\"\n\n#include <limits.h>\n#include <assert.h>\n\nextern const char* ak_mesh_edit_assert1;\n\nAK_HIDE\nAkResult\nak_movePositions(AkMesh          *mesh,\n                 AkMeshPrimitive *prim,\n                 AkDuplicator    *duplicator) {\n  AkMeshEditHelper   *edith;\n  AkSourceEditHelper *srch;\n  AkSourceBuffState  *buffstate;\n  AkAccessor         *acc, *newacc;\n  AkUIntArray        *dupc, *dupcsum;\n  AkBuffer           *oldbuff, *newbuff;\n  char               *olditms, *newitms;\n  size_t              vc, d, s, pno, poo, byteStride;\n  uint32_t            i, j;\n\n  if (!prim->pos\n      || !(edith     = mesh->edith)\n      || !(acc       = prim->pos->accessor)\n      || !(oldbuff   = acc->buffer)\n      || !(buffstate = rb_find(edith->buffers, prim->pos)))\n    return AK_ERR;\n\n  newbuff = buffstate->buff;\n  srch    = ak_meshSourceEditHelper(mesh, prim->pos);\n  newacc  = srch->source;\n\n  if (!newacc)\n    return AK_ERR;\n\n  dupc       = duplicator->range->dupc;\n  dupcsum    = duplicator->range->dupcsum;\n  vc         = dupc->count;\n  newitms    = newbuff->data;\n  olditms    = oldbuff->data;\n  byteStride = acc->byteStride;\n\n  /* copy vert positions to new location */\n  for (i = 0; i < vc; i++) {\n    if ((poo = dupc->items[3 * i + 2]) == 0)\n      continue;\n\n    pno = dupc->items[3 * i];\n    d   = dupc->items[3 * i + 1];\n    s   = dupcsum->items[pno];\n\n    for (j = 0; j <= d; j++) {\n      memcpy(newitms + byteStride * (pno + j + s),\n             olditms + byteStride * (poo - 1),\n             byteStride);\n    }\n  }\n\n  return AK_OK;\n}\n\nAK_HIDE\nAkResult\nak_primFixIndices(AkMesh          *mesh,\n                  AkMeshPrimitive *prim) {\n  AkDuplicator *dupl;\n\n  if ((prim->indexStride == 1)\n      || !prim->indices\n      || !(dupl = ak_meshDuplicatorForIndices(mesh, prim)))\n    return AK_ERR;\n\n  ak_meshFixIndexBuffer(mesh, prim, dupl);\n  ak_meshReserveBuffers(mesh, prim, dupl->dupCount + dupl->bufCount);\n  ak_movePositions(mesh, prim, dupl);\n\n  return AK_OK;\n}\n\nAK_HIDE\nAkResult\nak_meshFixIndicesDefault(AkMesh *mesh) {\n  AkMeshPrimitive *prim;\n\n  prim = mesh->primitive;\n  while (prim) {\n    ak_primFixIndices(mesh, prim);\n    prim = prim->next;\n  }\n\n  return AK_OK;\n}\n\nAK_HIDE\nAkResult\nak_meshFixIndices(AkMesh *mesh) {\n  AkResult ret;\n\n  ak_meshBeginEdit(mesh);\n\n  /* currently only default option */\n  ret = ak_meshFixIndicesDefault(mesh);\n\n  ak_meshEndEdit(mesh);\n\n  return ret;\n}\n"
  },
  {
    "path": "src/mesh/index.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_mesh_index_h\n#define ak_mesh_index_h\n\n#include \"../common.h\"\n\nAK_HIDE\nAkResult\nak_meshFixIndices(AkMesh *mesh);\n\nAK_HIDE\nAkResult\nak_primFixIndices(AkMesh          *mesh,\n                  AkMeshPrimitive *prim);\n\nAK_HIDE\nAkResult\nak_meshFixIndicesDefault(AkMesh *mesh);\n\nAK_HIDE\nAkResult\nak_movePositions(AkMesh          *mesh,\n                 AkMeshPrimitive *prim,\n                 AkDuplicator    *duplicator);\n\n#endif /* ak_mesh_index_h */\n"
  },
  {
    "path": "src/mesh/input.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"index.h\"\n#include \"edit_common.h\"\n#include <stdio.h>\n#include <string.h>\n\nvoid\nak_meshReIndexInputs(AkMesh * __restrict mesh) {\n  AkHeap          *heap;\n  AkObject        *meshobj;\n  RBTree          *tree;\n  AkInput         *inp;\n  AkMeshPrimitive *prim;\n  RBNode          *found;\n\n  meshobj = ak_objFrom(mesh);\n  heap    = ak_heap_getheap(meshobj);\n  tree    = rb_newtree_str();\n  prim    = mesh->primitive;\n\n  ak_dsSetAllocator(heap->allocator, tree->alc);\n\n  if (prim) {\n    do {\n      inp = prim->input;\n      while (inp) {\n        found = rb_find_node(tree, (void *)inp->semanticRaw);\n        if (found) {\n          found->val = (void *)((uintptr_t)found->val) + 1;\n          inp->index = (int32_t)(uintptr_t)found->val;\n          inp->isIndexed = true;\n        } else {\n          rb_insert(tree, (void *)inp->semanticRaw, NULL);\n          inp->index = 0;\n          inp->isIndexed = false;\n        }\n        inp = inp->next;\n      }\n\n      if (prim->next)\n        rb_empty(tree);\n\n      prim = prim->next;\n    } while (prim);\n\n    /* check for correct zero postfix e.g. TECOORD0 */\n    if (ak_opt_get(AK_OPT_ZERO_INDEXED_INPUT)) {\n      prim = mesh->primitive;\n      do {\n        inp = prim->input;\n        while (inp) {\n          found = rb_find_node(tree, (void *)inp->semanticRaw);\n          if (found && !inp->index && found->val)\n            inp->isIndexed = true;\n          inp = inp->next;\n        }\n\n        rb_empty(tree);\n        prim = prim->next;\n      } while (prim);\n    }\n  }\n\n  rb_destroy(tree);\n}\n\nvoid\nak_inputNameIndexed(AkInput * __restrict input,\n                    char    * __restrict buf) {\n  if (!input->semanticRaw)\n    return;\n\n  if (input->isIndexed)\n    sprintf(buf, \"%s%d\", input->semanticRaw, input->index);\n  else\n    strcpy(buf, input->semanticRaw);\n}\n\nAK_EXPORT\nvoid\nak_inputNameBySet(AkInput * __restrict input,\n                  char    * __restrict buf) {\n//  if (!input->semanticRaw)\n//    return;\n//\n//  if (input->set > 0)\n//    sprintf(buf, \"%s%u\", input->semanticRaw, input->set);\n//  else\n//    strcpy(buf, input->semanticRaw);\n\n  ak_inputNameIndexed(input, buf);\n}\n\nAK_EXPORT\nAkInput*\nak_meshInputGet(AkMeshPrimitive *prim,\n                const char      *inputSemantic,\n                uint32_t         set) {\n  AkInput *input;\n\n  /* first search in primitive */\n  input = prim->input;\n  while (input) {\n    if (!input->semanticRaw)\n      goto cont1;\n\n    if (strcmp(input->semanticRaw, inputSemantic) == 0\n        && input->set == set)\n      return input;\n\n  cont1:\n    input = input->next;\n  }\n\n  return NULL;\n}\n"
  },
  {
    "path": "src/mesh/isolate.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n\nAK_EXPORT\nbool\nak_meshIsPrimIsolated(AkMeshPrimitive *prim) {\n  AkInput    *inp;\n  AkAccessor *acc;\n  AkBuffer   *buff;\n\n  if (!(inp = prim->input))\n    return true;\n  \n  do {\n    /* check accessor reference count */\n    if ((acc = inp->accessor) && ak_refc(acc) > 1)\n      return false;\n    \n    /* check buffer reference count */\n    if ((buff = acc->buffer) && ak_refc(buff) > 1)\n      return false;\n  } while ((inp = inp->next));\n\n  return true;\n}\n\nAK_EXPORT\nbool\nak_meshIsIsolated(AkMesh *mesh) {\n  AkMeshPrimitive  *prim;\n\n  if (mesh && (prim = mesh->primitive)) {\n    do {\n      if (!ak_meshIsPrimIsolated(prim))\n        return false;\n    } while ((prim = prim->next));\n  }\n  \n  return true;\n}\n\nAK_EXPORT\nvoid\nak_meshIsolatePrim(AkMeshPrimitive *prim) {\n  if (!prim)\n    return;\n  \n  /* detach accessors */\n  \n  /* detach buffers */\n}\n\nAK_EXPORT\nvoid\nak_meshIsolate(AkMesh *mesh) {\n  AkMeshPrimitive  *prim;\n  \n  if (!mesh)\n    return;\n\n  if ((prim = mesh->primitive)) {\n    do {\n      ak_meshIsolatePrim(prim);\n    } while ((prim = prim->next));\n  }\n}\n"
  },
  {
    "path": "src/mesh/material.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include <assert.h>\n\nAK_EXPORT\nAkResult\nak_meshSetMaterial(AkMeshPrimitive *prim,\n                   const char      *material) {\n  AkGeometry *geom;\n  AkMap      *map;\n\n  geom = prim->mesh->geom;\n\n#ifdef DEBUG\n  assert(geom && \"set geometry for this primitive!\");\n  assert(geom->materialMap && \"set materialMap for this geom!\");\n#endif\n\n  map = geom->materialMap;\n\n  /* TODO: remove first */\n  ak_multimap_add(map, prim, (void *)material);\n\n  prim->bindmaterial = material;\n  return AK_OK;\n}\n"
  },
  {
    "path": "src/mesh/normal.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../id.h\"\n#include \"../data.h\"\n\n#include \"index.h\"\n\n#include <cglm/cglm.h>\n\nAK_HIDE\nvoid\nak_meshPrimGenNormals(AkMeshPrimitive    * __restrict prim);\n\nAK_EXPORT\nbool\nak_meshPrimNeedsNormals(AkMeshPrimitive * __restrict prim) {\n  AkAccessor   *acc;\n  AkInput      *input;\n  bool          ret;\n\n  if (!prim\n      || (prim->type != AK_PRIMITIVE_TRIANGLES\n          && prim->type != AK_PRIMITIVE_POLYGONS))\n    return false;\n\n  ret   = true;\n  input = prim->input;\n  while (input) {\n    if (input->semantic == AK_INPUT_NORMAL) {\n      if (!(acc = input->accessor) || !acc->buffer)\n        return ret;\n      ret = false;\n      break;\n    }\n\n    input = input->next;\n  }\n\n  return ret;\n}\n\nAK_EXPORT\nbool\nak_meshNeedsNormals(AkMesh * __restrict mesh) {\n  AkMeshPrimitive *prim;\n  bool ret;\n\n  ret  = false;\n  prim = mesh->primitive;\n  while (prim) {\n    ret |= ak_meshPrimNeedsNormals(prim);\n    if (ret)\n      break;\n    prim = prim->next;\n  }\n\n  return ret;\n}\n\nAK_HIDE\nvoid\nak_meshPrimGenNormals(AkMeshPrimitive * __restrict prim) {\n  AkDataContext *dctx;\n  AkDoc         *doc;\n  AkUIntArray   *inpIndices;\n  AkFloat       *pos;\n  AkUInt        *it, *it2;\n  AkHeap        *heap;\n  AkInput       *input, *nextInput;\n  AkBuffer      *posBuff, *buff;\n  AkAccessor    *posAcc, *acc;\n  AkUInt         st, newst;\n  AkInt          vo, pos_st;\n  uint32_t       count;\n\n  if ((prim->type    != AK_PRIMITIVE_TRIANGLES\n       && prim->type != AK_PRIMITIVE_POLYGONS)\n      || !prim->pos\n      || !(posAcc     = prim->pos->accessor)\n      || !(posBuff    = posAcc->buffer)\n      || (vo          = prim->pos->offset) == -1)\n    return;\n\n  dctx   = ak_data_new(prim, 64, sizeof(vec3), ak_cmp_vec3);\n  heap   = ak_heap_getheap(prim);\n  doc    = ak_heap_data(heap);\n  pos    = posBuff->data;\n  pos_st = posAcc->componentCount;\n\n  if (!prim->indices || prim->indices->count == 0)\n    return;\n\n  it    = prim->indices->items + vo;\n  st    = prim->indexStride;\n  count = (uint32_t)prim->indices->count / st;\n  newst = st + 1;\n\n  /* TODO: for now join this into existing indices,\n           but in the future use separate to fix indices  */\n  inpIndices = ak_heap_calloc(heap,\n                              prim,\n                              sizeof(*inpIndices)\n                              + count * newst * sizeof(*it));\n  inpIndices->count = count * newst;\n  it2 = inpIndices->items;\n\n  switch (prim->type) {\n    case AK_PRIMITIVE_POLYGONS: {\n      AkPolygon *poly;\n      AkUInt    *vc_it;\n      float     *a, *b, *c;\n      vec3       v1, v2, n;\n      size_t     i, j, k, vc, ist;\n      AkUInt     idx;\n\n      poly = (AkPolygon *)prim;\n\n      /* polygon ha triangulated */\n      if (!poly->vcount)\n        goto tri;\n\n      vc_it = poly->vcount->items;\n\n      for (i = k = 0; k < poly->vcount->count; k++) {\n        vc = vc_it[k];\n\n        /* TODO: normals for lines or points ? */\n        if (vc < 3)\n          continue;\n\n        ist = i * st + vo;\n\n        a = pos + it[ist]           * pos_st;\n        b = pos + it[ist + st]      * pos_st;\n        c = pos + it[ist + st + st] * pos_st;\n\n        glm_vec3_sub(a, b, v1);\n        glm_vec3_sub(b, c, v2);\n\n        glm_vec3_cross(v1, v2, n);\n        glm_vec3_normalize(n);\n\n        idx = ak_data_append(dctx, n);\n\n        for (j = i; j < i + vc; j++) {\n          /* other inputs */\n          memcpy(it2 + j * newst, it  + j * st, sizeof(*it) * st);\n\n          /* normal */\n          it2[j * newst + st] = idx;\n        }\n\n        i += vc;\n      }\n      break;\n    }\n    case AK_PRIMITIVE_TRIANGLES: tri: {\n      AkTriangles *tri;\n      float *a, *b, *c;\n      vec3   v1, v2, n;\n      AkUInt i, j, idx, ist;\n\n      tri = (AkTriangles *)prim;\n      switch (tri->mode) {\n        case AK_TRIANGLES:\n          for (i = 0; i < count; i += 3 /* 3: triangle */) {\n            ist = i * st + vo;\n\n            a = pos + it[ist]           * pos_st;\n            b = pos + it[ist + st]      * pos_st;\n            c = pos + it[ist + st + st] * pos_st;\n\n            glm_vec3_sub(a, b, v1);\n            glm_vec3_sub(b, c, v2);\n\n            glm_vec3_cross(v1, v2, n);\n            glm_vec3_normalize(n);\n\n            idx = ak_data_append(dctx, n);\n\n            for (j = i; j < i + 3; j++) {\n              /* other inputs */\n              memcpy(it2 + j * newst, it  + j * st, sizeof(*it) * st);\n\n              /* normal */\n              it2[j * newst + st] = idx;\n            }\n          }\n          break;\n        case AK_TRIANGLE_FAN: {\n          float *central = pos + it[vo] * pos_st; // Central vertex\n          for (i = 1; i < count - 1; i++) {\n            a = central;\n            b = pos + it[vo + i * st] * pos_st;\n            c = pos + it[vo + (i + 1) * st] * pos_st;\n\n            // Calculate normal\n            glm_vec3_sub(b, a, v1);\n            glm_vec3_sub(c, a, v2);\n            glm_vec3_cross(v1, v2, n);\n            glm_vec3_normalize(n);\n\n            idx = ak_data_append(dctx, n);\n\n            // Assign normals to central, current, and next vertex\n            it2[vo * newst + st] = idx; // Central vertex normal (may need adjustment)\n            it2[(vo + i * st) * newst + st] = idx; // Current vertex normal\n            it2[(vo + (i + 1) * st) * newst + st] = idx; // Next vertex normal\n          }\n          break;\n        }\n        case AK_TRIANGLE_STRIP: {\n          for (i = 0; i < count - 2; i++) {\n            a = pos + it[vo + i * st] * pos_st;\n            b = pos + it[vo + (i + 1) * st] * pos_st;\n            c = pos + it[vo + (i + 2) * st] * pos_st;\n\n            // Calculate normal\n            glm_vec3_sub(b, a, v1);\n            glm_vec3_sub(c, b, v2);\n            glm_vec3_cross(v1, v2, n);\n            glm_vec3_normalize(n);\n\n            idx = ak_data_append(dctx, n);\n\n            // Assign normals to the three vertices of the triangle\n            it2[(vo + i * st) * newst + st] = idx;\n            it2[(vo + (i + 1) * st) * newst + st] = idx;\n            it2[(vo + (i + 2) * st) * newst + st] = idx;\n          }\n          break;\n        }\n\n        default:\n          break;\n      }\n      break;\n    }\n    default:\n      ak_free(inpIndices);\n      return;\n  }\n\n  acc = ak_heap_calloc(heap, doc, sizeof(*acc));\n  ak_setypeid(acc, AKT_ACCESSOR);\n\n  acc->componentCount    = 3;\n  acc->count             = (uint32_t)dctx->itemcount;\n  acc->componentType     = AKT_FLOAT;\n  acc->componentSize     = AK_COMPONENT_SIZE_VEC3;\n  acc->bytesPerComponent = ak_typeDesc(acc->componentType)->size;\n  acc->byteStride        = acc->componentCount * acc->bytesPerComponent;\n  acc->fillByteSize      = acc->byteStride;\n  acc->byteLength        = acc->count * acc->byteStride;\n\n  buff                   = ak_heap_calloc(heap, doc, sizeof(*buff));\n  buff->data             = ak_heap_alloc(heap, buff, acc->byteLength);\n  buff->length           = acc->byteLength;\n\n  acc->buffer            = buff;\n\n  flist_sp_insert(&doc->lib.accessors, acc);\n  flist_sp_insert(&doc->lib.buffers, buff);\n  \n  /* add input */\n  input              = ak_heap_calloc(heap, prim, sizeof(*input));\n  input->offset      = st;\n  input->semantic    = AK_INPUT_NORMAL;\n  input->semanticRaw = \"NORMAL\";\n\n  nextInput = prim->input;\n  while (nextInput->next)\n    nextInput = nextInput->next;\n\n  nextInput->next = input;\n\n  input->accessor = acc;\n\n  prim->inputCount++;\n  prim->indexStride++;\n\n  ak_free(prim->indices);\n  prim->indices = inpIndices;\n\n  (void)ak_data_join(dctx, buff->data, 0, 0);\n  ak_free(dctx);\n}\n\nAK_EXPORT\nvoid\nak_meshGenNormals(AkMesh * __restrict mesh) {\n  AkMeshEditHelper *edith;\n  AkMeshPrimitive  *prim;\n\n  ak_meshBeginEdit(mesh);\n\n  prim  = mesh->primitive;\n  edith = mesh->edith;\n\n  while (prim) {\n    ak_meshPrimGenNormals(prim);\n\n    if (!edith->skipFixIndices)\n      ak_primFixIndices(mesh, prim);\n\n    prim = prim->next;\n  }\n\n  ak_meshEndEdit(mesh);\n}\n"
  },
  {
    "path": "src/mesh/triangulate.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n\nAK_HIDE\nuint32_t\nak_meshTriangulatePoly_noindices(AkPolygon * __restrict poly);\n\nAK_INLINE\nvoid\nak_meshPolyMarkTriangles(AkPolygon * __restrict poly, AkUInt nTrigs) {\n  poly->base.nPolygons = nTrigs;\n  poly->base.type      = AK_PRIMITIVE_TRIANGLES;\n\n  ak_free(poly->vcount);\n  poly->vcount = NULL;\n}\n\n/* not tested yet! */\nAK_HIDE\nuint32_t\nak_meshTriangulatePoly_noindices(AkPolygon * __restrict poly) {\n  AkBuffer     *buff, *newbuff;\n  AkHeap       *heap;\n  AkUInt       *vc_it;\n  AkAccessor   *acc;\n  AkFloat      *it_new, *it_old;\n  AkUInt        nGenTrigs, nTrigs, i, isz;\n  AkBool        isTriList;\n  size_t        st;\n\n  if (!poly->base.pos\n      || !(acc  = poly->base.pos->accessor)\n      || !(buff = acc->buffer))\n    return 0;\n\n  nTrigs    = 0;\n  nGenTrigs = 0;\n  isTriList = true;\n  vc_it     = poly->vcount->items;\n\n  for (i = 0; i < poly->vcount->count; i++) {\n    if (vc_it[i] > 3) {\n      nGenTrigs += vc_it[i] - 2;\n      isTriList = false;\n    } else {\n      nTrigs += 1;\n      if (vc_it[i] != 3)\n        isTriList = false;\n    }\n  }\n\n  if (!nGenTrigs) {\n    if (isTriList && nTrigs > 0)\n      ak_meshPolyMarkTriangles(poly, nTrigs);\n\n    return 0;\n  }\n\n  isz  = sizeof(AkFloat);\n  st   = acc->byteStride;\n  heap = ak_heap_getheap(poly->vcount);\n\n  newbuff         = ak_heap_calloc(heap, poly, sizeof(*newbuff));\n  newbuff->data   = ak_heap_alloc(heap, newbuff, isz * (nTrigs + nGenTrigs) * 3 * st);\n  newbuff->length = isz * nGenTrigs * 3 * st;\n\n  it_old = newbuff->data;\n  it_new = buff->data;\n\n  for (i = 0; i < poly->vcount->count; i++) {\n    uint32_t vc, j;\n\n    vc = vc_it[i];\n    if (vc > 2) {\n      for (j = 1; j < vc - 1; j++) {\n        memcpy(it_new, it_old, st * isz);\n        it_new += st;\n\n        memcpy(it_new, it_old + j * st, 2 * st * isz);\n        it_new += 2 * st;\n      }\n    } else {\n      memcpy(it_new, it_old, vc * st * isz);\n      it_new += vc * st;\n    }\n\n    it_old += vc * st;\n  }\n\n  return nGenTrigs;\n}\n\nAK_EXPORT\nuint32_t\nak_meshTriangulatePoly(AkPolygon * __restrict poly) {\n  AkHeap      *heap;\n  AkUIntArray *newind;\n  AkUInt      *vc_it, *ind_it, *newind_it;\n  AkUInt       nGenTrigs, nTrigs, i, st;\n  AkUInt       isz;\n  AkBool       isTriList;\n\n  if (!poly->vcount)\n    return 0;\n\n  /* we have only primitives for direct draw, no indexes :( */\n  if (!poly->base.indices)\n    return ak_meshTriangulatePoly_noindices(poly);\n\n  nTrigs    = 0;\n  nGenTrigs = 0;\n  isTriList = true;\n  vc_it     = poly->vcount->items;\n\n  for (i = 0; i < poly->vcount->count; i++) {\n    if (vc_it[i] > 3) {\n      nGenTrigs += vc_it[i] - 2;\n      isTriList = false;\n    } else {\n      nTrigs += 1;\n      if (vc_it[i] != 3)\n        isTriList = false;\n    }\n  }\n\n  if (!nGenTrigs) {\n    if (isTriList && nTrigs > 0)\n      ak_meshPolyMarkTriangles(poly, nTrigs);\n\n    return 0;\n  }\n\n  isz    = sizeof(AkUInt);\n  heap   = ak_heap_getheap(poly->vcount);\n  ind_it = poly->base.indices->items;\n  st     = poly->base.indexStride;\n  newind = ak_heap_alloc(heap,\n                         poly,\n                         sizeof(*newind)\n                         + isz * ((nTrigs + nGenTrigs) * 3) * st);\n  newind->count = ((nTrigs + nGenTrigs) * 3) * st;\n  newind_it     = newind->items;\n\n  for (i = 0; i < poly->vcount->count; i++) {\n    uint32_t vc, j;\n\n    vc = vc_it[i];\n    if (vc > 2) {\n      for (j = 1; j < vc - 1; j++) {\n        memcpy(newind_it, ind_it, st * isz);\n        newind_it += st;\n\n        memcpy(newind_it, ind_it + j * st, 2 * st * isz);\n        newind_it += 2 * st;\n      }\n    } else {\n      memcpy(newind_it, ind_it, vc * st * isz);\n      newind_it += vc * st;\n    }\n\n    ind_it += vc * st;\n  }\n\n  ak_free(poly->base.indices);\n  poly->base.indices   = newind;\n  ak_meshPolyMarkTriangles(poly, nGenTrigs + nTrigs);\n\n  return nGenTrigs;\n}\n\nAK_EXPORT\nuint32_t\nak_meshTriangulate(AkMesh * __restrict mesh) {\n  AkMeshPrimitive *prim;\n  uint32_t         extc;\n\n  extc = 0;\n  prim = mesh->primitive;\n  while (prim) {\n    if (prim->type == AK_PRIMITIVE_POLYGONS)\n      extc += ak_meshTriangulatePoly((AkPolygon *)prim);\n\n    prim = prim->next;\n  }\n  return extc;\n}\n"
  },
  {
    "path": "src/miniz/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/miniz/LICENSE",
    "content": "Copyright 2013-2014 RAD Game Tools and Valve Software\r\nCopyright 2010-2014 Rich Geldreich and Tenacious Software LLC\r\n\r\nAll Rights Reserved.\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a copy\r\nof this software and associated documentation files (the \"Software\"), to deal\r\nin the Software without restriction, including without limitation the rights\r\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\ncopies of the Software, and to permit persons to whom the Software is\r\nfurnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included in\r\nall copies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r\nTHE SOFTWARE.\r\n"
  },
  {
    "path": "src/miniz/miniz.c",
    "content": "#include \"miniz.h\"\n/**************************************************************************\n *\n * Copyright 2013-2014 RAD Game Tools and Valve Software\n * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC\n * All Rights Reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n **************************************************************************/\n\n\n\ntypedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1];\ntypedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1];\ntypedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1];\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* ------------------- zlib-style API's */\n\nmz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len)\n{\n    mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16);\n    size_t block_len = buf_len % 5552;\n    if (!ptr)\n        return MZ_ADLER32_INIT;\n    while (buf_len)\n    {\n        for (i = 0; i + 7 < block_len; i += 8, ptr += 8)\n        {\n            s1 += ptr[0], s2 += s1;\n            s1 += ptr[1], s2 += s1;\n            s1 += ptr[2], s2 += s1;\n            s1 += ptr[3], s2 += s1;\n            s1 += ptr[4], s2 += s1;\n            s1 += ptr[5], s2 += s1;\n            s1 += ptr[6], s2 += s1;\n            s1 += ptr[7], s2 += s1;\n        }\n        for (; i < block_len; ++i)\n            s1 += *ptr++, s2 += s1;\n        s1 %= 65521U, s2 %= 65521U;\n        buf_len -= block_len;\n        block_len = 5552;\n    }\n    return (s2 << 16) + s1;\n}\n\n/* Karl Malbrain's compact CRC-32. See \"A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed\": http://www.geocities.com/malbrain/ */\n#if 0\n    mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)\n    {\n        static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,\n                                               0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c };\n        mz_uint32 crcu32 = (mz_uint32)crc;\n        if (!ptr)\n            return MZ_CRC32_INIT;\n        crcu32 = ~crcu32;\n        while (buf_len--)\n        {\n            mz_uint8 b = *ptr++;\n            crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)];\n            crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)];\n        }\n        return ~crcu32;\n    }\n#elif defined(USE_EXTERNAL_MZCRC)\n/* If USE_EXTERNAL_CRC is defined, an external module will export the\n * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version.\n * Depending on the impl, it may be necessary to ~ the input/output crc values.\n */\nmz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len);\n#else\n/* Faster, but larger CPU cache footprint.\n */\nmz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len)\n{\n    static const mz_uint32 s_crc_table[256] =\n        {\n          0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535,\n          0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD,\n          0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D,\n          0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,\n          0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,\n          0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,\n          0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC,\n          0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,\n          0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB,\n          0xB6662D3D, 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,\n          0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB,\n          0x086D3D2D, 0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,\n          0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA,\n          0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 0x4DB26158, 0x3AB551CE,\n          0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,\n          0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,\n          0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409,\n          0xCE61E49F, 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,\n          0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739,\n          0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,\n          0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344, 0x8708A3D2, 0x1E01F268,\n          0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0,\n          0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8,\n          0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,\n          0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,\n          0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703,\n          0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7,\n          0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,\n          0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE,\n          0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,\n          0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 0x88085AE6,\n          0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,\n          0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D,\n          0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5,\n          0x47B2CF7F, 0x30B5FFE9, 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,\n          0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,\n          0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D\n        };\n\n    mz_uint32 crc32 = (mz_uint32)crc ^ 0xFFFFFFFF;\n    const mz_uint8 *pByte_buf = (const mz_uint8 *)ptr;\n\n    while (buf_len >= 4)\n    {\n        crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF];\n        crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[1]) & 0xFF];\n        crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[2]) & 0xFF];\n        crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[3]) & 0xFF];\n        pByte_buf += 4;\n        buf_len -= 4;\n    }\n\n    while (buf_len)\n    {\n        crc32 = (crc32 >> 8) ^ s_crc_table[(crc32 ^ pByte_buf[0]) & 0xFF];\n        ++pByte_buf;\n        --buf_len;\n    }\n\n    return ~crc32;\n}\n#endif\n\nvoid mz_free(void *p)\n{\n    MZ_FREE(p);\n}\n\nMINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size)\n{\n    (void)opaque, (void)items, (void)size;\n    return MZ_MALLOC(items * size);\n}\nMINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address)\n{\n    (void)opaque, (void)address;\n    MZ_FREE(address);\n}\nMINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size)\n{\n    (void)opaque, (void)address, (void)items, (void)size;\n    return MZ_REALLOC(address, items * size);\n}\n\nconst char *mz_version(void)\n{\n    return MZ_VERSION;\n}\n\n#ifndef MINIZ_NO_ZLIB_APIS\n\nint mz_deflateInit(mz_streamp pStream, int level)\n{\n    return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY);\n}\n\nint mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy)\n{\n    tdefl_compressor *pComp;\n    mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy);\n\n    if (!pStream)\n        return MZ_STREAM_ERROR;\n    if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)))\n        return MZ_PARAM_ERROR;\n\n    pStream->data_type = 0;\n    pStream->adler = MZ_ADLER32_INIT;\n    pStream->msg = NULL;\n    pStream->reserved = 0;\n    pStream->total_in = 0;\n    pStream->total_out = 0;\n    if (!pStream->zalloc)\n        pStream->zalloc = miniz_def_alloc_func;\n    if (!pStream->zfree)\n        pStream->zfree = miniz_def_free_func;\n\n    pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor));\n    if (!pComp)\n        return MZ_MEM_ERROR;\n\n    pStream->state = (struct mz_internal_state *)pComp;\n\n    if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY)\n    {\n        mz_deflateEnd(pStream);\n        return MZ_PARAM_ERROR;\n    }\n\n    return MZ_OK;\n}\n\nint mz_deflateReset(mz_streamp pStream)\n{\n    if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree))\n        return MZ_STREAM_ERROR;\n    pStream->total_in = pStream->total_out = 0;\n    tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, ((tdefl_compressor *)pStream->state)->m_flags);\n    return MZ_OK;\n}\n\nint mz_deflate(mz_streamp pStream, int flush)\n{\n    size_t in_bytes, out_bytes;\n    mz_ulong orig_total_in, orig_total_out;\n    int mz_status = MZ_OK;\n\n    if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out))\n        return MZ_STREAM_ERROR;\n    if (!pStream->avail_out)\n        return MZ_BUF_ERROR;\n\n    if (flush == MZ_PARTIAL_FLUSH)\n        flush = MZ_SYNC_FLUSH;\n\n    if (((tdefl_compressor *)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE)\n        return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR;\n\n    orig_total_in = pStream->total_in;\n    orig_total_out = pStream->total_out;\n    for (;;)\n    {\n        tdefl_status defl_status;\n        in_bytes = pStream->avail_in;\n        out_bytes = pStream->avail_out;\n\n        defl_status = tdefl_compress((tdefl_compressor *)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush);\n        pStream->next_in += (mz_uint)in_bytes;\n        pStream->avail_in -= (mz_uint)in_bytes;\n        pStream->total_in += (mz_uint)in_bytes;\n        pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state);\n\n        pStream->next_out += (mz_uint)out_bytes;\n        pStream->avail_out -= (mz_uint)out_bytes;\n        pStream->total_out += (mz_uint)out_bytes;\n\n        if (defl_status < 0)\n        {\n            mz_status = MZ_STREAM_ERROR;\n            break;\n        }\n        else if (defl_status == TDEFL_STATUS_DONE)\n        {\n            mz_status = MZ_STREAM_END;\n            break;\n        }\n        else if (!pStream->avail_out)\n            break;\n        else if ((!pStream->avail_in) && (flush != MZ_FINISH))\n        {\n            if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out))\n                break;\n            return MZ_BUF_ERROR; /* Can't make forward progress without some input.\n */\n        }\n    }\n    return mz_status;\n}\n\nint mz_deflateEnd(mz_streamp pStream)\n{\n    if (!pStream)\n        return MZ_STREAM_ERROR;\n    if (pStream->state)\n    {\n        pStream->zfree(pStream->opaque, pStream->state);\n        pStream->state = NULL;\n    }\n    return MZ_OK;\n}\n\nmz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len)\n{\n    (void)pStream;\n    /* This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) */\n    return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5);\n}\n\nint mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level)\n{\n    int status;\n    mz_stream stream;\n    memset(&stream, 0, sizeof(stream));\n\n    /* In case mz_ulong is 64-bits (argh I hate longs). */\n    if ((source_len | *pDest_len) > 0xFFFFFFFFU)\n        return MZ_PARAM_ERROR;\n\n    stream.next_in = pSource;\n    stream.avail_in = (mz_uint32)source_len;\n    stream.next_out = pDest;\n    stream.avail_out = (mz_uint32)*pDest_len;\n\n    status = mz_deflateInit(&stream, level);\n    if (status != MZ_OK)\n        return status;\n\n    status = mz_deflate(&stream, MZ_FINISH);\n    if (status != MZ_STREAM_END)\n    {\n        mz_deflateEnd(&stream);\n        return (status == MZ_OK) ? MZ_BUF_ERROR : status;\n    }\n\n    *pDest_len = stream.total_out;\n    return mz_deflateEnd(&stream);\n}\n\nint mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)\n{\n    return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION);\n}\n\nmz_ulong mz_compressBound(mz_ulong source_len)\n{\n    return mz_deflateBound(NULL, source_len);\n}\n\ntypedef struct\n{\n    tinfl_decompressor m_decomp;\n    mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed;\n    int m_window_bits;\n    mz_uint8 m_dict[TINFL_LZ_DICT_SIZE];\n    tinfl_status m_last_status;\n} inflate_state;\n\nint mz_inflateInit2(mz_streamp pStream, int window_bits)\n{\n    inflate_state *pDecomp;\n    if (!pStream)\n        return MZ_STREAM_ERROR;\n    if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))\n        return MZ_PARAM_ERROR;\n\n    pStream->data_type = 0;\n    pStream->adler = 0;\n    pStream->msg = NULL;\n    pStream->total_in = 0;\n    pStream->total_out = 0;\n    pStream->reserved = 0;\n    if (!pStream->zalloc)\n        pStream->zalloc = miniz_def_alloc_func;\n    if (!pStream->zfree)\n        pStream->zfree = miniz_def_free_func;\n\n    pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state));\n    if (!pDecomp)\n        return MZ_MEM_ERROR;\n\n    pStream->state = (struct mz_internal_state *)pDecomp;\n\n    tinfl_init(&pDecomp->m_decomp);\n    pDecomp->m_dict_ofs = 0;\n    pDecomp->m_dict_avail = 0;\n    pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;\n    pDecomp->m_first_call = 1;\n    pDecomp->m_has_flushed = 0;\n    pDecomp->m_window_bits = window_bits;\n\n    return MZ_OK;\n}\n\nint mz_inflateInit(mz_streamp pStream)\n{\n    return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS);\n}\n\nint mz_inflateReset(mz_streamp pStream)\n{\n    inflate_state *pDecomp;\n    if (!pStream)\n        return MZ_STREAM_ERROR;\n\n    pStream->data_type = 0;\n    pStream->adler = 0;\n    pStream->msg = NULL;\n    pStream->total_in = 0;\n    pStream->total_out = 0;\n    pStream->reserved = 0;\n\n    pDecomp = (inflate_state *)pStream->state;\n\n    tinfl_init(&pDecomp->m_decomp);\n    pDecomp->m_dict_ofs = 0;\n    pDecomp->m_dict_avail = 0;\n    pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT;\n    pDecomp->m_first_call = 1;\n    pDecomp->m_has_flushed = 0;\n    /* pDecomp->m_window_bits = window_bits */;\n\n    return MZ_OK;\n}\n\nint mz_inflate(mz_streamp pStream, int flush)\n{\n    inflate_state *pState;\n    mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32;\n    size_t in_bytes, out_bytes, orig_avail_in;\n    tinfl_status status;\n\n    if ((!pStream) || (!pStream->state))\n        return MZ_STREAM_ERROR;\n    if (flush == MZ_PARTIAL_FLUSH)\n        flush = MZ_SYNC_FLUSH;\n    if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH))\n        return MZ_STREAM_ERROR;\n\n    pState = (inflate_state *)pStream->state;\n    if (pState->m_window_bits > 0)\n        decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;\n    orig_avail_in = pStream->avail_in;\n\n    first_call = pState->m_first_call;\n    pState->m_first_call = 0;\n    if (pState->m_last_status < 0)\n        return MZ_DATA_ERROR;\n\n    if (pState->m_has_flushed && (flush != MZ_FINISH))\n        return MZ_STREAM_ERROR;\n    pState->m_has_flushed |= (flush == MZ_FINISH);\n\n    if ((flush == MZ_FINISH) && (first_call))\n    {\n        /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */\n        decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF;\n        in_bytes = pStream->avail_in;\n        out_bytes = pStream->avail_out;\n        status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags);\n        pState->m_last_status = status;\n        pStream->next_in += (mz_uint)in_bytes;\n        pStream->avail_in -= (mz_uint)in_bytes;\n        pStream->total_in += (mz_uint)in_bytes;\n        pStream->adler = tinfl_get_adler32(&pState->m_decomp);\n        pStream->next_out += (mz_uint)out_bytes;\n        pStream->avail_out -= (mz_uint)out_bytes;\n        pStream->total_out += (mz_uint)out_bytes;\n\n        if (status < 0)\n            return MZ_DATA_ERROR;\n        else if (status != TINFL_STATUS_DONE)\n        {\n            pState->m_last_status = TINFL_STATUS_FAILED;\n            return MZ_BUF_ERROR;\n        }\n        return MZ_STREAM_END;\n    }\n    /* flush != MZ_FINISH then we must assume there's more input. */\n    if (flush != MZ_FINISH)\n        decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT;\n\n    if (pState->m_dict_avail)\n    {\n        n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);\n        memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);\n        pStream->next_out += n;\n        pStream->avail_out -= n;\n        pStream->total_out += n;\n        pState->m_dict_avail -= n;\n        pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);\n        return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;\n    }\n\n    for (;;)\n    {\n        in_bytes = pStream->avail_in;\n        out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs;\n\n        status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags);\n        pState->m_last_status = status;\n\n        pStream->next_in += (mz_uint)in_bytes;\n        pStream->avail_in -= (mz_uint)in_bytes;\n        pStream->total_in += (mz_uint)in_bytes;\n        pStream->adler = tinfl_get_adler32(&pState->m_decomp);\n\n        pState->m_dict_avail = (mz_uint)out_bytes;\n\n        n = MZ_MIN(pState->m_dict_avail, pStream->avail_out);\n        memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n);\n        pStream->next_out += n;\n        pStream->avail_out -= n;\n        pStream->total_out += n;\n        pState->m_dict_avail -= n;\n        pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1);\n\n        if (status < 0)\n            return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */\n        else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in))\n            return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */\n        else if (flush == MZ_FINISH)\n        {\n            /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */\n            if (status == TINFL_STATUS_DONE)\n                return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END;\n            /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */\n            else if (!pStream->avail_out)\n                return MZ_BUF_ERROR;\n        }\n        else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail))\n            break;\n    }\n\n    return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK;\n}\n\nint mz_inflateEnd(mz_streamp pStream)\n{\n    if (!pStream)\n        return MZ_STREAM_ERROR;\n    if (pStream->state)\n    {\n        pStream->zfree(pStream->opaque, pStream->state);\n        pStream->state = NULL;\n    }\n    return MZ_OK;\n}\nint mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len)\n{\n    mz_stream stream;\n    int status;\n    memset(&stream, 0, sizeof(stream));\n\n    /* In case mz_ulong is 64-bits (argh I hate longs). */\n    if ((*pSource_len | *pDest_len) > 0xFFFFFFFFU)\n        return MZ_PARAM_ERROR;\n\n    stream.next_in = pSource;\n    stream.avail_in = (mz_uint32)*pSource_len;\n    stream.next_out = pDest;\n    stream.avail_out = (mz_uint32)*pDest_len;\n\n    status = mz_inflateInit(&stream);\n    if (status != MZ_OK)\n        return status;\n\n    status = mz_inflate(&stream, MZ_FINISH);\n    *pSource_len = *pSource_len - stream.avail_in;\n    if (status != MZ_STREAM_END)\n    {\n        mz_inflateEnd(&stream);\n        return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status;\n    }\n    *pDest_len = stream.total_out;\n\n    return mz_inflateEnd(&stream);\n}\n\nint mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len)\n{\n    return mz_uncompress2(pDest, pDest_len, pSource, &source_len);\n}\n\nconst char *mz_error(int err)\n{\n    static struct\n    {\n        int m_err;\n        const char *m_pDesc;\n    } s_error_descs[] =\n        {\n          { MZ_OK, \"\" }, { MZ_STREAM_END, \"stream end\" }, { MZ_NEED_DICT, \"need dictionary\" }, { MZ_ERRNO, \"file error\" }, { MZ_STREAM_ERROR, \"stream error\" }, { MZ_DATA_ERROR, \"data error\" }, { MZ_MEM_ERROR, \"out of memory\" }, { MZ_BUF_ERROR, \"buf error\" }, { MZ_VERSION_ERROR, \"version error\" }, { MZ_PARAM_ERROR, \"parameter error\" }\n        };\n    mz_uint i;\n    for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i)\n        if (s_error_descs[i].m_err == err)\n            return s_error_descs[i].m_pDesc;\n    return NULL;\n}\n\n#endif /*MINIZ_NO_ZLIB_APIS */\n\n#ifdef __cplusplus\n}\n#endif\n\n/*\n  This is free and unencumbered software released into the public domain.\n\n  Anyone is free to copy, modify, publish, use, compile, sell, or\n  distribute this software, either in source code form or as a compiled\n  binary, for any purpose, commercial or non-commercial, and by any\n  means.\n\n  In jurisdictions that recognize copyright laws, the author or authors\n  of this software dedicate any and all copyright interest in the\n  software to the public domain. We make this dedication for the benefit\n  of the public at large and to the detriment of our heirs and\n  successors. We intend this dedication to be an overt act of\n  relinquishment in perpetuity of all present and future rights to this\n  software under copyright law.\n\n  THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\n  OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\n  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n  OTHER DEALINGS IN THE SOFTWARE.\n\n  For more information, please refer to <http://unlicense.org/>\n*/\n/**************************************************************************\n *\n * Copyright 2013-2014 RAD Game Tools and Valve Software\n * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC\n * All Rights Reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n **************************************************************************/\n\n\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* ------------------- Low-level Compression (independent from all decompression API's) */\n\n/* Purposely making these tables static for faster init and thread safety. */\nstatic const mz_uint16 s_tdefl_len_sym[256] =\n    {\n      257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, 272, 272,\n      273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, 276, 276, 276, 276,\n      277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,\n      279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280, 280,\n      281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281,\n      282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282,\n      283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283,\n      284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 285\n    };\n\nstatic const mz_uint8 s_tdefl_len_extra[256] =\n    {\n      0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,\n      4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,\n      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,\n      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0\n    };\n\nstatic const mz_uint8 s_tdefl_small_dist_sym[512] =\n    {\n      0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11,\n      11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13,\n      13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,\n      14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,\n      14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,\n      15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,\n      16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,\n      16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,\n      16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,\n      17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,\n      17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,\n      17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17\n    };\n\nstatic const mz_uint8 s_tdefl_small_dist_extra[512] =\n    {\n      0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5,\n      5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,\n      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,\n      6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,\n      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,\n      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,\n      7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,\n      7, 7, 7, 7, 7, 7, 7, 7\n    };\n\nstatic const mz_uint8 s_tdefl_large_dist_sym[128] =\n    {\n      0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,\n      26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,\n      28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29\n    };\n\nstatic const mz_uint8 s_tdefl_large_dist_extra[128] =\n    {\n      0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,\n      12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,\n      13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13\n    };\n\n/* Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. */\ntypedef struct\n{\n    mz_uint16 m_key, m_sym_index;\n} tdefl_sym_freq;\nstatic tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *pSyms0, tdefl_sym_freq *pSyms1)\n{\n    mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2];\n    tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1;\n    MZ_CLEAR_OBJ(hist);\n    for (i = 0; i < num_syms; i++)\n    {\n        mz_uint freq = pSyms0[i].m_key;\n        hist[freq & 0xFF]++;\n        hist[256 + ((freq >> 8) & 0xFF)]++;\n    }\n    while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256]))\n        total_passes--;\n    for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8)\n    {\n        const mz_uint32 *pHist = &hist[pass << 8];\n        mz_uint offsets[256], cur_ofs = 0;\n        for (i = 0; i < 256; i++)\n        {\n            offsets[i] = cur_ofs;\n            cur_ofs += pHist[i];\n        }\n        for (i = 0; i < num_syms; i++)\n            pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i];\n        {\n            tdefl_sym_freq *t = pCur_syms;\n            pCur_syms = pNew_syms;\n            pNew_syms = t;\n        }\n    }\n    return pCur_syms;\n}\n\n/* tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. */\nstatic void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n)\n{\n    int root, leaf, next, avbl, used, dpth;\n    if (n == 0)\n        return;\n    else if (n == 1)\n    {\n        A[0].m_key = 1;\n        return;\n    }\n    A[0].m_key += A[1].m_key;\n    root = 0;\n    leaf = 2;\n    for (next = 1; next < n - 1; next++)\n    {\n        if (leaf >= n || A[root].m_key < A[leaf].m_key)\n        {\n            A[next].m_key = A[root].m_key;\n            A[root++].m_key = (mz_uint16)next;\n        }\n        else\n            A[next].m_key = A[leaf++].m_key;\n        if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key))\n        {\n            A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key);\n            A[root++].m_key = (mz_uint16)next;\n        }\n        else\n            A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key);\n    }\n    A[n - 2].m_key = 0;\n    for (next = n - 3; next >= 0; next--)\n        A[next].m_key = A[A[next].m_key].m_key + 1;\n    avbl = 1;\n    used = dpth = 0;\n    root = n - 2;\n    next = n - 1;\n    while (avbl > 0)\n    {\n        while (root >= 0 && (int)A[root].m_key == dpth)\n        {\n            used++;\n            root--;\n        }\n        while (avbl > used)\n        {\n            A[next--].m_key = (mz_uint16)(dpth);\n            avbl--;\n        }\n        avbl = 2 * used;\n        dpth++;\n        used = 0;\n    }\n}\n\n/* Limits canonical Huffman code table's max code size. */\nenum\n{\n    TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32\n};\nstatic void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size)\n{\n    int i;\n    mz_uint32 total = 0;\n    if (code_list_len <= 1)\n        return;\n    for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++)\n        pNum_codes[max_code_size] += pNum_codes[i];\n    for (i = max_code_size; i > 0; i--)\n        total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i));\n    while (total != (1UL << max_code_size))\n    {\n        pNum_codes[max_code_size]--;\n        for (i = max_code_size - 1; i > 0; i--)\n            if (pNum_codes[i])\n            {\n                pNum_codes[i]--;\n                pNum_codes[i + 1] += 2;\n                break;\n            }\n        total--;\n    }\n}\n\nstatic void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table)\n{\n    int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE];\n    mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1];\n    MZ_CLEAR_OBJ(num_codes);\n    if (static_table)\n    {\n        for (i = 0; i < table_len; i++)\n            num_codes[d->m_huff_code_sizes[table_num][i]]++;\n    }\n    else\n    {\n        tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms;\n        int num_used_syms = 0;\n        const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0];\n        for (i = 0; i < table_len; i++)\n            if (pSym_count[i])\n            {\n                syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i];\n                syms0[num_used_syms++].m_sym_index = (mz_uint16)i;\n            }\n\n        pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1);\n        tdefl_calculate_minimum_redundancy(pSyms, num_used_syms);\n\n        for (i = 0; i < num_used_syms; i++)\n            num_codes[pSyms[i].m_key]++;\n\n        tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit);\n\n        MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]);\n        MZ_CLEAR_OBJ(d->m_huff_codes[table_num]);\n        for (i = 1, j = num_used_syms; i <= code_size_limit; i++)\n            for (l = num_codes[i]; l > 0; l--)\n                d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i);\n    }\n\n    next_code[1] = 0;\n    for (j = 0, i = 2; i <= code_size_limit; i++)\n        next_code[i] = j = ((j + num_codes[i - 1]) << 1);\n\n    for (i = 0; i < table_len; i++)\n    {\n        mz_uint rev_code = 0, code, code_size;\n        if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0)\n            continue;\n        code = next_code[code_size]++;\n        for (l = code_size; l > 0; l--, code >>= 1)\n            rev_code = (rev_code << 1) | (code & 1);\n        d->m_huff_codes[table_num][i] = (mz_uint16)rev_code;\n    }\n}\n\n#define TDEFL_PUT_BITS(b, l)                                       \\\n    do                                                             \\\n    {                                                              \\\n        mz_uint bits = b;                                          \\\n        mz_uint len = l;                                           \\\n        MZ_ASSERT(bits <= ((1U << len) - 1U));                     \\\n        d->m_bit_buffer |= (bits << d->m_bits_in);                 \\\n        d->m_bits_in += len;                                       \\\n        while (d->m_bits_in >= 8)                                  \\\n        {                                                          \\\n            if (d->m_pOutput_buf < d->m_pOutput_buf_end)           \\\n                *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \\\n            d->m_bit_buffer >>= 8;                                 \\\n            d->m_bits_in -= 8;                                     \\\n        }                                                          \\\n    }                                                              \\\n    MZ_MACRO_END\n\n#define TDEFL_RLE_PREV_CODE_SIZE()                                                                                       \\\n    {                                                                                                                    \\\n        if (rle_repeat_count)                                                                                            \\\n        {                                                                                                                \\\n            if (rle_repeat_count < 3)                                                                                    \\\n            {                                                                                                            \\\n                d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \\\n                while (rle_repeat_count--)                                                                               \\\n                    packed_code_sizes[num_packed_code_sizes++] = prev_code_size;                                         \\\n            }                                                                                                            \\\n            else                                                                                                         \\\n            {                                                                                                            \\\n                d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1);                                        \\\n                packed_code_sizes[num_packed_code_sizes++] = 16;                                                         \\\n                packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3);                           \\\n            }                                                                                                            \\\n            rle_repeat_count = 0;                                                                                        \\\n        }                                                                                                                \\\n    }\n\n#define TDEFL_RLE_ZERO_CODE_SIZE()                                                         \\\n    {                                                                                      \\\n        if (rle_z_count)                                                                   \\\n        {                                                                                  \\\n            if (rle_z_count < 3)                                                           \\\n            {                                                                              \\\n                d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count);  \\\n                while (rle_z_count--)                                                      \\\n                    packed_code_sizes[num_packed_code_sizes++] = 0;                        \\\n            }                                                                              \\\n            else if (rle_z_count <= 10)                                                    \\\n            {                                                                              \\\n                d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1);          \\\n                packed_code_sizes[num_packed_code_sizes++] = 17;                           \\\n                packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3);  \\\n            }                                                                              \\\n            else                                                                           \\\n            {                                                                              \\\n                d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1);          \\\n                packed_code_sizes[num_packed_code_sizes++] = 18;                           \\\n                packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \\\n            }                                                                              \\\n            rle_z_count = 0;                                                               \\\n        }                                                                                  \\\n    }\n\nstatic mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };\n\nstatic void tdefl_start_dynamic_block(tdefl_compressor *d)\n{\n    int num_lit_codes, num_dist_codes, num_bit_lengths;\n    mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index;\n    mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF;\n\n    d->m_huff_count[0][256] = 1;\n\n    tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE);\n    tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE);\n\n    for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--)\n        if (d->m_huff_code_sizes[0][num_lit_codes - 1])\n            break;\n    for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--)\n        if (d->m_huff_code_sizes[1][num_dist_codes - 1])\n            break;\n\n    memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes);\n    memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes);\n    total_code_sizes_to_pack = num_lit_codes + num_dist_codes;\n    num_packed_code_sizes = 0;\n    rle_z_count = 0;\n    rle_repeat_count = 0;\n\n    memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2);\n    for (i = 0; i < total_code_sizes_to_pack; i++)\n    {\n        mz_uint8 code_size = code_sizes_to_pack[i];\n        if (!code_size)\n        {\n            TDEFL_RLE_PREV_CODE_SIZE();\n            if (++rle_z_count == 138)\n            {\n                TDEFL_RLE_ZERO_CODE_SIZE();\n            }\n        }\n        else\n        {\n            TDEFL_RLE_ZERO_CODE_SIZE();\n            if (code_size != prev_code_size)\n            {\n                TDEFL_RLE_PREV_CODE_SIZE();\n                d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1);\n                packed_code_sizes[num_packed_code_sizes++] = code_size;\n            }\n            else if (++rle_repeat_count == 6)\n            {\n                TDEFL_RLE_PREV_CODE_SIZE();\n            }\n        }\n        prev_code_size = code_size;\n    }\n    if (rle_repeat_count)\n    {\n        TDEFL_RLE_PREV_CODE_SIZE();\n    }\n    else\n    {\n        TDEFL_RLE_ZERO_CODE_SIZE();\n    }\n\n    tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE);\n\n    TDEFL_PUT_BITS(2, 2);\n\n    TDEFL_PUT_BITS(num_lit_codes - 257, 5);\n    TDEFL_PUT_BITS(num_dist_codes - 1, 5);\n\n    for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--)\n        if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]])\n            break;\n    num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1));\n    TDEFL_PUT_BITS(num_bit_lengths - 4, 4);\n    for (i = 0; (int)i < num_bit_lengths; i++)\n        TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3);\n\n    for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes;)\n    {\n        mz_uint code = packed_code_sizes[packed_code_sizes_index++];\n        MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2);\n        TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]);\n        if (code >= 16)\n            TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], \"\\02\\03\\07\"[code - 16]);\n    }\n}\n\nstatic void tdefl_start_static_block(tdefl_compressor *d)\n{\n    mz_uint i;\n    mz_uint8 *p = &d->m_huff_code_sizes[0][0];\n\n    for (i = 0; i <= 143; ++i)\n        *p++ = 8;\n    for (; i <= 255; ++i)\n        *p++ = 9;\n    for (; i <= 279; ++i)\n        *p++ = 7;\n    for (; i <= 287; ++i)\n        *p++ = 8;\n\n    memset(d->m_huff_code_sizes[1], 5, 32);\n\n    tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE);\n    tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE);\n\n    TDEFL_PUT_BITS(1, 2);\n}\n\nstatic const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };\n\n#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS\nstatic mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)\n{\n    mz_uint flags;\n    mz_uint8 *pLZ_codes;\n    mz_uint8 *pOutput_buf = d->m_pOutput_buf;\n    mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf;\n    mz_uint64 bit_buffer = d->m_bit_buffer;\n    mz_uint bits_in = d->m_bits_in;\n\n#define TDEFL_PUT_BITS_FAST(b, l)                    \\\n    {                                                \\\n        bit_buffer |= (((mz_uint64)(b)) << bits_in); \\\n        bits_in += (l);                              \\\n    }\n\n    flags = 1;\n    for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1)\n    {\n        if (flags == 1)\n            flags = *pLZ_codes++ | 0x100;\n\n        if (flags & 1)\n        {\n            mz_uint s0, s1, n0, n1, sym, num_extra_bits;\n            mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1);\n            pLZ_codes += 3;\n\n            MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);\n            TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);\n            TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);\n\n            /* This sequence coaxes MSVC into using cmov's vs. jmp's. */\n            s0 = s_tdefl_small_dist_sym[match_dist & 511];\n            n0 = s_tdefl_small_dist_extra[match_dist & 511];\n            s1 = s_tdefl_large_dist_sym[match_dist >> 8];\n            n1 = s_tdefl_large_dist_extra[match_dist >> 8];\n            sym = (match_dist < 512) ? s0 : s1;\n            num_extra_bits = (match_dist < 512) ? n0 : n1;\n\n            MZ_ASSERT(d->m_huff_code_sizes[1][sym]);\n            TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);\n            TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);\n        }\n        else\n        {\n            mz_uint lit = *pLZ_codes++;\n            MZ_ASSERT(d->m_huff_code_sizes[0][lit]);\n            TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);\n\n            if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))\n            {\n                flags >>= 1;\n                lit = *pLZ_codes++;\n                MZ_ASSERT(d->m_huff_code_sizes[0][lit]);\n                TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);\n\n                if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end))\n                {\n                    flags >>= 1;\n                    lit = *pLZ_codes++;\n                    MZ_ASSERT(d->m_huff_code_sizes[0][lit]);\n                    TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);\n                }\n            }\n        }\n\n        if (pOutput_buf >= d->m_pOutput_buf_end)\n            return MZ_FALSE;\n\n        *(mz_uint64 *)pOutput_buf = bit_buffer;\n        pOutput_buf += (bits_in >> 3);\n        bit_buffer >>= (bits_in & ~7);\n        bits_in &= 7;\n    }\n\n#undef TDEFL_PUT_BITS_FAST\n\n    d->m_pOutput_buf = pOutput_buf;\n    d->m_bits_in = 0;\n    d->m_bit_buffer = 0;\n\n    while (bits_in)\n    {\n        mz_uint32 n = MZ_MIN(bits_in, 16);\n        TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n);\n        bit_buffer >>= n;\n        bits_in -= n;\n    }\n\n    TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);\n\n    return (d->m_pOutput_buf < d->m_pOutput_buf_end);\n}\n#else\nstatic mz_bool tdefl_compress_lz_codes(tdefl_compressor *d)\n{\n    mz_uint flags;\n    mz_uint8 *pLZ_codes;\n\n    flags = 1;\n    for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1)\n    {\n        if (flags == 1)\n            flags = *pLZ_codes++ | 0x100;\n        if (flags & 1)\n        {\n            mz_uint sym, num_extra_bits;\n            mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8));\n            pLZ_codes += 3;\n\n            MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);\n            TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]);\n            TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]);\n\n            if (match_dist < 512)\n            {\n                sym = s_tdefl_small_dist_sym[match_dist];\n                num_extra_bits = s_tdefl_small_dist_extra[match_dist];\n            }\n            else\n            {\n                sym = s_tdefl_large_dist_sym[match_dist >> 8];\n                num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8];\n            }\n            MZ_ASSERT(d->m_huff_code_sizes[1][sym]);\n            TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]);\n            TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits);\n        }\n        else\n        {\n            mz_uint lit = *pLZ_codes++;\n            MZ_ASSERT(d->m_huff_code_sizes[0][lit]);\n            TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]);\n        }\n    }\n\n    TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]);\n\n    return (d->m_pOutput_buf < d->m_pOutput_buf_end);\n}\n#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS */\n\nstatic mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block)\n{\n    if (static_block)\n        tdefl_start_static_block(d);\n    else\n        tdefl_start_dynamic_block(d);\n    return tdefl_compress_lz_codes(d);\n}\n\nstatic int tdefl_flush_block(tdefl_compressor *d, int flush)\n{\n    mz_uint saved_bit_buf, saved_bits_in;\n    mz_uint8 *pSaved_output_buf;\n    mz_bool comp_block_succeeded = MZ_FALSE;\n    int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size;\n    mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf;\n\n    d->m_pOutput_buf = pOutput_buf_start;\n    d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16;\n\n    MZ_ASSERT(!d->m_output_flush_remaining);\n    d->m_output_flush_ofs = 0;\n    d->m_output_flush_remaining = 0;\n\n    *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left);\n    d->m_pLZ_code_buf -= (d->m_num_flags_left == 8);\n\n    if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index))\n    {\n        TDEFL_PUT_BITS(0x78, 8);\n        TDEFL_PUT_BITS(0x01, 8);\n    }\n\n    TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1);\n\n    pSaved_output_buf = d->m_pOutput_buf;\n    saved_bit_buf = d->m_bit_buffer;\n    saved_bits_in = d->m_bits_in;\n\n    if (!use_raw_block)\n        comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48));\n\n    /* If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. */\n    if (((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) &&\n        ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size))\n    {\n        mz_uint i;\n        d->m_pOutput_buf = pSaved_output_buf;\n        d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;\n        TDEFL_PUT_BITS(0, 2);\n        if (d->m_bits_in)\n        {\n            TDEFL_PUT_BITS(0, 8 - d->m_bits_in);\n        }\n        for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF)\n        {\n            TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16);\n        }\n        for (i = 0; i < d->m_total_lz_bytes; ++i)\n        {\n            TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8);\n        }\n    }\n    /* Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. */\n    else if (!comp_block_succeeded)\n    {\n        d->m_pOutput_buf = pSaved_output_buf;\n        d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in;\n        tdefl_compress_block(d, MZ_TRUE);\n    }\n\n    if (flush)\n    {\n        if (flush == TDEFL_FINISH)\n        {\n            if (d->m_bits_in)\n            {\n                TDEFL_PUT_BITS(0, 8 - d->m_bits_in);\n            }\n            if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER)\n            {\n                mz_uint i, a = d->m_adler32;\n                for (i = 0; i < 4; i++)\n                {\n                    TDEFL_PUT_BITS((a >> 24) & 0xFF, 8);\n                    a <<= 8;\n                }\n            }\n        }\n        else\n        {\n            mz_uint i, z = 0;\n            TDEFL_PUT_BITS(0, 3);\n            if (d->m_bits_in)\n            {\n                TDEFL_PUT_BITS(0, 8 - d->m_bits_in);\n            }\n            for (i = 2; i; --i, z ^= 0xFFFF)\n            {\n                TDEFL_PUT_BITS(z & 0xFFFF, 16);\n            }\n        }\n    }\n\n    MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end);\n\n    memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);\n    memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);\n\n    d->m_pLZ_code_buf = d->m_lz_code_buf + 1;\n    d->m_pLZ_flags = d->m_lz_code_buf;\n    d->m_num_flags_left = 8;\n    d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes;\n    d->m_total_lz_bytes = 0;\n    d->m_block_index++;\n\n    if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0)\n    {\n        if (d->m_pPut_buf_func)\n        {\n            *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;\n            if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user))\n                return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED);\n        }\n        else if (pOutput_buf_start == d->m_output_buf)\n        {\n            int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs));\n            memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy);\n            d->m_out_buf_ofs += bytes_to_copy;\n            if ((n -= bytes_to_copy) != 0)\n            {\n                d->m_output_flush_ofs = bytes_to_copy;\n                d->m_output_flush_remaining = n;\n            }\n        }\n        else\n        {\n            d->m_out_buf_ofs += n;\n        }\n    }\n\n    return d->m_output_flush_remaining;\n}\n\n#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES\n#ifdef MINIZ_UNALIGNED_USE_MEMCPY\nstatic mz_uint16 TDEFL_READ_UNALIGNED_WORD(const mz_uint8* p)\n{\n\tmz_uint16 ret;\n\tmemcpy(&ret, p, sizeof(mz_uint16));\n\treturn ret;\n}\nstatic mz_uint16 TDEFL_READ_UNALIGNED_WORD2(const mz_uint16* p)\n{\n\tmz_uint16 ret;\n\tmemcpy(&ret, p, sizeof(mz_uint16));\n\treturn ret;\n}\n#else\n#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16 *)(p)\n#define TDEFL_READ_UNALIGNED_WORD2(p) *(const mz_uint16 *)(p)\n#endif\nstatic MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)\n{\n    mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;\n    mz_uint num_probes_left = d->m_max_probes[match_len >= 32];\n    const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q;\n    mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD2(s);\n    MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN);\n    if (max_match_len <= match_len)\n        return;\n    for (;;)\n    {\n        for (;;)\n        {\n            if (--num_probes_left == 0)\n                return;\n#define TDEFL_PROBE                                                                             \\\n    next_probe_pos = d->m_next[probe_pos];                                                      \\\n    if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \\\n        return;                                                                                 \\\n    probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK;                                       \\\n    if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01)                \\\n        break;\n            TDEFL_PROBE;\n            TDEFL_PROBE;\n            TDEFL_PROBE;\n        }\n        if (!dist)\n            break;\n        q = (const mz_uint16 *)(d->m_dict + probe_pos);\n        if (TDEFL_READ_UNALIGNED_WORD2(q) != s01)\n            continue;\n        p = s;\n        probe_len = 32;\n        do\n        {\n        } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) &&\n                 (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0));\n        if (!probe_len)\n        {\n            *pMatch_dist = dist;\n            *pMatch_len = MZ_MIN(max_match_len, (mz_uint)TDEFL_MAX_MATCH_LEN);\n            break;\n        }\n        else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q)) > match_len)\n        {\n            *pMatch_dist = dist;\n            if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len)\n                break;\n            c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]);\n        }\n    }\n}\n#else\nstatic MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len)\n{\n    mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len;\n    mz_uint num_probes_left = d->m_max_probes[match_len >= 32];\n    const mz_uint8 *s = d->m_dict + pos, *p, *q;\n    mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1];\n    MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN);\n    if (max_match_len <= match_len)\n        return;\n    for (;;)\n    {\n        for (;;)\n        {\n            if (--num_probes_left == 0)\n                return;\n#define TDEFL_PROBE                                                                               \\\n    next_probe_pos = d->m_next[probe_pos];                                                        \\\n    if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist))   \\\n        return;                                                                                   \\\n    probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK;                                         \\\n    if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) \\\n        break;\n            TDEFL_PROBE;\n            TDEFL_PROBE;\n            TDEFL_PROBE;\n        }\n        if (!dist)\n            break;\n        p = s;\n        q = d->m_dict + probe_pos;\n        for (probe_len = 0; probe_len < max_match_len; probe_len++)\n            if (*p++ != *q++)\n                break;\n        if (probe_len > match_len)\n        {\n            *pMatch_dist = dist;\n            if ((*pMatch_len = match_len = probe_len) == max_match_len)\n                return;\n            c0 = d->m_dict[pos + match_len];\n            c1 = d->m_dict[pos + match_len - 1];\n        }\n    }\n}\n#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES */\n\n#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN\n#ifdef MINIZ_UNALIGNED_USE_MEMCPY\nstatic mz_uint32 TDEFL_READ_UNALIGNED_WORD32(const mz_uint8* p)\n{\n\tmz_uint32 ret;\n\tmemcpy(&ret, p, sizeof(mz_uint32));\n\treturn ret;\n}\n#else\n#define TDEFL_READ_UNALIGNED_WORD32(p) *(const mz_uint32 *)(p)\n#endif\nstatic mz_bool tdefl_compress_fast(tdefl_compressor *d)\n{\n    /* Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. */\n    mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left;\n    mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags;\n    mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;\n\n    while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size)))\n    {\n        const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096;\n        mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;\n        mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size);\n        d->m_src_buf_left -= num_bytes_to_process;\n        lookahead_size += num_bytes_to_process;\n\n        while (num_bytes_to_process)\n        {\n            mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process);\n            memcpy(d->m_dict + dst_pos, d->m_pSrc, n);\n            if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))\n                memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos));\n            d->m_pSrc += n;\n            dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK;\n            num_bytes_to_process -= n;\n        }\n\n        dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size);\n        if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE))\n            break;\n\n        while (lookahead_size >= 4)\n        {\n            mz_uint cur_match_dist, cur_match_len = 1;\n            mz_uint8 *pCur_dict = d->m_dict + cur_pos;\n            mz_uint first_trigram = TDEFL_READ_UNALIGNED_WORD32(pCur_dict) & 0xFFFFFF;\n            mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK;\n            mz_uint probe_pos = d->m_hash[hash];\n            d->m_hash[hash] = (mz_uint16)lookahead_pos;\n\n            if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((TDEFL_READ_UNALIGNED_WORD32(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram))\n            {\n                const mz_uint16 *p = (const mz_uint16 *)pCur_dict;\n                const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos);\n                mz_uint32 probe_len = 32;\n                do\n                {\n                } while ((TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) &&\n                         (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (TDEFL_READ_UNALIGNED_WORD2(++p) == TDEFL_READ_UNALIGNED_WORD2(++q)) && (--probe_len > 0));\n                cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q);\n                if (!probe_len)\n                    cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0;\n\n                if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)))\n                {\n                    cur_match_len = 1;\n                    *pLZ_code_buf++ = (mz_uint8)first_trigram;\n                    *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);\n                    d->m_huff_count[0][(mz_uint8)first_trigram]++;\n                }\n                else\n                {\n                    mz_uint32 s0, s1;\n                    cur_match_len = MZ_MIN(cur_match_len, lookahead_size);\n\n                    MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE));\n\n                    cur_match_dist--;\n\n                    pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN);\n#ifdef MINIZ_UNALIGNED_USE_MEMCPY\n\t\t\t\t\tmemcpy(&pLZ_code_buf[1], &cur_match_dist, sizeof(cur_match_dist));\n#else\n                    *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist;\n#endif\n                    pLZ_code_buf += 3;\n                    *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80);\n\n                    s0 = s_tdefl_small_dist_sym[cur_match_dist & 511];\n                    s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8];\n                    d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++;\n\n                    d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++;\n                }\n            }\n            else\n            {\n                *pLZ_code_buf++ = (mz_uint8)first_trigram;\n                *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);\n                d->m_huff_count[0][(mz_uint8)first_trigram]++;\n            }\n\n            if (--num_flags_left == 0)\n            {\n                num_flags_left = 8;\n                pLZ_flags = pLZ_code_buf++;\n            }\n\n            total_lz_bytes += cur_match_len;\n            lookahead_pos += cur_match_len;\n            dict_size = MZ_MIN(dict_size + cur_match_len, (mz_uint)TDEFL_LZ_DICT_SIZE);\n            cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK;\n            MZ_ASSERT(lookahead_size >= cur_match_len);\n            lookahead_size -= cur_match_len;\n\n            if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])\n            {\n                int n;\n                d->m_lookahead_pos = lookahead_pos;\n                d->m_lookahead_size = lookahead_size;\n                d->m_dict_size = dict_size;\n                d->m_total_lz_bytes = total_lz_bytes;\n                d->m_pLZ_code_buf = pLZ_code_buf;\n                d->m_pLZ_flags = pLZ_flags;\n                d->m_num_flags_left = num_flags_left;\n                if ((n = tdefl_flush_block(d, 0)) != 0)\n                    return (n < 0) ? MZ_FALSE : MZ_TRUE;\n                total_lz_bytes = d->m_total_lz_bytes;\n                pLZ_code_buf = d->m_pLZ_code_buf;\n                pLZ_flags = d->m_pLZ_flags;\n                num_flags_left = d->m_num_flags_left;\n            }\n        }\n\n        while (lookahead_size)\n        {\n            mz_uint8 lit = d->m_dict[cur_pos];\n\n            total_lz_bytes++;\n            *pLZ_code_buf++ = lit;\n            *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1);\n            if (--num_flags_left == 0)\n            {\n                num_flags_left = 8;\n                pLZ_flags = pLZ_code_buf++;\n            }\n\n            d->m_huff_count[0][lit]++;\n\n            lookahead_pos++;\n            dict_size = MZ_MIN(dict_size + 1, (mz_uint)TDEFL_LZ_DICT_SIZE);\n            cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;\n            lookahead_size--;\n\n            if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8])\n            {\n                int n;\n                d->m_lookahead_pos = lookahead_pos;\n                d->m_lookahead_size = lookahead_size;\n                d->m_dict_size = dict_size;\n                d->m_total_lz_bytes = total_lz_bytes;\n                d->m_pLZ_code_buf = pLZ_code_buf;\n                d->m_pLZ_flags = pLZ_flags;\n                d->m_num_flags_left = num_flags_left;\n                if ((n = tdefl_flush_block(d, 0)) != 0)\n                    return (n < 0) ? MZ_FALSE : MZ_TRUE;\n                total_lz_bytes = d->m_total_lz_bytes;\n                pLZ_code_buf = d->m_pLZ_code_buf;\n                pLZ_flags = d->m_pLZ_flags;\n                num_flags_left = d->m_num_flags_left;\n            }\n        }\n    }\n\n    d->m_lookahead_pos = lookahead_pos;\n    d->m_lookahead_size = lookahead_size;\n    d->m_dict_size = dict_size;\n    d->m_total_lz_bytes = total_lz_bytes;\n    d->m_pLZ_code_buf = pLZ_code_buf;\n    d->m_pLZ_flags = pLZ_flags;\n    d->m_num_flags_left = num_flags_left;\n    return MZ_TRUE;\n}\n#endif /* MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */\n\nstatic MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit)\n{\n    d->m_total_lz_bytes++;\n    *d->m_pLZ_code_buf++ = lit;\n    *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1);\n    if (--d->m_num_flags_left == 0)\n    {\n        d->m_num_flags_left = 8;\n        d->m_pLZ_flags = d->m_pLZ_code_buf++;\n    }\n    d->m_huff_count[0][lit]++;\n}\n\nstatic MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist)\n{\n    mz_uint32 s0, s1;\n\n    MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE));\n\n    d->m_total_lz_bytes += match_len;\n\n    d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN);\n\n    match_dist -= 1;\n    d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF);\n    d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8);\n    d->m_pLZ_code_buf += 3;\n\n    *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80);\n    if (--d->m_num_flags_left == 0)\n    {\n        d->m_num_flags_left = 8;\n        d->m_pLZ_flags = d->m_pLZ_code_buf++;\n    }\n\n    s0 = s_tdefl_small_dist_sym[match_dist & 511];\n    s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127];\n    d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++;\n    d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++;\n}\n\nstatic mz_bool tdefl_compress_normal(tdefl_compressor *d)\n{\n    const mz_uint8 *pSrc = d->m_pSrc;\n    size_t src_buf_left = d->m_src_buf_left;\n    tdefl_flush flush = d->m_flush;\n\n    while ((src_buf_left) || ((flush) && (d->m_lookahead_size)))\n    {\n        mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos;\n        /* Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. */\n        if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1))\n        {\n            mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2;\n            mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK];\n            mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size);\n            const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process;\n            src_buf_left -= num_bytes_to_process;\n            d->m_lookahead_size += num_bytes_to_process;\n            while (pSrc != pSrc_end)\n            {\n                mz_uint8 c = *pSrc++;\n                d->m_dict[dst_pos] = c;\n                if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))\n                    d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;\n                hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);\n                d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash];\n                d->m_hash[hash] = (mz_uint16)(ins_pos);\n                dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK;\n                ins_pos++;\n            }\n        }\n        else\n        {\n            while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))\n            {\n                mz_uint8 c = *pSrc++;\n                mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK;\n                src_buf_left--;\n                d->m_dict[dst_pos] = c;\n                if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1))\n                    d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c;\n                if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN)\n                {\n                    mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2;\n                    mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1);\n                    d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash];\n                    d->m_hash[hash] = (mz_uint16)(ins_pos);\n                }\n            }\n        }\n        d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size);\n        if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN))\n            break;\n\n        /* Simple lazy/greedy parsing state machine. */\n        len_to_move = 1;\n        cur_match_dist = 0;\n        cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1);\n        cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK;\n        if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS))\n        {\n            if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))\n            {\n                mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK];\n                cur_match_len = 0;\n                while (cur_match_len < d->m_lookahead_size)\n                {\n                    if (d->m_dict[cur_pos + cur_match_len] != c)\n                        break;\n                    cur_match_len++;\n                }\n                if (cur_match_len < TDEFL_MIN_MATCH_LEN)\n                    cur_match_len = 0;\n                else\n                    cur_match_dist = 1;\n            }\n        }\n        else\n        {\n            tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len);\n        }\n        if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U * 1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5)))\n        {\n            cur_match_dist = cur_match_len = 0;\n        }\n        if (d->m_saved_match_len)\n        {\n            if (cur_match_len > d->m_saved_match_len)\n            {\n                tdefl_record_literal(d, (mz_uint8)d->m_saved_lit);\n                if (cur_match_len >= 128)\n                {\n                    tdefl_record_match(d, cur_match_len, cur_match_dist);\n                    d->m_saved_match_len = 0;\n                    len_to_move = cur_match_len;\n                }\n                else\n                {\n                    d->m_saved_lit = d->m_dict[cur_pos];\n                    d->m_saved_match_dist = cur_match_dist;\n                    d->m_saved_match_len = cur_match_len;\n                }\n            }\n            else\n            {\n                tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist);\n                len_to_move = d->m_saved_match_len - 1;\n                d->m_saved_match_len = 0;\n            }\n        }\n        else if (!cur_match_dist)\n            tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]);\n        else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128))\n        {\n            tdefl_record_match(d, cur_match_len, cur_match_dist);\n            len_to_move = cur_match_len;\n        }\n        else\n        {\n            d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)];\n            d->m_saved_match_dist = cur_match_dist;\n            d->m_saved_match_len = cur_match_len;\n        }\n        /* Move the lookahead forward by len_to_move bytes. */\n        d->m_lookahead_pos += len_to_move;\n        MZ_ASSERT(d->m_lookahead_size >= len_to_move);\n        d->m_lookahead_size -= len_to_move;\n        d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, (mz_uint)TDEFL_LZ_DICT_SIZE);\n        /* Check if it's time to flush the current LZ codes to the internal output buffer. */\n        if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) ||\n            ((d->m_total_lz_bytes > 31 * 1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))))\n        {\n            int n;\n            d->m_pSrc = pSrc;\n            d->m_src_buf_left = src_buf_left;\n            if ((n = tdefl_flush_block(d, 0)) != 0)\n                return (n < 0) ? MZ_FALSE : MZ_TRUE;\n        }\n    }\n\n    d->m_pSrc = pSrc;\n    d->m_src_buf_left = src_buf_left;\n    return MZ_TRUE;\n}\n\nstatic tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d)\n{\n    if (d->m_pIn_buf_size)\n    {\n        *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf;\n    }\n\n    if (d->m_pOut_buf_size)\n    {\n        size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining);\n        memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n);\n        d->m_output_flush_ofs += (mz_uint)n;\n        d->m_output_flush_remaining -= (mz_uint)n;\n        d->m_out_buf_ofs += n;\n\n        *d->m_pOut_buf_size = d->m_out_buf_ofs;\n    }\n\n    return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY;\n}\n\ntdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush)\n{\n    if (!d)\n    {\n        if (pIn_buf_size)\n            *pIn_buf_size = 0;\n        if (pOut_buf_size)\n            *pOut_buf_size = 0;\n        return TDEFL_STATUS_BAD_PARAM;\n    }\n\n    d->m_pIn_buf = pIn_buf;\n    d->m_pIn_buf_size = pIn_buf_size;\n    d->m_pOut_buf = pOut_buf;\n    d->m_pOut_buf_size = pOut_buf_size;\n    d->m_pSrc = (const mz_uint8 *)(pIn_buf);\n    d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0;\n    d->m_out_buf_ofs = 0;\n    d->m_flush = flush;\n\n    if (((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) ||\n        (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf))\n    {\n        if (pIn_buf_size)\n            *pIn_buf_size = 0;\n        if (pOut_buf_size)\n            *pOut_buf_size = 0;\n        return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM);\n    }\n    d->m_wants_to_finish |= (flush == TDEFL_FINISH);\n\n    if ((d->m_output_flush_remaining) || (d->m_finished))\n        return (d->m_prev_return_status = tdefl_flush_output_buffer(d));\n\n#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN\n    if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) &&\n        ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) &&\n        ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0))\n    {\n        if (!tdefl_compress_fast(d))\n            return d->m_prev_return_status;\n    }\n    else\n#endif /* #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN */\n    {\n        if (!tdefl_compress_normal(d))\n            return d->m_prev_return_status;\n    }\n\n    if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf))\n        d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf);\n\n    if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining))\n    {\n        if (tdefl_flush_block(d, flush) < 0)\n            return d->m_prev_return_status;\n        d->m_finished = (flush == TDEFL_FINISH);\n        if (flush == TDEFL_FULL_FLUSH)\n        {\n            MZ_CLEAR_OBJ(d->m_hash);\n            MZ_CLEAR_OBJ(d->m_next);\n            d->m_dict_size = 0;\n        }\n    }\n\n    return (d->m_prev_return_status = tdefl_flush_output_buffer(d));\n}\n\ntdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush)\n{\n    MZ_ASSERT(d->m_pPut_buf_func);\n    return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush);\n}\n\ntdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)\n{\n    d->m_pPut_buf_func = pPut_buf_func;\n    d->m_pPut_buf_user = pPut_buf_user;\n    d->m_flags = (mz_uint)(flags);\n    d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3;\n    d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0;\n    d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3;\n    if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG))\n        MZ_CLEAR_OBJ(d->m_hash);\n    d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0;\n    d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0;\n    d->m_pLZ_code_buf = d->m_lz_code_buf + 1;\n    d->m_pLZ_flags = d->m_lz_code_buf;\n    *d->m_pLZ_flags = 0;\n    d->m_num_flags_left = 8;\n    d->m_pOutput_buf = d->m_output_buf;\n    d->m_pOutput_buf_end = d->m_output_buf;\n    d->m_prev_return_status = TDEFL_STATUS_OKAY;\n    d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0;\n    d->m_adler32 = 1;\n    d->m_pIn_buf = NULL;\n    d->m_pOut_buf = NULL;\n    d->m_pIn_buf_size = NULL;\n    d->m_pOut_buf_size = NULL;\n    d->m_flush = TDEFL_NO_FLUSH;\n    d->m_pSrc = NULL;\n    d->m_src_buf_left = 0;\n    d->m_out_buf_ofs = 0;\n    if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG))\n        MZ_CLEAR_OBJ(d->m_dict);\n    memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0);\n    memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1);\n    return TDEFL_STATUS_OKAY;\n}\n\ntdefl_status tdefl_get_prev_return_status(tdefl_compressor *d)\n{\n    return d->m_prev_return_status;\n}\n\nmz_uint32 tdefl_get_adler32(tdefl_compressor *d)\n{\n    return d->m_adler32;\n}\n\nmz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)\n{\n    tdefl_compressor *pComp;\n    mz_bool succeeded;\n    if (((buf_len) && (!pBuf)) || (!pPut_buf_func))\n        return MZ_FALSE;\n    pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor));\n    if (!pComp)\n        return MZ_FALSE;\n    succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY);\n    succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE);\n    MZ_FREE(pComp);\n    return succeeded;\n}\n\ntypedef struct\n{\n    size_t m_size, m_capacity;\n    mz_uint8 *m_pBuf;\n    mz_bool m_expandable;\n} tdefl_output_buffer;\n\nstatic mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser)\n{\n    tdefl_output_buffer *p = (tdefl_output_buffer *)pUser;\n    size_t new_size = p->m_size + len;\n    if (new_size > p->m_capacity)\n    {\n        size_t new_capacity = p->m_capacity;\n        mz_uint8 *pNew_buf;\n        if (!p->m_expandable)\n            return MZ_FALSE;\n        do\n        {\n            new_capacity = MZ_MAX(128U, new_capacity << 1U);\n        } while (new_size > new_capacity);\n        pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity);\n        if (!pNew_buf)\n            return MZ_FALSE;\n        p->m_pBuf = pNew_buf;\n        p->m_capacity = new_capacity;\n    }\n    memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len);\n    p->m_size = new_size;\n    return MZ_TRUE;\n}\n\nvoid *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)\n{\n    tdefl_output_buffer out_buf;\n    MZ_CLEAR_OBJ(out_buf);\n    if (!pOut_len)\n        return MZ_FALSE;\n    else\n        *pOut_len = 0;\n    out_buf.m_expandable = MZ_TRUE;\n    if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags))\n        return NULL;\n    *pOut_len = out_buf.m_size;\n    return out_buf.m_pBuf;\n}\n\nsize_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)\n{\n    tdefl_output_buffer out_buf;\n    MZ_CLEAR_OBJ(out_buf);\n    if (!pOut_buf)\n        return 0;\n    out_buf.m_pBuf = (mz_uint8 *)pOut_buf;\n    out_buf.m_capacity = out_buf_len;\n    if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags))\n        return 0;\n    return out_buf.m_size;\n}\n\nstatic const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };\n\n/* level may actually range from [0,10] (10 is a \"hidden\" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). */\nmz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy)\n{\n    mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0);\n    if (window_bits > 0)\n        comp_flags |= TDEFL_WRITE_ZLIB_HEADER;\n\n    if (!level)\n        comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS;\n    else if (strategy == MZ_FILTERED)\n        comp_flags |= TDEFL_FILTER_MATCHES;\n    else if (strategy == MZ_HUFFMAN_ONLY)\n        comp_flags &= ~TDEFL_MAX_PROBES_MASK;\n    else if (strategy == MZ_FIXED)\n        comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS;\n    else if (strategy == MZ_RLE)\n        comp_flags |= TDEFL_RLE_MATCHES;\n\n    return comp_flags;\n}\n\n#ifdef _MSC_VER\n#pragma warning(push)\n#pragma warning(disable : 4204) /* nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) */\n#endif\n\n/* Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at\n http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/.\n This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. */\nvoid *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip)\n{\n    /* Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. */\n    static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 };\n    tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor));\n    tdefl_output_buffer out_buf;\n    int i, bpl = w * num_chans, y, z;\n    mz_uint32 c;\n    *pLen_out = 0;\n    if (!pComp)\n        return NULL;\n    MZ_CLEAR_OBJ(out_buf);\n    out_buf.m_expandable = MZ_TRUE;\n    out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h);\n    if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity)))\n    {\n        MZ_FREE(pComp);\n        return NULL;\n    }\n    /* write dummy header */\n    for (z = 41; z; --z)\n        tdefl_output_buffer_putter(&z, 1, &out_buf);\n    /* compress image data */\n    tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER);\n    for (y = 0; y < h; ++y)\n    {\n        tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH);\n        tdefl_compress_buffer(pComp, (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH);\n    }\n    if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE)\n    {\n        MZ_FREE(pComp);\n        MZ_FREE(out_buf.m_pBuf);\n        return NULL;\n    }\n    /* write real header */\n    *pLen_out = out_buf.m_size - 41;\n    {\n        static const mz_uint8 chans[] = { 0x00, 0x00, 0x04, 0x02, 0x06 };\n        mz_uint8 pnghdr[41] = { 0x89, 0x50, 0x4e, 0x47, 0x0d,\n                                0x0a, 0x1a, 0x0a, 0x00, 0x00,\n                                0x00, 0x0d, 0x49, 0x48, 0x44,\n                                0x52, 0x00, 0x00, 0x00, 0x00,\n                                0x00, 0x00, 0x00, 0x00, 0x08,\n                                0x00, 0x00, 0x00, 0x00, 0x00,\n                                0x00, 0x00, 0x00, 0x00, 0x00,\n                                0x00, 0x00, 0x49, 0x44, 0x41,\n                                0x54 };\n        pnghdr[18] = (mz_uint8)(w >> 8);\n        pnghdr[19] = (mz_uint8)w;\n        pnghdr[22] = (mz_uint8)(h >> 8);\n        pnghdr[23] = (mz_uint8)h;\n        pnghdr[25] = chans[num_chans];\n        pnghdr[33] = (mz_uint8)(*pLen_out >> 24);\n        pnghdr[34] = (mz_uint8)(*pLen_out >> 16);\n        pnghdr[35] = (mz_uint8)(*pLen_out >> 8);\n        pnghdr[36] = (mz_uint8)*pLen_out;\n        c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17);\n        for (i = 0; i < 4; ++i, c <<= 8)\n            ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24);\n        memcpy(out_buf.m_pBuf, pnghdr, 41);\n    }\n    /* write footer (IDAT CRC-32, followed by IEND chunk) */\n    if (!tdefl_output_buffer_putter(\"\\0\\0\\0\\0\\0\\0\\0\\0\\x49\\x45\\x4e\\x44\\xae\\x42\\x60\\x82\", 16, &out_buf))\n    {\n        *pLen_out = 0;\n        MZ_FREE(pComp);\n        MZ_FREE(out_buf.m_pBuf);\n        return NULL;\n    }\n    c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, *pLen_out + 4);\n    for (i = 0; i < 4; ++i, c <<= 8)\n        (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24);\n    /* compute final size of file, grab compressed data buffer and return */\n    *pLen_out += 57;\n    MZ_FREE(pComp);\n    return out_buf.m_pBuf;\n}\nvoid *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out)\n{\n    /* Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) */\n    return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE);\n}\n\n#ifndef MINIZ_NO_MALLOC\n/* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */\n/* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */\n/* structure size and allocation mechanism. */\ntdefl_compressor *tdefl_compressor_alloc()\n{\n    return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor));\n}\n\nvoid tdefl_compressor_free(tdefl_compressor *pComp)\n{\n    MZ_FREE(pComp);\n}\n#endif\n\n#ifdef _MSC_VER\n#pragma warning(pop)\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n /**************************************************************************\n *\n * Copyright 2013-2014 RAD Game Tools and Valve Software\n * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC\n * All Rights Reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n **************************************************************************/\n\n\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* ------------------- Low-level Decompression (completely independent from all compression API's) */\n\n#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l)\n#define TINFL_MEMSET(p, c, l) memset(p, c, l)\n\n#define TINFL_CR_BEGIN  \\\n    switch (r->m_state) \\\n    {                   \\\n        case 0:\n#define TINFL_CR_RETURN(state_index, result) \\\n    do                                       \\\n    {                                        \\\n        status = result;                     \\\n        r->m_state = state_index;            \\\n        goto common_exit;                    \\\n        case state_index:;                   \\\n    }                                        \\\n    MZ_MACRO_END\n#define TINFL_CR_RETURN_FOREVER(state_index, result) \\\n    do                                               \\\n    {                                                \\\n        for (;;)                                     \\\n        {                                            \\\n            TINFL_CR_RETURN(state_index, result);    \\\n        }                                            \\\n    }                                                \\\n    MZ_MACRO_END\n#define TINFL_CR_FINISH }\n\n#define TINFL_GET_BYTE(state_index, c)                                                                                                                           \\\n    do                                                                                                                                                           \\\n    {                                                                                                                                                            \\\n        while (pIn_buf_cur >= pIn_buf_end)                                                                                                                       \\\n        {                                                                                                                                                        \\\n            TINFL_CR_RETURN(state_index, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS); \\\n        }                                                                                                                                                        \\\n        c = *pIn_buf_cur++;                                                                                                                                      \\\n    }                                                                                                                                                            \\\n    MZ_MACRO_END\n\n#define TINFL_NEED_BITS(state_index, n)                \\\n    do                                                 \\\n    {                                                  \\\n        mz_uint c;                                     \\\n        TINFL_GET_BYTE(state_index, c);                \\\n        bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \\\n        num_bits += 8;                                 \\\n    } while (num_bits < (mz_uint)(n))\n#define TINFL_SKIP_BITS(state_index, n)      \\\n    do                                       \\\n    {                                        \\\n        if (num_bits < (mz_uint)(n))         \\\n        {                                    \\\n            TINFL_NEED_BITS(state_index, n); \\\n        }                                    \\\n        bit_buf >>= (n);                     \\\n        num_bits -= (n);                     \\\n    }                                        \\\n    MZ_MACRO_END\n#define TINFL_GET_BITS(state_index, b, n)    \\\n    do                                       \\\n    {                                        \\\n        if (num_bits < (mz_uint)(n))         \\\n        {                                    \\\n            TINFL_NEED_BITS(state_index, n); \\\n        }                                    \\\n        b = bit_buf & ((1 << (n)) - 1);      \\\n        bit_buf >>= (n);                     \\\n        num_bits -= (n);                     \\\n    }                                        \\\n    MZ_MACRO_END\n\n/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */\n/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */\n/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */\n/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */\n#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff)                             \\\n    do                                                                         \\\n    {                                                                          \\\n        temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)];     \\\n        if (temp >= 0)                                                         \\\n        {                                                                      \\\n            code_len = temp >> 9;                                              \\\n            if ((code_len) && (num_bits >= code_len))                          \\\n                break;                                                         \\\n        }                                                                      \\\n        else if (num_bits > TINFL_FAST_LOOKUP_BITS)                            \\\n        {                                                                      \\\n            code_len = TINFL_FAST_LOOKUP_BITS;                                 \\\n            do                                                                 \\\n            {                                                                  \\\n                temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \\\n            } while ((temp < 0) && (num_bits >= (code_len + 1)));              \\\n            if (temp >= 0)                                                     \\\n                break;                                                         \\\n        }                                                                      \\\n        TINFL_GET_BYTE(state_index, c);                                        \\\n        bit_buf |= (((tinfl_bit_buf_t)c) << num_bits);                         \\\n        num_bits += 8;                                                         \\\n    } while (num_bits < 15);\n\n/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */\n/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */\n/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */\n/* The slow path is only executed at the very end of the input buffer. */\n/* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */\n/* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */\n#define TINFL_HUFF_DECODE(state_index, sym, pHuff)                                                                                  \\\n    do                                                                                                                              \\\n    {                                                                                                                               \\\n        int temp;                                                                                                                   \\\n        mz_uint code_len, c;                                                                                                        \\\n        if (num_bits < 15)                                                                                                          \\\n        {                                                                                                                           \\\n            if ((pIn_buf_end - pIn_buf_cur) < 2)                                                                                    \\\n            {                                                                                                                       \\\n                TINFL_HUFF_BITBUF_FILL(state_index, pHuff);                                                                         \\\n            }                                                                                                                       \\\n            else                                                                                                                    \\\n            {                                                                                                                       \\\n                bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \\\n                pIn_buf_cur += 2;                                                                                                   \\\n                num_bits += 16;                                                                                                     \\\n            }                                                                                                                       \\\n        }                                                                                                                           \\\n        if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)                                               \\\n            code_len = temp >> 9, temp &= 511;                                                                                      \\\n        else                                                                                                                        \\\n        {                                                                                                                           \\\n            code_len = TINFL_FAST_LOOKUP_BITS;                                                                                      \\\n            do                                                                                                                      \\\n            {                                                                                                                       \\\n                temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)];                                                      \\\n            } while (temp < 0);                                                                                                     \\\n        }                                                                                                                           \\\n        sym = temp;                                                                                                                 \\\n        bit_buf >>= code_len;                                                                                                       \\\n        num_bits -= code_len;                                                                                                       \\\n    }                                                                                                                               \\\n    MZ_MACRO_END\n\ntinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags)\n{\n    static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 };\n    static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 };\n    static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 };\n    static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 };\n    static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 };\n    static const int s_min_table_sizes[3] = { 257, 1, 4 };\n\n    tinfl_status status = TINFL_STATUS_FAILED;\n    mz_uint32 num_bits, dist, counter, num_extra;\n    tinfl_bit_buf_t bit_buf;\n    const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size;\n    mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size;\n    size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start;\n\n    /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */\n    if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start))\n    {\n        *pIn_buf_size = *pOut_buf_size = 0;\n        return TINFL_STATUS_BAD_PARAM;\n    }\n\n    num_bits = r->m_num_bits;\n    bit_buf = r->m_bit_buf;\n    dist = r->m_dist;\n    counter = r->m_counter;\n    num_extra = r->m_num_extra;\n    dist_from_out_buf_start = r->m_dist_from_out_buf_start;\n    TINFL_CR_BEGIN\n\n    bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0;\n    r->m_z_adler32 = r->m_check_adler32 = 1;\n    if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)\n    {\n        TINFL_GET_BYTE(1, r->m_zhdr0);\n        TINFL_GET_BYTE(2, r->m_zhdr1);\n        counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8));\n        if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))\n            counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4)))));\n        if (counter)\n        {\n            TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED);\n        }\n    }\n\n    do\n    {\n        TINFL_GET_BITS(3, r->m_final, 3);\n        r->m_type = r->m_final >> 1;\n        if (r->m_type == 0)\n        {\n            TINFL_SKIP_BITS(5, num_bits & 7);\n            for (counter = 0; counter < 4; ++counter)\n            {\n                if (num_bits)\n                    TINFL_GET_BITS(6, r->m_raw_header[counter], 8);\n                else\n                    TINFL_GET_BYTE(7, r->m_raw_header[counter]);\n            }\n            if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8))))\n            {\n                TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED);\n            }\n            while ((counter) && (num_bits))\n            {\n                TINFL_GET_BITS(51, dist, 8);\n                while (pOut_buf_cur >= pOut_buf_end)\n                {\n                    TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT);\n                }\n                *pOut_buf_cur++ = (mz_uint8)dist;\n                counter--;\n            }\n            while (counter)\n            {\n                size_t n;\n                while (pOut_buf_cur >= pOut_buf_end)\n                {\n                    TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT);\n                }\n                while (pIn_buf_cur >= pIn_buf_end)\n                {\n                    TINFL_CR_RETURN(38, (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) ? TINFL_STATUS_NEEDS_MORE_INPUT : TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS);\n                }\n                n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter);\n                TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n);\n                pIn_buf_cur += n;\n                pOut_buf_cur += n;\n                counter -= (mz_uint)n;\n            }\n        }\n        else if (r->m_type == 3)\n        {\n            TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED);\n        }\n        else\n        {\n            if (r->m_type == 1)\n            {\n                mz_uint8 *p = r->m_tables[0].m_code_size;\n                mz_uint i;\n                r->m_table_sizes[0] = 288;\n                r->m_table_sizes[1] = 32;\n                TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32);\n                for (i = 0; i <= 143; ++i)\n                    *p++ = 8;\n                for (; i <= 255; ++i)\n                    *p++ = 9;\n                for (; i <= 279; ++i)\n                    *p++ = 7;\n                for (; i <= 287; ++i)\n                    *p++ = 8;\n            }\n            else\n            {\n                for (counter = 0; counter < 3; counter++)\n                {\n                    TINFL_GET_BITS(11, r->m_table_sizes[counter], \"\\05\\05\\04\"[counter]);\n                    r->m_table_sizes[counter] += s_min_table_sizes[counter];\n                }\n                MZ_CLEAR_OBJ(r->m_tables[2].m_code_size);\n                for (counter = 0; counter < r->m_table_sizes[2]; counter++)\n                {\n                    mz_uint s;\n                    TINFL_GET_BITS(14, s, 3);\n                    r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s;\n                }\n                r->m_table_sizes[2] = 19;\n            }\n            for (; (int)r->m_type >= 0; r->m_type--)\n            {\n                int tree_next, tree_cur;\n                tinfl_huff_table *pTable;\n                mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16];\n                pTable = &r->m_tables[r->m_type];\n                MZ_CLEAR_OBJ(total_syms);\n                MZ_CLEAR_OBJ(pTable->m_look_up);\n                MZ_CLEAR_OBJ(pTable->m_tree);\n                for (i = 0; i < r->m_table_sizes[r->m_type]; ++i)\n                    total_syms[pTable->m_code_size[i]]++;\n                used_syms = 0, total = 0;\n                next_code[0] = next_code[1] = 0;\n                for (i = 1; i <= 15; ++i)\n                {\n                    used_syms += total_syms[i];\n                    next_code[i + 1] = (total = ((total + total_syms[i]) << 1));\n                }\n                if ((65536 != total) && (used_syms > 1))\n                {\n                    TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED);\n                }\n                for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index)\n                {\n                    mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index];\n                    if (!code_size)\n                        continue;\n                    cur_code = next_code[code_size]++;\n                    for (l = code_size; l > 0; l--, cur_code >>= 1)\n                        rev_code = (rev_code << 1) | (cur_code & 1);\n                    if (code_size <= TINFL_FAST_LOOKUP_BITS)\n                    {\n                        mz_int16 k = (mz_int16)((code_size << 9) | sym_index);\n                        while (rev_code < TINFL_FAST_LOOKUP_SIZE)\n                        {\n                            pTable->m_look_up[rev_code] = k;\n                            rev_code += (1 << code_size);\n                        }\n                        continue;\n                    }\n                    if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)]))\n                    {\n                        pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next;\n                        tree_cur = tree_next;\n                        tree_next -= 2;\n                    }\n                    rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1);\n                    for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--)\n                    {\n                        tree_cur -= ((rev_code >>= 1) & 1);\n                        if (!pTable->m_tree[-tree_cur - 1])\n                        {\n                            pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next;\n                            tree_cur = tree_next;\n                            tree_next -= 2;\n                        }\n                        else\n                            tree_cur = pTable->m_tree[-tree_cur - 1];\n                    }\n                    tree_cur -= ((rev_code >>= 1) & 1);\n                    pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index;\n                }\n                if (r->m_type == 2)\n                {\n                    for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);)\n                    {\n                        mz_uint s;\n                        TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]);\n                        if (dist < 16)\n                        {\n                            r->m_len_codes[counter++] = (mz_uint8)dist;\n                            continue;\n                        }\n                        if ((dist == 16) && (!counter))\n                        {\n                            TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED);\n                        }\n                        num_extra = \"\\02\\03\\07\"[dist - 16];\n                        TINFL_GET_BITS(18, s, num_extra);\n                        s += \"\\03\\03\\013\"[dist - 16];\n                        TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s);\n                        counter += s;\n                    }\n                    if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter)\n                    {\n                        TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED);\n                    }\n                    TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]);\n                    TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]);\n                }\n            }\n            for (;;)\n            {\n                mz_uint8 *pSrc;\n                for (;;)\n                {\n                    if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2))\n                    {\n                        TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]);\n                        if (counter >= 256)\n                            break;\n                        while (pOut_buf_cur >= pOut_buf_end)\n                        {\n                            TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT);\n                        }\n                        *pOut_buf_cur++ = (mz_uint8)counter;\n                    }\n                    else\n                    {\n                        int sym2;\n                        mz_uint code_len;\n#if TINFL_USE_64BIT_BITBUF\n                        if (num_bits < 30)\n                        {\n                            bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits);\n                            pIn_buf_cur += 4;\n                            num_bits += 32;\n                        }\n#else\n                        if (num_bits < 15)\n                        {\n                            bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits);\n                            pIn_buf_cur += 2;\n                            num_bits += 16;\n                        }\n#endif\n                        if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)\n                            code_len = sym2 >> 9;\n                        else\n                        {\n                            code_len = TINFL_FAST_LOOKUP_BITS;\n                            do\n                            {\n                                sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)];\n                            } while (sym2 < 0);\n                        }\n                        counter = sym2;\n                        bit_buf >>= code_len;\n                        num_bits -= code_len;\n                        if (counter & 256)\n                            break;\n\n#if !TINFL_USE_64BIT_BITBUF\n                        if (num_bits < 15)\n                        {\n                            bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits);\n                            pIn_buf_cur += 2;\n                            num_bits += 16;\n                        }\n#endif\n                        if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0)\n                            code_len = sym2 >> 9;\n                        else\n                        {\n                            code_len = TINFL_FAST_LOOKUP_BITS;\n                            do\n                            {\n                                sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)];\n                            } while (sym2 < 0);\n                        }\n                        bit_buf >>= code_len;\n                        num_bits -= code_len;\n\n                        pOut_buf_cur[0] = (mz_uint8)counter;\n                        if (sym2 & 256)\n                        {\n                            pOut_buf_cur++;\n                            counter = sym2;\n                            break;\n                        }\n                        pOut_buf_cur[1] = (mz_uint8)sym2;\n                        pOut_buf_cur += 2;\n                    }\n                }\n                if ((counter &= 511) == 256)\n                    break;\n\n                num_extra = s_length_extra[counter - 257];\n                counter = s_length_base[counter - 257];\n                if (num_extra)\n                {\n                    mz_uint extra_bits;\n                    TINFL_GET_BITS(25, extra_bits, num_extra);\n                    counter += extra_bits;\n                }\n\n                TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]);\n                num_extra = s_dist_extra[dist];\n                dist = s_dist_base[dist];\n                if (num_extra)\n                {\n                    mz_uint extra_bits;\n                    TINFL_GET_BITS(27, extra_bits, num_extra);\n                    dist += extra_bits;\n                }\n\n                dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start;\n                if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))\n                {\n                    TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED);\n                }\n\n                pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask);\n\n                if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end)\n                {\n                    while (counter--)\n                    {\n                        while (pOut_buf_cur >= pOut_buf_end)\n                        {\n                            TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT);\n                        }\n                        *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask];\n                    }\n                    continue;\n                }\n#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES\n                else if ((counter >= 9) && (counter <= dist))\n                {\n                    const mz_uint8 *pSrc_end = pSrc + (counter & ~7);\n                    do\n                    {\n#ifdef MINIZ_UNALIGNED_USE_MEMCPY\n\t\t\t\t\t\tmemcpy(pOut_buf_cur, pSrc, sizeof(mz_uint32)*2);\n#else\n                        ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0];\n                        ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1];\n#endif\n                        pOut_buf_cur += 8;\n                    } while ((pSrc += 8) < pSrc_end);\n                    if ((counter &= 7) < 3)\n                    {\n                        if (counter)\n                        {\n                            pOut_buf_cur[0] = pSrc[0];\n                            if (counter > 1)\n                                pOut_buf_cur[1] = pSrc[1];\n                            pOut_buf_cur += counter;\n                        }\n                        continue;\n                    }\n                }\n#endif\n                while(counter>2)\n                {\n                    pOut_buf_cur[0] = pSrc[0];\n                    pOut_buf_cur[1] = pSrc[1];\n                    pOut_buf_cur[2] = pSrc[2];\n                    pOut_buf_cur += 3;\n                    pSrc += 3;\n\t\t\t\t\tcounter -= 3;\n                }\n                if (counter > 0)\n                {\n                    pOut_buf_cur[0] = pSrc[0];\n                    if (counter > 1)\n                        pOut_buf_cur[1] = pSrc[1];\n                    pOut_buf_cur += counter;\n                }\n            }\n        }\n    } while (!(r->m_final & 1));\n\n    /* Ensure byte alignment and put back any bytes from the bitbuf if we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */\n    /* I'm being super conservative here. A number of simplifications can be made to the byte alignment part, and the Adler32 check shouldn't ever need to worry about reading from the bitbuf now. */\n    TINFL_SKIP_BITS(32, num_bits & 7);\n    while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8))\n    {\n        --pIn_buf_cur;\n        num_bits -= 8;\n    }\n    bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1);\n    MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */\n\n    if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER)\n    {\n        for (counter = 0; counter < 4; ++counter)\n        {\n            mz_uint s;\n            if (num_bits)\n                TINFL_GET_BITS(41, s, 8);\n            else\n                TINFL_GET_BYTE(42, s);\n            r->m_z_adler32 = (r->m_z_adler32 << 8) | s;\n        }\n    }\n    TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE);\n\n    TINFL_CR_FINISH\n\ncommon_exit:\n    /* As long as we aren't telling the caller that we NEED more input to make forward progress: */\n    /* Put back any bytes from the bitbuf in case we've looked ahead too far on gzip, or other Deflate streams followed by arbitrary data. */\n    /* We need to be very careful here to NOT push back any bytes we definitely know we need to make forward progress, though, or we'll lock the caller up into an inf loop. */\n    if ((status != TINFL_STATUS_NEEDS_MORE_INPUT) && (status != TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS))\n    {\n        while ((pIn_buf_cur > pIn_buf_next) && (num_bits >= 8))\n        {\n            --pIn_buf_cur;\n            num_bits -= 8;\n        }\n    }\n    r->m_num_bits = num_bits;\n    r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1);\n    r->m_dist = dist;\n    r->m_counter = counter;\n    r->m_num_extra = num_extra;\n    r->m_dist_from_out_buf_start = dist_from_out_buf_start;\n    *pIn_buf_size = pIn_buf_cur - pIn_buf_next;\n    *pOut_buf_size = pOut_buf_cur - pOut_buf_next;\n    if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0))\n    {\n        const mz_uint8 *ptr = pOut_buf_next;\n        size_t buf_len = *pOut_buf_size;\n        mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16;\n        size_t block_len = buf_len % 5552;\n        while (buf_len)\n        {\n            for (i = 0; i + 7 < block_len; i += 8, ptr += 8)\n            {\n                s1 += ptr[0], s2 += s1;\n                s1 += ptr[1], s2 += s1;\n                s1 += ptr[2], s2 += s1;\n                s1 += ptr[3], s2 += s1;\n                s1 += ptr[4], s2 += s1;\n                s1 += ptr[5], s2 += s1;\n                s1 += ptr[6], s2 += s1;\n                s1 += ptr[7], s2 += s1;\n            }\n            for (; i < block_len; ++i)\n                s1 += *ptr++, s2 += s1;\n            s1 %= 65521U, s2 %= 65521U;\n            buf_len -= block_len;\n            block_len = 5552;\n        }\n        r->m_check_adler32 = (s2 << 16) + s1;\n        if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32))\n            status = TINFL_STATUS_ADLER32_MISMATCH;\n    }\n    return status;\n}\n\n/* Higher level helper functions. */\nvoid *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags)\n{\n    tinfl_decompressor decomp;\n    void *pBuf = NULL, *pNew_buf;\n    size_t src_buf_ofs = 0, out_buf_capacity = 0;\n    *pOut_len = 0;\n    tinfl_init(&decomp);\n    for (;;)\n    {\n        size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity;\n        tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, &dst_buf_size,\n                                               (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);\n        if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT))\n        {\n            MZ_FREE(pBuf);\n            *pOut_len = 0;\n            return NULL;\n        }\n        src_buf_ofs += src_buf_size;\n        *pOut_len += dst_buf_size;\n        if (status == TINFL_STATUS_DONE)\n            break;\n        new_out_buf_capacity = out_buf_capacity * 2;\n        if (new_out_buf_capacity < 128)\n            new_out_buf_capacity = 128;\n        pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity);\n        if (!pNew_buf)\n        {\n            MZ_FREE(pBuf);\n            *pOut_len = 0;\n            return NULL;\n        }\n        pBuf = pNew_buf;\n        out_buf_capacity = new_out_buf_capacity;\n    }\n    return pBuf;\n}\n\nsize_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags)\n{\n    tinfl_decompressor decomp;\n    tinfl_status status;\n    tinfl_init(&decomp);\n    status = tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF);\n    return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len;\n}\n\nint tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags)\n{\n    int result = 0;\n    tinfl_decompressor decomp;\n    mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE);\n    size_t in_buf_ofs = 0, dict_ofs = 0;\n    if (!pDict)\n        return TINFL_STATUS_FAILED;\n    tinfl_init(&decomp);\n    for (;;)\n    {\n        size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs;\n        tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size,\n                                               (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)));\n        in_buf_ofs += in_buf_size;\n        if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user)))\n            break;\n        if (status != TINFL_STATUS_HAS_MORE_OUTPUT)\n        {\n            result = (status == TINFL_STATUS_DONE);\n            break;\n        }\n        dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1);\n    }\n    MZ_FREE(pDict);\n    *pIn_buf_size = in_buf_ofs;\n    return result;\n}\n\n#ifndef MINIZ_NO_MALLOC\ntinfl_decompressor *tinfl_decompressor_alloc()\n{\n    tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor));\n    if (pDecomp)\n        tinfl_init(pDecomp);\n    return pDecomp;\n}\n\nvoid tinfl_decompressor_free(tinfl_decompressor *pDecomp)\n{\n    MZ_FREE(pDecomp);\n}\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n /**************************************************************************\n *\n * Copyright 2013-2014 RAD Game Tools and Valve Software\n * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC\n * Copyright 2016 Martin Raiber\n * All Rights Reserved.\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \"Software\"), to deal\n * in the Software without restriction, including without limitation the rights\n * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n * copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n * THE SOFTWARE.\n *\n **************************************************************************/\n\n\n#ifndef MINIZ_NO_ARCHIVE_APIS\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* ------------------- .ZIP archive reading */\n\n#ifdef MINIZ_NO_STDIO\n#define MZ_FILE void *\n#else\n#include <sys/stat.h>\n\n#if defined(_MSC_VER) || defined(__MINGW64__)\nstatic FILE *mz_fopen(const char *pFilename, const char *pMode)\n{\n    FILE *pFile = NULL;\n    fopen_s(&pFile, pFilename, pMode);\n    return pFile;\n}\nstatic FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream)\n{\n    FILE *pFile = NULL;\n    if (freopen_s(&pFile, pPath, pMode, pStream))\n        return NULL;\n    return pFile;\n}\n#ifndef MINIZ_NO_TIME\n#include <sys/utime.h>\n#endif\n#define MZ_FOPEN mz_fopen\n#define MZ_FCLOSE fclose\n#define MZ_FREAD fread\n#define MZ_FWRITE fwrite\n#define MZ_FTELL64 _ftelli64\n#define MZ_FSEEK64 _fseeki64\n#define MZ_FILE_STAT_STRUCT _stat64\n#define MZ_FILE_STAT _stat64\n#define MZ_FFLUSH fflush\n#define MZ_FREOPEN mz_freopen\n#define MZ_DELETE_FILE remove\n#elif defined(__MINGW32__)\n#ifndef MINIZ_NO_TIME\n#include <sys/utime.h>\n#endif\n#define MZ_FOPEN(f, m) fopen(f, m)\n#define MZ_FCLOSE fclose\n#define MZ_FREAD fread\n#define MZ_FWRITE fwrite\n#define MZ_FTELL64 ftello64\n#define MZ_FSEEK64 fseeko64\n#define MZ_FILE_STAT_STRUCT _stat\n#define MZ_FILE_STAT _stat\n#define MZ_FFLUSH fflush\n#define MZ_FREOPEN(f, m, s) freopen(f, m, s)\n#define MZ_DELETE_FILE remove\n#elif defined(__TINYC__)\n#ifndef MINIZ_NO_TIME\n#include <sys/utime.h>\n#endif\n#define MZ_FOPEN(f, m) fopen(f, m)\n#define MZ_FCLOSE fclose\n#define MZ_FREAD fread\n#define MZ_FWRITE fwrite\n#define MZ_FTELL64 ftell\n#define MZ_FSEEK64 fseek\n#define MZ_FILE_STAT_STRUCT stat\n#define MZ_FILE_STAT stat\n#define MZ_FFLUSH fflush\n#define MZ_FREOPEN(f, m, s) freopen(f, m, s)\n#define MZ_DELETE_FILE remove\n#elif defined(__USE_LARGEFILE64) /* gcc, clang */\n#ifndef MINIZ_NO_TIME\n#include <utime.h>\n#endif\n#define MZ_FOPEN(f, m) fopen64(f, m)\n#define MZ_FCLOSE fclose\n#define MZ_FREAD fread\n#define MZ_FWRITE fwrite\n#define MZ_FTELL64 ftello64\n#define MZ_FSEEK64 fseeko64\n#define MZ_FILE_STAT_STRUCT stat64\n#define MZ_FILE_STAT stat64\n#define MZ_FFLUSH fflush\n#define MZ_FREOPEN(p, m, s) freopen64(p, m, s)\n#define MZ_DELETE_FILE remove\n#elif defined(__APPLE__)\n#ifndef MINIZ_NO_TIME\n#include <utime.h>\n#endif\n#define MZ_FOPEN(f, m) fopen(f, m)\n#define MZ_FCLOSE fclose\n#define MZ_FREAD fread\n#define MZ_FWRITE fwrite\n#define MZ_FTELL64 ftello\n#define MZ_FSEEK64 fseeko\n#define MZ_FILE_STAT_STRUCT stat\n#define MZ_FILE_STAT stat\n#define MZ_FFLUSH fflush\n#define MZ_FREOPEN(p, m, s) freopen(p, m, s)\n#define MZ_DELETE_FILE remove\n\n#else\n#pragma message(\"Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files.\")\n#ifndef MINIZ_NO_TIME\n#include <utime.h>\n#endif\n#define MZ_FOPEN(f, m) fopen(f, m)\n#define MZ_FCLOSE fclose\n#define MZ_FREAD fread\n#define MZ_FWRITE fwrite\n#ifdef __STRICT_ANSI__\n#define MZ_FTELL64 ftell\n#define MZ_FSEEK64 fseek\n#else\n#define MZ_FTELL64 ftello\n#define MZ_FSEEK64 fseeko\n#endif\n#define MZ_FILE_STAT_STRUCT stat\n#define MZ_FILE_STAT stat\n#define MZ_FFLUSH fflush\n#define MZ_FREOPEN(f, m, s) freopen(f, m, s)\n#define MZ_DELETE_FILE remove\n#endif /* #ifdef _MSC_VER */\n#endif /* #ifdef MINIZ_NO_STDIO */\n\n#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c))\n\n/* Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. */\nenum\n{\n    /* ZIP archive identifiers and record sizes */\n    MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50,\n    MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50,\n    MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50,\n    MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30,\n    MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46,\n    MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,\n\n    /* ZIP64 archive identifier and record sizes */\n    MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50,\n    MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50,\n    MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56,\n    MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20,\n    MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001,\n    MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50,\n    MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24,\n    MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16,\n\n    /* Central directory header record offsets */\n    MZ_ZIP_CDH_SIG_OFS = 0,\n    MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4,\n    MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6,\n    MZ_ZIP_CDH_BIT_FLAG_OFS = 8,\n    MZ_ZIP_CDH_METHOD_OFS = 10,\n    MZ_ZIP_CDH_FILE_TIME_OFS = 12,\n    MZ_ZIP_CDH_FILE_DATE_OFS = 14,\n    MZ_ZIP_CDH_CRC32_OFS = 16,\n    MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20,\n    MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24,\n    MZ_ZIP_CDH_FILENAME_LEN_OFS = 28,\n    MZ_ZIP_CDH_EXTRA_LEN_OFS = 30,\n    MZ_ZIP_CDH_COMMENT_LEN_OFS = 32,\n    MZ_ZIP_CDH_DISK_START_OFS = 34,\n    MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36,\n    MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38,\n    MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42,\n\n    /* Local directory header offsets */\n    MZ_ZIP_LDH_SIG_OFS = 0,\n    MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4,\n    MZ_ZIP_LDH_BIT_FLAG_OFS = 6,\n    MZ_ZIP_LDH_METHOD_OFS = 8,\n    MZ_ZIP_LDH_FILE_TIME_OFS = 10,\n    MZ_ZIP_LDH_FILE_DATE_OFS = 12,\n    MZ_ZIP_LDH_CRC32_OFS = 14,\n    MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18,\n    MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22,\n    MZ_ZIP_LDH_FILENAME_LEN_OFS = 26,\n    MZ_ZIP_LDH_EXTRA_LEN_OFS = 28,\n    MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR = 1 << 3,\n\n    /* End of central directory offsets */\n    MZ_ZIP_ECDH_SIG_OFS = 0,\n    MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4,\n    MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6,\n    MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8,\n    MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10,\n    MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12,\n    MZ_ZIP_ECDH_CDIR_OFS_OFS = 16,\n    MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,\n\n    /* ZIP64 End of central directory locator offsets */\n    MZ_ZIP64_ECDL_SIG_OFS = 0,                    /* 4 bytes */\n    MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4,          /* 4 bytes */\n    MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8,  /* 8 bytes */\n    MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */\n\n    /* ZIP64 End of central directory header offsets */\n    MZ_ZIP64_ECDH_SIG_OFS = 0,                       /* 4 bytes */\n    MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4,            /* 8 bytes */\n    MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12,          /* 2 bytes */\n    MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14,           /* 2 bytes */\n    MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16,            /* 4 bytes */\n    MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20,            /* 4 bytes */\n    MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */\n    MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32,       /* 8 bytes */\n    MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40,                /* 8 bytes */\n    MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48,                 /* 8 bytes */\n    MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0,\n    MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10,\n    MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1,\n    MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32,\n    MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64,\n    MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192,\n    MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11\n};\n\ntypedef struct\n{\n    void *m_p;\n    size_t m_size, m_capacity;\n    mz_uint m_element_size;\n} mz_zip_array;\n\nstruct mz_zip_internal_state_tag\n{\n    mz_zip_array m_central_dir;\n    mz_zip_array m_central_dir_offsets;\n    mz_zip_array m_sorted_central_dir_offsets;\n\n    /* The flags passed in when the archive is initially opened. */\n    uint32_t m_init_flags;\n\n    /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. */\n    mz_bool m_zip64;\n\n    /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.) */\n    mz_bool m_zip64_has_extended_info_fields;\n\n    /* These fields are used by the file, FILE, memory, and memory/heap read/write helpers. */\n    MZ_FILE *m_pFile;\n    mz_uint64 m_file_archive_start_ofs;\n\n    void *m_pMem;\n    size_t m_mem_size;\n    size_t m_mem_capacity;\n};\n\n#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size\n\n#if defined(DEBUG) || defined(_DEBUG)\nstatic MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index)\n{\n    MZ_ASSERT(index < pArray->m_size);\n    return index;\n}\n#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)]\n#else\n#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index]\n#endif\n\nstatic MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size)\n{\n    memset(pArray, 0, sizeof(mz_zip_array));\n    pArray->m_element_size = element_size;\n}\n\nstatic MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray)\n{\n    pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p);\n    memset(pArray, 0, sizeof(mz_zip_array));\n}\n\nstatic mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing)\n{\n    void *pNew_p;\n    size_t new_capacity = min_new_capacity;\n    MZ_ASSERT(pArray->m_element_size);\n    if (pArray->m_capacity >= min_new_capacity)\n        return MZ_TRUE;\n    if (growing)\n    {\n        new_capacity = MZ_MAX(1, pArray->m_capacity);\n        while (new_capacity < min_new_capacity)\n            new_capacity *= 2;\n    }\n    if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity)))\n        return MZ_FALSE;\n    pArray->m_p = pNew_p;\n    pArray->m_capacity = new_capacity;\n    return MZ_TRUE;\n}\n\nstatic MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing)\n{\n    if (new_capacity > pArray->m_capacity)\n    {\n        if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing))\n            return MZ_FALSE;\n    }\n    return MZ_TRUE;\n}\n\nstatic MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing)\n{\n    if (new_size > pArray->m_capacity)\n    {\n        if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing))\n            return MZ_FALSE;\n    }\n    pArray->m_size = new_size;\n    return MZ_TRUE;\n}\n\nstatic MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n)\n{\n    return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE);\n}\n\nstatic MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n)\n{\n    size_t orig_size = pArray->m_size;\n    if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE))\n        return MZ_FALSE;\n    if (n > 0)\n        memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size);\n    return MZ_TRUE;\n}\n\n#ifndef MINIZ_NO_TIME\nstatic MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date)\n{\n    struct tm tm;\n    memset(&tm, 0, sizeof(tm));\n    tm.tm_isdst = -1;\n    tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900;\n    tm.tm_mon = ((dos_date >> 5) & 15) - 1;\n    tm.tm_mday = dos_date & 31;\n    tm.tm_hour = (dos_time >> 11) & 31;\n    tm.tm_min = (dos_time >> 5) & 63;\n    tm.tm_sec = (dos_time << 1) & 62;\n    return mktime(&tm);\n}\n\n#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS\nstatic void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)\n{\n#ifdef _MSC_VER\n    struct tm tm_struct;\n    struct tm *tm = &tm_struct;\n    errno_t err = localtime_s(tm, &time);\n    if (err)\n    {\n        *pDOS_date = 0;\n        *pDOS_time = 0;\n        return;\n    }\n#else\n    struct tm *tm = localtime(&time);\n#endif /* #ifdef _MSC_VER */\n\n    *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1));\n    *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);\n}\n#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */\n\n#ifndef MINIZ_NO_STDIO\n#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS\nstatic mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime)\n{\n    struct MZ_FILE_STAT_STRUCT file_stat;\n\n    /* On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */\n    if (MZ_FILE_STAT(pFilename, &file_stat) != 0)\n        return MZ_FALSE;\n\n    *pTime = file_stat.st_mtime;\n\n    return MZ_TRUE;\n}\n#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/\n\nstatic mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time)\n{\n    struct utimbuf t;\n\n    memset(&t, 0, sizeof(t));\n    t.actime = access_time;\n    t.modtime = modified_time;\n\n    return !utime(pFilename, &t);\n}\n#endif /* #ifndef MINIZ_NO_STDIO */\n#endif /* #ifndef MINIZ_NO_TIME */\n\nstatic MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num)\n{\n    if (pZip)\n        pZip->m_last_error = err_num;\n    return MZ_FALSE;\n}\n\nstatic mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags)\n{\n    (void)flags;\n    if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (!pZip->m_pAlloc)\n        pZip->m_pAlloc = miniz_def_alloc_func;\n    if (!pZip->m_pFree)\n        pZip->m_pFree = miniz_def_free_func;\n    if (!pZip->m_pRealloc)\n        pZip->m_pRealloc = miniz_def_realloc_func;\n\n    pZip->m_archive_size = 0;\n    pZip->m_central_directory_file_ofs = 0;\n    pZip->m_total_files = 0;\n    pZip->m_last_error = MZ_ZIP_NO_ERROR;\n\n    if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))\n        return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n\n    memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));\n    MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));\n    MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));\n    MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));\n    pZip->m_pState->m_init_flags = flags;\n    pZip->m_pState->m_zip64 = MZ_FALSE;\n    pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE;\n\n    pZip->m_zip_mode = MZ_ZIP_MODE_READING;\n\n    return MZ_TRUE;\n}\n\nstatic MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index)\n{\n    const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;\n    const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index));\n    mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS);\n    mz_uint8 l = 0, r = 0;\n    pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;\n    pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;\n    pE = pL + MZ_MIN(l_len, r_len);\n    while (pL < pE)\n    {\n        if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))\n            break;\n        pL++;\n        pR++;\n    }\n    return (pL == pE) ? (l_len < r_len) : (l < r);\n}\n\n#define MZ_SWAP_UINT32(a, b) \\\n    do                       \\\n    {                        \\\n        mz_uint32 t = a;     \\\n        a = b;               \\\n        b = t;               \\\n    }                        \\\n    MZ_MACRO_END\n\n/* Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) */\nstatic void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip)\n{\n    mz_zip_internal_state *pState = pZip->m_pState;\n    const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;\n    const mz_zip_array *pCentral_dir = &pState->m_central_dir;\n    mz_uint32 *pIndices;\n    mz_uint32 start, end;\n    const mz_uint32 size = pZip->m_total_files;\n\n    if (size <= 1U)\n        return;\n\n    pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);\n\n    start = (size - 2U) >> 1U;\n    for (;;)\n    {\n        mz_uint64 child, root = start;\n        for (;;)\n        {\n            if ((child = (root << 1U) + 1U) >= size)\n                break;\n            child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])));\n            if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))\n                break;\n            MZ_SWAP_UINT32(pIndices[root], pIndices[child]);\n            root = child;\n        }\n        if (!start)\n            break;\n        start--;\n    }\n\n    end = size - 1;\n    while (end > 0)\n    {\n        mz_uint64 child, root = 0;\n        MZ_SWAP_UINT32(pIndices[end], pIndices[0]);\n        for (;;)\n        {\n            if ((child = (root << 1U) + 1U) >= end)\n                break;\n            child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]));\n            if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))\n                break;\n            MZ_SWAP_UINT32(pIndices[root], pIndices[child]);\n            root = child;\n        }\n        end--;\n    }\n}\n\nstatic mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs)\n{\n    mz_int64 cur_file_ofs;\n    mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];\n    mz_uint8 *pBuf = (mz_uint8 *)buf_u32;\n\n    /* Basic sanity checks - reject files which are too small */\n    if (pZip->m_archive_size < record_size)\n        return MZ_FALSE;\n\n    /* Find the record by scanning the file from the end towards the beginning. */\n    cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);\n    for (;;)\n    {\n        int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);\n\n        if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)\n            return MZ_FALSE;\n\n        for (i = n - 4; i >= 0; --i)\n        {\n            mz_uint s = MZ_READ_LE32(pBuf + i);\n            if (s == record_sig)\n            {\n                if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size)\n                    break;\n            }\n        }\n\n        if (i >= 0)\n        {\n            cur_file_ofs += i;\n            break;\n        }\n\n        /* Give up if we've searched the entire file, or we've gone back \"too far\" (~64kb) */\n        if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size)))\n            return MZ_FALSE;\n\n        cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);\n    }\n\n    *pOfs = cur_file_ofs;\n    return MZ_TRUE;\n}\n\nstatic mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags)\n{\n    mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0;\n    mz_uint64 cdir_ofs = 0;\n    mz_int64 cur_file_ofs = 0;\n    const mz_uint8 *p;\n\n    mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];\n    mz_uint8 *pBuf = (mz_uint8 *)buf_u32;\n    mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);\n    mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];\n    mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32;\n\n    mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];\n    mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32;\n\n    mz_uint64 zip64_end_of_central_dir_ofs = 0;\n\n    /* Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. */\n    if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)\n        return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);\n\n    if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs))\n        return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR);\n\n    /* Read and verify the end of central directory record. */\n    if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n\n    if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)\n        return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);\n\n    if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE))\n    {\n        if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE)\n        {\n            if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG)\n            {\n                zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS);\n                if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE))\n                    return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);\n\n                if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)\n                {\n                    if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG)\n                    {\n                        pZip->m_pState->m_zip64 = MZ_TRUE;\n                    }\n                }\n            }\n        }\n    }\n\n    pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS);\n    cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS);\n    num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);\n    cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);\n    cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS);\n    cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);\n\n    if (pZip->m_pState->m_zip64)\n    {\n        mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS);\n        mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS);\n        mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS);\n        mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS);\n        mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS);\n\n        if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12))\n            return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n        if (zip64_total_num_of_disks != 1U)\n            return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);\n\n        /* Check for miniz's practical limits */\n        if (zip64_cdir_total_entries > MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);\n\n        pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries;\n\n        if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);\n\n        cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk;\n\n        /* Check for miniz's current practical limits (sorry, this should be enough for millions of files) */\n        if (zip64_size_of_central_directory > MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);\n\n        cdir_size = (mz_uint32)zip64_size_of_central_directory;\n\n        num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS);\n\n        cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS);\n\n        cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS);\n    }\n\n    if (pZip->m_total_files != cdir_entries_on_this_disk)\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);\n\n    if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1)))\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);\n\n    if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n    if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n    pZip->m_central_directory_file_ofs = cdir_ofs;\n\n    if (pZip->m_total_files)\n    {\n        mz_uint i, n;\n        /* Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices. */\n        if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) ||\n            (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)))\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n\n        if (sort_central_dir)\n        {\n            if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE))\n                return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n        }\n\n        if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size)\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n\n        /* Now create an index into the central directory file records, do some basic sanity checking on each record */\n        p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;\n        for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i)\n        {\n            mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size;\n            mz_uint64 comp_size, decomp_size, local_header_ofs;\n\n            if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))\n                return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n            MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);\n\n            if (sort_central_dir)\n                MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i;\n\n            comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);\n            decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);\n            local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);\n            filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);\n            ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);\n\n            if ((!pZip->m_pState->m_zip64_has_extended_info_fields) &&\n                (ext_data_size) &&\n                (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX))\n            {\n                /* Attempt to find zip64 extended information field in the entry's extra data */\n                mz_uint32 extra_size_remaining = ext_data_size;\n\n                if (extra_size_remaining)\n                {\n\t\t\t\t\tconst mz_uint8 *pExtra_data;\n\t\t\t\t\tvoid* buf = NULL;\n\n\t\t\t\t\tif (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > n)\n\t\t\t\t\t{\n\t\t\t\t\t\tbuf = MZ_MALLOC(ext_data_size);\n\t\t\t\t\t\tif(buf==NULL)\n\t\t\t\t\t\t\treturn mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n\n\t\t\t\t\t\tif (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMZ_FREE(buf);\n\t\t\t\t\t\t\treturn mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tpExtra_data = (mz_uint8*)buf;\n\t\t\t\t\t}\n\t\t\t\t\telse\n\t\t\t\t\t{\n\t\t\t\t\t\tpExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size;\n\t\t\t\t\t}\n\n                    do\n                    {\n                        mz_uint32 field_id;\n                        mz_uint32 field_data_size;\n\n\t\t\t\t\t\tif (extra_size_remaining < (sizeof(mz_uint16) * 2))\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMZ_FREE(buf);\n\t\t\t\t\t\t\treturn mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\t\t\t\t\t\t}\n\n                        field_id = MZ_READ_LE16(pExtra_data);\n                        field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));\n\n\t\t\t\t\t\tif ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining)\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tMZ_FREE(buf);\n\t\t\t\t\t\t\treturn mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\t\t\t\t\t\t}\n\n                        if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)\n                        {\n                            /* Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin). */\n                            pZip->m_pState->m_zip64 = MZ_TRUE;\n                            pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE;\n                            break;\n                        }\n\n                        pExtra_data += sizeof(mz_uint16) * 2 + field_data_size;\n                        extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size;\n                    } while (extra_size_remaining);\n\n\t\t\t\t\tMZ_FREE(buf);\n                }\n            }\n\n            /* I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh */\n            if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX))\n            {\n                if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size))\n                    return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n            }\n\n            disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);\n            if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1)))\n                return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);\n\n            if (comp_size != MZ_UINT32_MAX)\n            {\n                if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)\n                    return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n            }\n\n            bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);\n            if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED)\n                return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);\n\n            if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n)\n                return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n            n -= total_header_size;\n            p += total_header_size;\n        }\n    }\n\n    if (sort_central_dir)\n        mz_zip_reader_sort_central_dir_offsets_by_filename(pZip);\n\n    return MZ_TRUE;\n}\n\nvoid mz_zip_zero_struct(mz_zip_archive *pZip)\n{\n    if (pZip)\n        MZ_CLEAR_OBJ(*pZip);\n}\n\nstatic mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error)\n{\n    mz_bool status = MZ_TRUE;\n\n    if (!pZip)\n        return MZ_FALSE;\n\n    if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))\n    {\n        if (set_last_error)\n            pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER;\n\n        return MZ_FALSE;\n    }\n\n    if (pZip->m_pState)\n    {\n        mz_zip_internal_state *pState = pZip->m_pState;\n        pZip->m_pState = NULL;\n\n        mz_zip_array_clear(pZip, &pState->m_central_dir);\n        mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);\n        mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);\n\n#ifndef MINIZ_NO_STDIO\n        if (pState->m_pFile)\n        {\n            if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE)\n            {\n                if (MZ_FCLOSE(pState->m_pFile) == EOF)\n                {\n                    if (set_last_error)\n                        pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED;\n                    status = MZ_FALSE;\n                }\n            }\n            pState->m_pFile = NULL;\n        }\n#endif /* #ifndef MINIZ_NO_STDIO */\n\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pState);\n    }\n    pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;\n\n    return status;\n}\n\nmz_bool mz_zip_reader_end(mz_zip_archive *pZip)\n{\n    return mz_zip_reader_end_internal(pZip, MZ_TRUE);\n}\nmz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags)\n{\n    if ((!pZip) || (!pZip->m_pRead))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (!mz_zip_reader_init_internal(pZip, flags))\n        return MZ_FALSE;\n\n    pZip->m_zip_type = MZ_ZIP_TYPE_USER;\n    pZip->m_archive_size = size;\n\n    if (!mz_zip_reader_read_central_dir(pZip, flags))\n    {\n        mz_zip_reader_end_internal(pZip, MZ_FALSE);\n        return MZ_FALSE;\n    }\n\n    return MZ_TRUE;\n}\n\nstatic size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)\n{\n    mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;\n    size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n);\n    memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s);\n    return s;\n}\n\nmz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags)\n{\n    if (!pMem)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)\n        return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);\n\n    if (!mz_zip_reader_init_internal(pZip, flags))\n        return MZ_FALSE;\n\n    pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY;\n    pZip->m_archive_size = size;\n    pZip->m_pRead = mz_zip_mem_read_func;\n    pZip->m_pIO_opaque = pZip;\n    pZip->m_pNeeds_keepalive = NULL;\n\n#ifdef __cplusplus\n    pZip->m_pState->m_pMem = const_cast<void *>(pMem);\n#else\n    pZip->m_pState->m_pMem = (void *)pMem;\n#endif\n\n    pZip->m_pState->m_mem_size = size;\n\n    if (!mz_zip_reader_read_central_dir(pZip, flags))\n    {\n        mz_zip_reader_end_internal(pZip, MZ_FALSE);\n        return MZ_FALSE;\n    }\n\n    return MZ_TRUE;\n}\n\n#ifndef MINIZ_NO_STDIO\nstatic size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)\n{\n    mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;\n    mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);\n\n    file_ofs += pZip->m_pState->m_file_archive_start_ofs;\n\n    if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))\n        return 0;\n\n    return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile);\n}\n\nmz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags)\n{\n    return mz_zip_reader_init_file_v2(pZip, pFilename, flags, 0, 0);\n}\n\nmz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size)\n{\n    mz_uint64 file_size;\n    MZ_FILE *pFile;\n\n    if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    pFile = MZ_FOPEN(pFilename, \"rb\");\n    if (!pFile)\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);\n\n    file_size = archive_size;\n    if (!file_size)\n    {\n        if (MZ_FSEEK64(pFile, 0, SEEK_END))\n        {\n            MZ_FCLOSE(pFile);\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);\n        }\n\n        file_size = MZ_FTELL64(pFile);\n    }\n\n    /* TODO: Better sanity check archive_size and the # of actual remaining bytes */\n\n    if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)\n    {\n\tMZ_FCLOSE(pFile);\n        return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);\n    }\n\n    if (!mz_zip_reader_init_internal(pZip, flags))\n    {\n        MZ_FCLOSE(pFile);\n        return MZ_FALSE;\n    }\n\n    pZip->m_zip_type = MZ_ZIP_TYPE_FILE;\n    pZip->m_pRead = mz_zip_file_read_func;\n    pZip->m_pIO_opaque = pZip;\n    pZip->m_pState->m_pFile = pFile;\n    pZip->m_archive_size = file_size;\n    pZip->m_pState->m_file_archive_start_ofs = file_start_ofs;\n\n    if (!mz_zip_reader_read_central_dir(pZip, flags))\n    {\n        mz_zip_reader_end_internal(pZip, MZ_FALSE);\n        return MZ_FALSE;\n    }\n\n    return MZ_TRUE;\n}\n\nmz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags)\n{\n    mz_uint64 cur_file_ofs;\n\n    if ((!pZip) || (!pFile))\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);\n\n    cur_file_ofs = MZ_FTELL64(pFile);\n\n    if (!archive_size)\n    {\n        if (MZ_FSEEK64(pFile, 0, SEEK_END))\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);\n\n        archive_size = MZ_FTELL64(pFile) - cur_file_ofs;\n\n        if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)\n            return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);\n    }\n\n    if (!mz_zip_reader_init_internal(pZip, flags))\n        return MZ_FALSE;\n\n    pZip->m_zip_type = MZ_ZIP_TYPE_CFILE;\n    pZip->m_pRead = mz_zip_file_read_func;\n\n    pZip->m_pIO_opaque = pZip;\n    pZip->m_pState->m_pFile = pFile;\n    pZip->m_archive_size = archive_size;\n    pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs;\n\n    if (!mz_zip_reader_read_central_dir(pZip, flags))\n    {\n        mz_zip_reader_end_internal(pZip, MZ_FALSE);\n        return MZ_FALSE;\n    }\n\n    return MZ_TRUE;\n}\n\n#endif /* #ifndef MINIZ_NO_STDIO */\n\nstatic MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index)\n{\n    if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files))\n        return NULL;\n    return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));\n}\n\nmz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index)\n{\n    mz_uint m_bit_flag;\n    const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);\n    if (!p)\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n        return MZ_FALSE;\n    }\n\n    m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);\n    return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0;\n}\n\nmz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index)\n{\n    mz_uint bit_flag;\n    mz_uint method;\n\n    const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);\n    if (!p)\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n        return MZ_FALSE;\n    }\n\n    method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);\n    bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);\n\n    if ((method != 0) && (method != MZ_DEFLATED))\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);\n        return MZ_FALSE;\n    }\n\n    if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION))\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);\n        return MZ_FALSE;\n    }\n\n    if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);\n        return MZ_FALSE;\n    }\n\n    return MZ_TRUE;\n}\n\nmz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index)\n{\n    mz_uint filename_len, attribute_mapping_id, external_attr;\n    const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);\n    if (!p)\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n        return MZ_FALSE;\n    }\n\n    filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);\n    if (filename_len)\n    {\n        if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/')\n            return MZ_TRUE;\n    }\n\n    /* Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. */\n    /* Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. */\n    /* FIXME: Remove this check? Is it necessary - we already check the filename. */\n    attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8;\n    (void)attribute_mapping_id;\n\n    external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);\n    if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0)\n    {\n        return MZ_TRUE;\n    }\n\n    return MZ_FALSE;\n}\n\nstatic mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data)\n{\n    mz_uint n;\n    const mz_uint8 *p = pCentral_dir_header;\n\n    if (pFound_zip64_extra_data)\n        *pFound_zip64_extra_data = MZ_FALSE;\n\n    if ((!p) || (!pStat))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    /* Extract fields from the central directory record. */\n    pStat->m_file_index = file_index;\n    pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index);\n    pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS);\n    pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS);\n    pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);\n    pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);\n#ifndef MINIZ_NO_TIME\n    pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS));\n#endif\n    pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS);\n    pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);\n    pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);\n    pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS);\n    pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);\n    pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);\n\n    /* Copy as much of the filename and comment as possible. */\n    n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);\n    n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1);\n    memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);\n    pStat->m_filename[n] = '\\0';\n\n    n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS);\n    n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1);\n    pStat->m_comment_size = n;\n    memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n);\n    pStat->m_comment[n] = '\\0';\n\n    /* Set some flags for convienance */\n    pStat->m_is_directory = mz_zip_reader_is_file_a_directory(pZip, file_index);\n    pStat->m_is_encrypted = mz_zip_reader_is_file_encrypted(pZip, file_index);\n    pStat->m_is_supported = mz_zip_reader_is_file_supported(pZip, file_index);\n\n    /* See if we need to read any zip64 extended information fields. */\n    /* Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them). */\n    if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX)\n    {\n        /* Attempt to find zip64 extended information field in the entry's extra data */\n        mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);\n\n        if (extra_size_remaining)\n        {\n            const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);\n\n            do\n            {\n                mz_uint32 field_id;\n                mz_uint32 field_data_size;\n\n                if (extra_size_remaining < (sizeof(mz_uint16) * 2))\n                    return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n                field_id = MZ_READ_LE16(pExtra_data);\n                field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));\n\n                if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining)\n                    return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n                if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)\n                {\n                    const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2;\n                    mz_uint32 field_data_remaining = field_data_size;\n\n                    if (pFound_zip64_extra_data)\n                        *pFound_zip64_extra_data = MZ_TRUE;\n\n                    if (pStat->m_uncomp_size == MZ_UINT32_MAX)\n                    {\n                        if (field_data_remaining < sizeof(mz_uint64))\n                            return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n                        pStat->m_uncomp_size = MZ_READ_LE64(pField_data);\n                        pField_data += sizeof(mz_uint64);\n                        field_data_remaining -= sizeof(mz_uint64);\n                    }\n\n                    if (pStat->m_comp_size == MZ_UINT32_MAX)\n                    {\n                        if (field_data_remaining < sizeof(mz_uint64))\n                            return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n                        pStat->m_comp_size = MZ_READ_LE64(pField_data);\n                        pField_data += sizeof(mz_uint64);\n                        field_data_remaining -= sizeof(mz_uint64);\n                    }\n\n                    if (pStat->m_local_header_ofs == MZ_UINT32_MAX)\n                    {\n                        if (field_data_remaining < sizeof(mz_uint64))\n                            return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n                        pStat->m_local_header_ofs = MZ_READ_LE64(pField_data);\n                        pField_data += sizeof(mz_uint64);\n                        field_data_remaining -= sizeof(mz_uint64);\n                    }\n\n                    break;\n                }\n\n                pExtra_data += sizeof(mz_uint16) * 2 + field_data_size;\n                extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size;\n            } while (extra_size_remaining);\n        }\n    }\n\n    return MZ_TRUE;\n}\n\nstatic MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags)\n{\n    mz_uint i;\n    if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE)\n        return 0 == memcmp(pA, pB, len);\n    for (i = 0; i < len; ++i)\n        if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i]))\n            return MZ_FALSE;\n    return MZ_TRUE;\n}\n\nstatic MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len)\n{\n    const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;\n    mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS);\n    mz_uint8 l = 0, r = 0;\n    pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;\n    pE = pL + MZ_MIN(l_len, r_len);\n    while (pL < pE)\n    {\n        if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))\n            break;\n        pL++;\n        pR++;\n    }\n    return (pL == pE) ? (int)(l_len - r_len) : (l - r);\n}\n\nstatic mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex)\n{\n    mz_zip_internal_state *pState = pZip->m_pState;\n    const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;\n    const mz_zip_array *pCentral_dir = &pState->m_central_dir;\n    mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);\n    const uint32_t size = pZip->m_total_files;\n    const mz_uint filename_len = (mz_uint)strlen(pFilename);\n\n    if (pIndex)\n        *pIndex = 0;\n\n    if (size)\n    {\n        /* yes I could use uint32_t's, but then we would have to add some special case checks in the loop, argh, and */\n        /* honestly the major expense here on 32-bit CPU's will still be the filename compare */\n        mz_int64 l = 0, h = (mz_int64)size - 1;\n\n        while (l <= h)\n        {\n            mz_int64 m = l + ((h - l) >> 1);\n            uint32_t file_index = pIndices[(uint32_t)m];\n\n            int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len);\n            if (!comp)\n            {\n                if (pIndex)\n                    *pIndex = file_index;\n                return MZ_TRUE;\n            }\n            else if (comp < 0)\n                l = m + 1;\n            else\n                h = m - 1;\n        }\n    }\n\n    return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND);\n}\n\nint mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags)\n{\n    mz_uint32 index;\n    if (!mz_zip_reader_locate_file_v2(pZip, pName, pComment, flags, &index))\n        return -1;\n    else\n        return (int)index;\n}\n\nmz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex)\n{\n    mz_uint file_index;\n    size_t name_len, comment_len;\n\n    if (pIndex)\n        *pIndex = 0;\n\n    if ((!pZip) || (!pZip->m_pState) || (!pName))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    /* See if we can use a binary search */\n    if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) &&\n        (pZip->m_zip_mode == MZ_ZIP_MODE_READING) &&\n        ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size))\n    {\n        return mz_zip_locate_file_binary_search(pZip, pName, pIndex);\n    }\n\n    /* Locate the entry by scanning the entire central directory */\n    name_len = strlen(pName);\n    if (name_len > MZ_UINT16_MAX)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    comment_len = pComment ? strlen(pComment) : 0;\n    if (comment_len > MZ_UINT16_MAX)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    for (file_index = 0; file_index < pZip->m_total_files; file_index++)\n    {\n        const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));\n        mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);\n        const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;\n        if (filename_len < name_len)\n            continue;\n        if (comment_len)\n        {\n            mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS);\n            const char *pFile_comment = pFilename + filename_len + file_extra_len;\n            if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags)))\n                continue;\n        }\n        if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len))\n        {\n            int ofs = filename_len - 1;\n            do\n            {\n                if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\\\') || (pFilename[ofs] == ':'))\n                    break;\n            } while (--ofs >= 0);\n            ofs++;\n            pFilename += ofs;\n            filename_len -= ofs;\n        }\n        if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags)))\n        {\n            if (pIndex)\n                *pIndex = file_index;\n            return MZ_TRUE;\n        }\n    }\n\n    return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND);\n}\n\nmz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)\n{\n    int status = TINFL_STATUS_DONE;\n    mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail;\n    mz_zip_archive_file_stat file_stat;\n    void *pRead_buf;\n    mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];\n    mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;\n    tinfl_decompressor inflator;\n\n    if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))\n        return MZ_FALSE;\n\n    /* A directory or zero length file */\n    if ((file_stat.m_is_directory) || (!file_stat.m_comp_size))\n        return MZ_TRUE;\n\n    /* Encryption and patch files are not supported. */\n    if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG))\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);\n\n    /* This function only supports decompressing stored and deflate. */\n    if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);\n\n    /* Ensure supplied output buffer is large enough. */\n    needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size;\n    if (buf_size < needed_size)\n        return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL);\n\n    /* Read and parse the local directory entry. */\n    cur_file_ofs = file_stat.m_local_header_ofs;\n    if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n\n    if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n    cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);\n    if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n    if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))\n    {\n        /* The file is stored or the caller has requested the compressed data. */\n        if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size)\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n\n#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS\n        if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0)\n        {\n            if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)\n                return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED);\n        }\n#endif\n\n        return MZ_TRUE;\n    }\n\n    /* Decompress the file either directly from memory or from a file input buffer. */\n    tinfl_init(&inflator);\n\n    if (pZip->m_pState->m_pMem)\n    {\n        /* Read directly from the archive in memory. */\n        pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;\n        read_buf_size = read_buf_avail = file_stat.m_comp_size;\n        comp_remaining = 0;\n    }\n    else if (pUser_read_buf)\n    {\n        /* Use a user provided read buffer. */\n        if (!user_read_buf_size)\n            return MZ_FALSE;\n        pRead_buf = (mz_uint8 *)pUser_read_buf;\n        read_buf_size = user_read_buf_size;\n        read_buf_avail = 0;\n        comp_remaining = file_stat.m_comp_size;\n    }\n    else\n    {\n        /* Temporarily allocate a read buffer. */\n        read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE);\n        if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))\n            return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);\n\n        if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n\n        read_buf_avail = 0;\n        comp_remaining = file_stat.m_comp_size;\n    }\n\n    do\n    {\n        /* The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above */\n        size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs);\n        if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))\n        {\n            read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);\n            if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)\n            {\n                status = TINFL_STATUS_FAILED;\n                mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED);\n                break;\n            }\n            cur_file_ofs += read_buf_avail;\n            comp_remaining -= read_buf_avail;\n            read_buf_ofs = 0;\n        }\n        in_buf_size = (size_t)read_buf_avail;\n        status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0));\n        read_buf_avail -= in_buf_size;\n        read_buf_ofs += in_buf_size;\n        out_buf_ofs += out_buf_size;\n    } while (status == TINFL_STATUS_NEEDS_MORE_INPUT);\n\n    if (status == TINFL_STATUS_DONE)\n    {\n        /* Make sure the entire file was decompressed, and check its CRC. */\n        if (out_buf_ofs != file_stat.m_uncomp_size)\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE);\n            status = TINFL_STATUS_FAILED;\n        }\n#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS\n        else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED);\n            status = TINFL_STATUS_FAILED;\n        }\n#endif\n    }\n\n    if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf))\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);\n\n    return status == TINFL_STATUS_DONE;\n}\n\nmz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)\n{\n    mz_uint32 file_index;\n    if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index))\n        return MZ_FALSE;\n    return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size);\n}\n\nmz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags)\n{\n    return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0);\n}\n\nmz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags)\n{\n    return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0);\n}\n\nvoid *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags)\n{\n    mz_uint64 comp_size, uncomp_size, alloc_size;\n    const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);\n    void *pBuf;\n\n    if (pSize)\n        *pSize = 0;\n\n    if (!p)\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n        return NULL;\n    }\n\n    comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);\n    uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);\n\n    alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size;\n    if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);\n        return NULL;\n    }\n\n    if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size)))\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n        return NULL;\n    }\n\n    if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags))\n    {\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);\n        return NULL;\n    }\n\n    if (pSize)\n        *pSize = (size_t)alloc_size;\n    return pBuf;\n}\n\nvoid *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags)\n{\n    mz_uint32 file_index;\n    if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index))\n    {\n        if (pSize)\n            *pSize = 0;\n        return MZ_FALSE;\n    }\n    return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags);\n}\n\nmz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)\n{\n    int status = TINFL_STATUS_DONE;\n#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS\n    mz_uint file_crc32 = MZ_CRC32_INIT;\n#endif\n    mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs;\n    mz_zip_archive_file_stat file_stat;\n    void *pRead_buf = NULL;\n    void *pWrite_buf = NULL;\n    mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];\n    mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;\n\n    if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))\n        return MZ_FALSE;\n\n    /* A directory or zero length file */\n    if ((file_stat.m_is_directory) || (!file_stat.m_comp_size))\n        return MZ_TRUE;\n\n    /* Encryption and patch files are not supported. */\n    if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG))\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);\n\n    /* This function only supports decompressing stored and deflate. */\n    if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);\n\n    /* Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir) */\n    cur_file_ofs = file_stat.m_local_header_ofs;\n    if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n\n    if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n    cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);\n    if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n    /* Decompress the file either directly from memory or from a file input buffer. */\n    if (pZip->m_pState->m_pMem)\n    {\n        pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;\n        read_buf_size = read_buf_avail = file_stat.m_comp_size;\n        comp_remaining = 0;\n    }\n    else\n    {\n        read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE);\n        if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n\n        read_buf_avail = 0;\n        comp_remaining = file_stat.m_comp_size;\n    }\n\n    if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))\n    {\n        /* The file is stored or the caller has requested the compressed data. */\n        if (pZip->m_pState->m_pMem)\n        {\n            if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX))\n                return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);\n\n            if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size)\n            {\n                mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED);\n                status = TINFL_STATUS_FAILED;\n            }\n            else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))\n            {\n#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS\n                file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size);\n#endif\n            }\n\n            cur_file_ofs += file_stat.m_comp_size;\n            out_buf_ofs += file_stat.m_comp_size;\n            comp_remaining = 0;\n        }\n        else\n        {\n            while (comp_remaining)\n            {\n                read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);\n                if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)\n                {\n                    mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n                    status = TINFL_STATUS_FAILED;\n                    break;\n                }\n\n#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS\n                if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))\n                {\n                    file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail);\n                }\n#endif\n\n                if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)\n                {\n                    mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED);\n                    status = TINFL_STATUS_FAILED;\n                    break;\n                }\n\n                cur_file_ofs += read_buf_avail;\n                out_buf_ofs += read_buf_avail;\n                comp_remaining -= read_buf_avail;\n            }\n        }\n    }\n    else\n    {\n        tinfl_decompressor inflator;\n        tinfl_init(&inflator);\n\n        if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE)))\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n            status = TINFL_STATUS_FAILED;\n        }\n        else\n        {\n            do\n            {\n                mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));\n                size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));\n                if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))\n                {\n                    read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);\n                    if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)\n                    {\n                        mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n                        status = TINFL_STATUS_FAILED;\n                        break;\n                    }\n                    cur_file_ofs += read_buf_avail;\n                    comp_remaining -= read_buf_avail;\n                    read_buf_ofs = 0;\n                }\n\n                in_buf_size = (size_t)read_buf_avail;\n                status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0);\n                read_buf_avail -= in_buf_size;\n                read_buf_ofs += in_buf_size;\n\n                if (out_buf_size)\n                {\n                    if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size)\n                    {\n                        mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED);\n                        status = TINFL_STATUS_FAILED;\n                        break;\n                    }\n\n#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS\n                    file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size);\n#endif\n                    if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size)\n                    {\n                        mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED);\n                        status = TINFL_STATUS_FAILED;\n                        break;\n                    }\n                }\n            } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT));\n        }\n    }\n\n    if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))\n    {\n        /* Make sure the entire file was decompressed, and check its CRC. */\n        if (out_buf_ofs != file_stat.m_uncomp_size)\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE);\n            status = TINFL_STATUS_FAILED;\n        }\n#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS\n        else if (file_crc32 != file_stat.m_crc32)\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED);\n            status = TINFL_STATUS_FAILED;\n        }\n#endif\n    }\n\n    if (!pZip->m_pState->m_pMem)\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);\n\n    if (pWrite_buf)\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf);\n\n    return status == TINFL_STATUS_DONE;\n}\n\nmz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)\n{\n    mz_uint32 file_index;\n    if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index))\n        return MZ_FALSE;\n\n    return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags);\n}\n\nmz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags)\n{\n    mz_zip_reader_extract_iter_state *pState;\n    mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];\n    mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;\n\n    /* Argument sanity check */\n    if ((!pZip) || (!pZip->m_pState))\n        return NULL;\n\n    /* Allocate an iterator status structure */\n    pState = (mz_zip_reader_extract_iter_state*)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_reader_extract_iter_state));\n    if (!pState)\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n        return NULL;\n    }\n\n    /* Fetch file details */\n    if (!mz_zip_reader_file_stat(pZip, file_index, &pState->file_stat))\n    {\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pState);\n        return NULL;\n    }\n\n    /* Encryption and patch files are not supported. */\n    if (pState->file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG))\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pState);\n        return NULL;\n    }\n\n    /* This function only supports decompressing stored and deflate. */\n    if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (pState->file_stat.m_method != 0) && (pState->file_stat.m_method != MZ_DEFLATED))\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pState);\n        return NULL;\n    }\n\n    /* Init state - save args */\n    pState->pZip = pZip;\n    pState->flags = flags;\n\n    /* Init state - reset variables to defaults */\n    pState->status = TINFL_STATUS_DONE;\n#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS\n    pState->file_crc32 = MZ_CRC32_INIT;\n#endif\n    pState->read_buf_ofs = 0;\n    pState->out_buf_ofs = 0;\n    pState->pRead_buf = NULL;\n    pState->pWrite_buf = NULL;\n    pState->out_blk_remain = 0;\n\n    /* Read and parse the local directory entry. */\n    pState->cur_file_ofs = pState->file_stat.m_local_header_ofs;\n    if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pState);\n        return NULL;\n    }\n\n    if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pState);\n        return NULL;\n    }\n\n    pState->cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);\n    if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > pZip->m_archive_size)\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pState);\n        return NULL;\n    }\n\n    /* Decompress the file either directly from memory or from a file input buffer. */\n    if (pZip->m_pState->m_pMem)\n    {\n        pState->pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + pState->cur_file_ofs;\n        pState->read_buf_size = pState->read_buf_avail = pState->file_stat.m_comp_size;\n        pState->comp_remaining = pState->file_stat.m_comp_size;\n    }\n    else\n    {\n        if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)))\n        {\n            /* Decompression required, therefore intermediate read buffer required */\n            pState->read_buf_size = MZ_MIN(pState->file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE);\n            if (NULL == (pState->pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)pState->read_buf_size)))\n            {\n                mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n                pZip->m_pFree(pZip->m_pAlloc_opaque, pState);\n                return NULL;\n            }\n        }\n        else\n        {\n            /* Decompression not required - we will be reading directly into user buffer, no temp buf required */\n            pState->read_buf_size = 0;\n        }\n        pState->read_buf_avail = 0;\n        pState->comp_remaining = pState->file_stat.m_comp_size;\n    }\n\n    if (!((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method)))\n    {\n        /* Decompression required, init decompressor */\n        tinfl_init( &pState->inflator );\n\n        /* Allocate write buffer */\n        if (NULL == (pState->pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE)))\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n            if (pState->pRead_buf)\n                pZip->m_pFree(pZip->m_pAlloc_opaque, pState->pRead_buf);\n            pZip->m_pFree(pZip->m_pAlloc_opaque, pState);\n            return NULL;\n        }\n    }\n\n    return pState;\n}\n\nmz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags)\n{\n    mz_uint32 file_index;\n\n    /* Locate file index by name */\n    if (!mz_zip_reader_locate_file_v2(pZip, pFilename, NULL, flags, &file_index))\n        return NULL;\n\n    /* Construct iterator */\n    return mz_zip_reader_extract_iter_new(pZip, file_index, flags);\n}\n\nsize_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size)\n{\n    size_t copied_to_caller = 0;\n\n    /* Argument sanity check */\n    if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState) || (!pvBuf))\n        return 0;\n\n    if ((pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!pState->file_stat.m_method))\n    {\n        /* The file is stored or the caller has requested the compressed data, calc amount to return. */\n        copied_to_caller = (size_t)MZ_MIN( buf_size, pState->comp_remaining );\n\n        /* Zip is in memory....or requires reading from a file? */\n        if (pState->pZip->m_pState->m_pMem)\n        {\n            /* Copy data to caller's buffer */\n            memcpy( pvBuf, pState->pRead_buf, copied_to_caller );\n            pState->pRead_buf = ((mz_uint8*)pState->pRead_buf) + copied_to_caller;\n        }\n        else\n        {\n            /* Read directly into caller's buffer */\n            if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pvBuf, copied_to_caller) != copied_to_caller)\n            {\n                /* Failed to read all that was asked for, flag failure and alert user */\n                mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED);\n                pState->status = TINFL_STATUS_FAILED;\n                copied_to_caller = 0;\n            }\n        }\n\n#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS\n        /* Compute CRC if not returning compressed data only */\n        if (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA))\n            pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, (const mz_uint8 *)pvBuf, copied_to_caller);\n#endif\n\n        /* Advance offsets, dec counters */\n        pState->cur_file_ofs += copied_to_caller;\n        pState->out_buf_ofs += copied_to_caller;\n        pState->comp_remaining -= copied_to_caller;\n    }\n    else\n    {\n        do\n        {\n            /* Calc ptr to write buffer - given current output pos and block size */\n            mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pState->pWrite_buf + (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));\n\n            /* Calc max output size - given current output pos and block size */\n            size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (pState->out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));\n\n            if (!pState->out_blk_remain)\n            {\n                /* Read more data from file if none available (and reading from file) */\n                if ((!pState->read_buf_avail) && (!pState->pZip->m_pState->m_pMem))\n                {\n                    /* Calc read size */\n                    pState->read_buf_avail = MZ_MIN(pState->read_buf_size, pState->comp_remaining);\n                    if (pState->pZip->m_pRead(pState->pZip->m_pIO_opaque, pState->cur_file_ofs, pState->pRead_buf, (size_t)pState->read_buf_avail) != pState->read_buf_avail)\n                    {\n                        mz_zip_set_error(pState->pZip, MZ_ZIP_FILE_READ_FAILED);\n                        pState->status = TINFL_STATUS_FAILED;\n                        break;\n                    }\n\n                    /* Advance offsets, dec counters */\n                    pState->cur_file_ofs += pState->read_buf_avail;\n                    pState->comp_remaining -= pState->read_buf_avail;\n                    pState->read_buf_ofs = 0;\n                }\n\n                /* Perform decompression */\n                in_buf_size = (size_t)pState->read_buf_avail;\n                pState->status = tinfl_decompress(&pState->inflator, (const mz_uint8 *)pState->pRead_buf + pState->read_buf_ofs, &in_buf_size, (mz_uint8 *)pState->pWrite_buf, pWrite_buf_cur, &out_buf_size, pState->comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0);\n                pState->read_buf_avail -= in_buf_size;\n                pState->read_buf_ofs += in_buf_size;\n\n                /* Update current output block size remaining */\n                pState->out_blk_remain = out_buf_size;\n            }\n\n            if (pState->out_blk_remain)\n            {\n                /* Calc amount to return. */\n                size_t to_copy = MZ_MIN( (buf_size - copied_to_caller), pState->out_blk_remain );\n\n                /* Copy data to caller's buffer */\n                memcpy( (uint8_t*)pvBuf + copied_to_caller, pWrite_buf_cur, to_copy );\n\n#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS\n                /* Perform CRC */\n                pState->file_crc32 = (mz_uint32)mz_crc32(pState->file_crc32, pWrite_buf_cur, to_copy);\n#endif\n\n                /* Decrement data consumed from block */\n                pState->out_blk_remain -= to_copy;\n\n                /* Inc output offset, while performing sanity check */\n                if ((pState->out_buf_ofs += to_copy) > pState->file_stat.m_uncomp_size)\n                {\n                    mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED);\n                    pState->status = TINFL_STATUS_FAILED;\n                    break;\n                }\n\n                /* Increment counter of data copied to caller */\n                copied_to_caller += to_copy;\n            }\n        } while ( (copied_to_caller < buf_size) && ((pState->status == TINFL_STATUS_NEEDS_MORE_INPUT) || (pState->status == TINFL_STATUS_HAS_MORE_OUTPUT)) );\n    }\n\n    /* Return how many bytes were copied into user buffer */\n    return copied_to_caller;\n}\n\nmz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState)\n{\n    int status;\n\n    /* Argument sanity check */\n    if ((!pState) || (!pState->pZip) || (!pState->pZip->m_pState))\n        return MZ_FALSE;\n\n    /* Was decompression completed and requested? */\n    if ((pState->status == TINFL_STATUS_DONE) && (!(pState->flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))\n    {\n        /* Make sure the entire file was decompressed, and check its CRC. */\n        if (pState->out_buf_ofs != pState->file_stat.m_uncomp_size)\n        {\n            mz_zip_set_error(pState->pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE);\n            pState->status = TINFL_STATUS_FAILED;\n        }\n#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS\n        else if (pState->file_crc32 != pState->file_stat.m_crc32)\n        {\n            mz_zip_set_error(pState->pZip, MZ_ZIP_DECOMPRESSION_FAILED);\n            pState->status = TINFL_STATUS_FAILED;\n        }\n#endif\n    }\n\n    /* Free buffers */\n    if (!pState->pZip->m_pState->m_pMem)\n        pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pRead_buf);\n    if (pState->pWrite_buf)\n        pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState->pWrite_buf);\n\n    /* Save status */\n    status = pState->status;\n\n    /* Free context */\n    pState->pZip->m_pFree(pState->pZip->m_pAlloc_opaque, pState);\n\n    return status == TINFL_STATUS_DONE;\n}\n\n#ifndef MINIZ_NO_STDIO\nstatic size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n)\n{\n    (void)ofs;\n\n    return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque);\n}\n\nmz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags)\n{\n    mz_bool status;\n    mz_zip_archive_file_stat file_stat;\n    MZ_FILE *pFile;\n\n    if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))\n        return MZ_FALSE;\n\n    if ((file_stat.m_is_directory) || (!file_stat.m_is_supported))\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);\n\n    pFile = MZ_FOPEN(pDst_filename, \"wb\");\n    if (!pFile)\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);\n\n    status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);\n\n    if (MZ_FCLOSE(pFile) == EOF)\n    {\n        if (status)\n            mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED);\n\n        status = MZ_FALSE;\n    }\n\n#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO)\n    if (status)\n        mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time);\n#endif\n\n    return status;\n}\n\nmz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags)\n{\n    mz_uint32 file_index;\n    if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index))\n        return MZ_FALSE;\n\n    return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags);\n}\n\nmz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags)\n{\n    mz_zip_archive_file_stat file_stat;\n\n    if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat))\n        return MZ_FALSE;\n\n    if ((file_stat.m_is_directory) || (!file_stat.m_is_supported))\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);\n\n    return mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);\n}\n\nmz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags)\n{\n    mz_uint32 file_index;\n    if (!mz_zip_reader_locate_file_v2(pZip, pArchive_filename, NULL, flags, &file_index))\n        return MZ_FALSE;\n\n    return mz_zip_reader_extract_to_cfile(pZip, file_index, pFile, flags);\n}\n#endif /* #ifndef MINIZ_NO_STDIO */\n\nstatic size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)\n{\n    mz_uint32 *p = (mz_uint32 *)pOpaque;\n    (void)file_ofs;\n    *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n);\n    return n;\n}\n\nmz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags)\n{\n    mz_zip_archive_file_stat file_stat;\n    mz_zip_internal_state *pState;\n    const mz_uint8 *pCentral_dir_header;\n    mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE;\n    mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE;\n    mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];\n    mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;\n    mz_uint64 local_header_ofs = 0;\n    mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32;\n    mz_uint64 local_header_comp_size, local_header_uncomp_size;\n    mz_uint32 uncomp_crc32 = MZ_CRC32_INIT;\n    mz_bool has_data_descriptor;\n    mz_uint32 local_header_bit_flags;\n\n    mz_zip_array file_data_array;\n    mz_zip_array_init(&file_data_array, 1);\n\n    if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (file_index > pZip->m_total_files)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    pState = pZip->m_pState;\n\n    pCentral_dir_header = mz_zip_get_cdh(pZip, file_index);\n\n    if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir))\n        return MZ_FALSE;\n\n    /* A directory or zero length file */\n    if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size))\n        return MZ_TRUE;\n\n    /* Encryption and patch files are not supported. */\n    if (file_stat.m_is_encrypted)\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);\n\n    /* This function only supports stored and deflate. */\n    if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);\n\n    if (!file_stat.m_is_supported)\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);\n\n    /* Read and parse the local directory entry. */\n    local_header_ofs = file_stat.m_local_header_ofs;\n    if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n\n    if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n    local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS);\n    local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);\n    local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS);\n    local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS);\n    local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS);\n    local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);\n    has_data_descriptor = (local_header_bit_flags & 8) != 0;\n\n    if (local_header_filename_len != strlen(file_stat.m_filename))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n    if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n    if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE))\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n        goto handle_failure;\n    }\n\n    if (local_header_filename_len)\n    {\n        if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len)\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n            goto handle_failure;\n        }\n\n        /* I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation. */\n        if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0)\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);\n            goto handle_failure;\n        }\n    }\n\n    if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX)))\n    {\n        mz_uint32 extra_size_remaining = local_header_extra_len;\n        const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p;\n\n        if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len)\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n            goto handle_failure;\n        }\n\n        do\n        {\n            mz_uint32 field_id, field_data_size, field_total_size;\n\n            if (extra_size_remaining < (sizeof(mz_uint16) * 2))\n            {\n                mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n                goto handle_failure;\n            }\n\n            field_id = MZ_READ_LE16(pExtra_data);\n            field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));\n            field_total_size = field_data_size + sizeof(mz_uint16) * 2;\n\n            if (field_total_size > extra_size_remaining)\n            {\n                mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n                goto handle_failure;\n            }\n\n            if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)\n            {\n                const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32);\n\n                if (field_data_size < sizeof(mz_uint64) * 2)\n                {\n                    mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n                    goto handle_failure;\n                }\n\n                local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data);\n                local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64));\n\n                found_zip64_ext_data_in_ldir = MZ_TRUE;\n                break;\n            }\n\n            pExtra_data += field_total_size;\n            extra_size_remaining -= field_total_size;\n        } while (extra_size_remaining);\n    }\n\n    /* TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip) */\n    /* I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors */\n    if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32))\n    {\n        mz_uint8 descriptor_buf[32];\n        mz_bool has_id;\n        const mz_uint8 *pSrc;\n        mz_uint32 file_crc32;\n        mz_uint64 comp_size = 0, uncomp_size = 0;\n\n        mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4;\n\n        if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s))\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n            goto handle_failure;\n        }\n\n        has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID);\n        pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf;\n\n        file_crc32 = MZ_READ_LE32(pSrc);\n\n        if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir))\n        {\n            comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32));\n            uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64));\n        }\n        else\n        {\n            comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32));\n            uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32));\n        }\n\n        if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size))\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);\n            goto handle_failure;\n        }\n    }\n    else\n    {\n        if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size))\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);\n            goto handle_failure;\n        }\n    }\n\n    mz_zip_array_clear(pZip, &file_data_array);\n\n    if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0)\n    {\n        if (!mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0))\n            return MZ_FALSE;\n\n        /* 1 more check to be sure, although the extract checks too. */\n        if (uncomp_crc32 != file_stat.m_crc32)\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);\n            return MZ_FALSE;\n        }\n    }\n\n    return MZ_TRUE;\n\nhandle_failure:\n    mz_zip_array_clear(pZip, &file_data_array);\n    return MZ_FALSE;\n}\n\nmz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags)\n{\n    mz_zip_internal_state *pState;\n    uint32_t i;\n\n    if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    pState = pZip->m_pState;\n\n    /* Basic sanity checks */\n    if (!pState->m_zip64)\n    {\n        if (pZip->m_total_files > MZ_UINT16_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);\n\n        if (pZip->m_archive_size > MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);\n    }\n    else\n    {\n        if (pZip->m_total_files >= MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);\n\n        if (pState->m_central_dir.m_size >= MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);\n    }\n\n    for (i = 0; i < pZip->m_total_files; i++)\n    {\n        if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags)\n        {\n            mz_uint32 found_index;\n            mz_zip_archive_file_stat stat;\n\n            if (!mz_zip_reader_file_stat(pZip, i, &stat))\n                return MZ_FALSE;\n\n            if (!mz_zip_reader_locate_file_v2(pZip, stat.m_filename, NULL, 0, &found_index))\n                return MZ_FALSE;\n\n            /* This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user) */\n            if (found_index != i)\n                return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);\n        }\n\n        if (!mz_zip_validate_file(pZip, i, flags))\n            return MZ_FALSE;\n    }\n\n    return MZ_TRUE;\n}\n\nmz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr)\n{\n    mz_bool success = MZ_TRUE;\n    mz_zip_archive zip;\n    mz_zip_error actual_err = MZ_ZIP_NO_ERROR;\n\n    if ((!pMem) || (!size))\n    {\n        if (pErr)\n            *pErr = MZ_ZIP_INVALID_PARAMETER;\n        return MZ_FALSE;\n    }\n\n    mz_zip_zero_struct(&zip);\n\n    if (!mz_zip_reader_init_mem(&zip, pMem, size, flags))\n    {\n        if (pErr)\n            *pErr = zip.m_last_error;\n        return MZ_FALSE;\n    }\n\n    if (!mz_zip_validate_archive(&zip, flags))\n    {\n        actual_err = zip.m_last_error;\n        success = MZ_FALSE;\n    }\n\n    if (!mz_zip_reader_end_internal(&zip, success))\n    {\n        if (!actual_err)\n            actual_err = zip.m_last_error;\n        success = MZ_FALSE;\n    }\n\n    if (pErr)\n        *pErr = actual_err;\n\n    return success;\n}\n\n#ifndef MINIZ_NO_STDIO\nmz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr)\n{\n    mz_bool success = MZ_TRUE;\n    mz_zip_archive zip;\n    mz_zip_error actual_err = MZ_ZIP_NO_ERROR;\n\n    if (!pFilename)\n    {\n        if (pErr)\n            *pErr = MZ_ZIP_INVALID_PARAMETER;\n        return MZ_FALSE;\n    }\n\n    mz_zip_zero_struct(&zip);\n\n    if (!mz_zip_reader_init_file_v2(&zip, pFilename, flags, 0, 0))\n    {\n        if (pErr)\n            *pErr = zip.m_last_error;\n        return MZ_FALSE;\n    }\n\n    if (!mz_zip_validate_archive(&zip, flags))\n    {\n        actual_err = zip.m_last_error;\n        success = MZ_FALSE;\n    }\n\n    if (!mz_zip_reader_end_internal(&zip, success))\n    {\n        if (!actual_err)\n            actual_err = zip.m_last_error;\n        success = MZ_FALSE;\n    }\n\n    if (pErr)\n        *pErr = actual_err;\n\n    return success;\n}\n#endif /* #ifndef MINIZ_NO_STDIO */\n\n/* ------------------- .ZIP archive writing */\n\n#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS\n\nstatic MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v)\n{\n    p[0] = (mz_uint8)v;\n    p[1] = (mz_uint8)(v >> 8);\n}\nstatic MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v)\n{\n    p[0] = (mz_uint8)v;\n    p[1] = (mz_uint8)(v >> 8);\n    p[2] = (mz_uint8)(v >> 16);\n    p[3] = (mz_uint8)(v >> 24);\n}\nstatic MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v)\n{\n    mz_write_le32(p, (mz_uint32)v);\n    mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32));\n}\n\n#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v))\n#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v))\n#define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v))\n\nstatic size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)\n{\n    mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;\n    mz_zip_internal_state *pState = pZip->m_pState;\n    mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size);\n\n    if (!n)\n        return 0;\n\n    /* An allocation this big is likely to just fail on 32-bit systems, so don't even go there. */\n    if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);\n        return 0;\n    }\n\n    if (new_size > pState->m_mem_capacity)\n    {\n        void *pNew_block;\n        size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity);\n\n        while (new_capacity < new_size)\n            new_capacity *= 2;\n\n        if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity)))\n        {\n            mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n            return 0;\n        }\n\n        pState->m_pMem = pNew_block;\n        pState->m_mem_capacity = new_capacity;\n    }\n    memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n);\n    pState->m_mem_size = (size_t)new_size;\n    return n;\n}\n\nstatic mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error)\n{\n    mz_zip_internal_state *pState;\n    mz_bool status = MZ_TRUE;\n\n    if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)))\n    {\n        if (set_last_error)\n            mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n        return MZ_FALSE;\n    }\n\n    pState = pZip->m_pState;\n    pZip->m_pState = NULL;\n    mz_zip_array_clear(pZip, &pState->m_central_dir);\n    mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);\n    mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);\n\n#ifndef MINIZ_NO_STDIO\n    if (pState->m_pFile)\n    {\n        if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE)\n        {\n            if (MZ_FCLOSE(pState->m_pFile) == EOF)\n            {\n                if (set_last_error)\n                    mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED);\n                status = MZ_FALSE;\n            }\n        }\n\n        pState->m_pFile = NULL;\n    }\n#endif /* #ifndef MINIZ_NO_STDIO */\n\n    if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem))\n    {\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem);\n        pState->m_pMem = NULL;\n    }\n\n    pZip->m_pFree(pZip->m_pAlloc_opaque, pState);\n    pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;\n    return status;\n}\n\nmz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags)\n{\n    mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0;\n\n    if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)\n    {\n        if (!pZip->m_pRead)\n            return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n    }\n\n    if (pZip->m_file_offset_alignment)\n    {\n        /* Ensure user specified file offset alignment is a power of 2. */\n        if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1))\n            return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n    }\n\n    if (!pZip->m_pAlloc)\n        pZip->m_pAlloc = miniz_def_alloc_func;\n    if (!pZip->m_pFree)\n        pZip->m_pFree = miniz_def_free_func;\n    if (!pZip->m_pRealloc)\n        pZip->m_pRealloc = miniz_def_realloc_func;\n\n    pZip->m_archive_size = existing_size;\n    pZip->m_central_directory_file_ofs = 0;\n    pZip->m_total_files = 0;\n\n    if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))\n        return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n\n    memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));\n\n    MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));\n    MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));\n    MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));\n\n    pZip->m_pState->m_zip64 = zip64;\n    pZip->m_pState->m_zip64_has_extended_info_fields = zip64;\n\n    pZip->m_zip_type = MZ_ZIP_TYPE_USER;\n    pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;\n\n    return MZ_TRUE;\n}\n\nmz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size)\n{\n    return mz_zip_writer_init_v2(pZip, existing_size, 0);\n}\n\nmz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags)\n{\n    pZip->m_pWrite = mz_zip_heap_write_func;\n    pZip->m_pNeeds_keepalive = NULL;\n\n    if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)\n        pZip->m_pRead = mz_zip_mem_read_func;\n\n    pZip->m_pIO_opaque = pZip;\n\n    if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags))\n        return MZ_FALSE;\n\n    pZip->m_zip_type = MZ_ZIP_TYPE_HEAP;\n\n    if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning)))\n    {\n        if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size)))\n        {\n            mz_zip_writer_end_internal(pZip, MZ_FALSE);\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n        }\n        pZip->m_pState->m_mem_capacity = initial_allocation_size;\n    }\n\n    return MZ_TRUE;\n}\n\nmz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size)\n{\n    return mz_zip_writer_init_heap_v2(pZip, size_to_reserve_at_beginning, initial_allocation_size, 0);\n}\n\n#ifndef MINIZ_NO_STDIO\nstatic size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)\n{\n    mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;\n    mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);\n\n    file_ofs += pZip->m_pState->m_file_archive_start_ofs;\n\n    if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))\n    {\n        mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);\n        return 0;\n    }\n\n    return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile);\n}\n\nmz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning)\n{\n    return mz_zip_writer_init_file_v2(pZip, pFilename, size_to_reserve_at_beginning, 0);\n}\n\nmz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags)\n{\n    MZ_FILE *pFile;\n\n    pZip->m_pWrite = mz_zip_file_write_func;\n    pZip->m_pNeeds_keepalive = NULL;\n\n    if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)\n        pZip->m_pRead = mz_zip_file_read_func;\n\n    pZip->m_pIO_opaque = pZip;\n\n    if (!mz_zip_writer_init_v2(pZip, size_to_reserve_at_beginning, flags))\n        return MZ_FALSE;\n\n    if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? \"w+b\" : \"wb\")))\n    {\n        mz_zip_writer_end(pZip);\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);\n    }\n\n    pZip->m_pState->m_pFile = pFile;\n    pZip->m_zip_type = MZ_ZIP_TYPE_FILE;\n\n    if (size_to_reserve_at_beginning)\n    {\n        mz_uint64 cur_ofs = 0;\n        char buf[4096];\n\n        MZ_CLEAR_OBJ(buf);\n\n        do\n        {\n            size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning);\n            if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n)\n            {\n                mz_zip_writer_end(pZip);\n                return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n            }\n            cur_ofs += n;\n            size_to_reserve_at_beginning -= n;\n        } while (size_to_reserve_at_beginning);\n    }\n\n    return MZ_TRUE;\n}\n\nmz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags)\n{\n    pZip->m_pWrite = mz_zip_file_write_func;\n    pZip->m_pNeeds_keepalive = NULL;\n\n    if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)\n        pZip->m_pRead = mz_zip_file_read_func;\n\n    pZip->m_pIO_opaque = pZip;\n\n    if (!mz_zip_writer_init_v2(pZip, 0, flags))\n        return MZ_FALSE;\n\n    pZip->m_pState->m_pFile = pFile;\n    pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);\n    pZip->m_zip_type = MZ_ZIP_TYPE_CFILE;\n\n    return MZ_TRUE;\n}\n#endif /* #ifndef MINIZ_NO_STDIO */\n\nmz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags)\n{\n    mz_zip_internal_state *pState;\n\n    if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (flags & MZ_ZIP_FLAG_WRITE_ZIP64)\n    {\n        /* We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?) */\n        if (!pZip->m_pState->m_zip64)\n            return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n    }\n\n    /* No sense in trying to write to an archive that's already at the support max size */\n    if (pZip->m_pState->m_zip64)\n    {\n        if (pZip->m_total_files == MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);\n    }\n    else\n    {\n        if (pZip->m_total_files == MZ_UINT16_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);\n\n        if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);\n    }\n\n    pState = pZip->m_pState;\n\n    if (pState->m_pFile)\n    {\n#ifdef MINIZ_NO_STDIO\n        (void)pFilename;\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n#else\n        if (pZip->m_pIO_opaque != pZip)\n            return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n        if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE)\n        {\n            if (!pFilename)\n                return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n            /* Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable. */\n            if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, \"r+b\", pState->m_pFile)))\n            {\n                /* The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. */\n                mz_zip_reader_end_internal(pZip, MZ_FALSE);\n                return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);\n            }\n        }\n\n        pZip->m_pWrite = mz_zip_file_write_func;\n        pZip->m_pNeeds_keepalive = NULL;\n#endif /* #ifdef MINIZ_NO_STDIO */\n    }\n    else if (pState->m_pMem)\n    {\n        /* Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. */\n        if (pZip->m_pIO_opaque != pZip)\n            return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n        pState->m_mem_capacity = pState->m_mem_size;\n        pZip->m_pWrite = mz_zip_heap_write_func;\n        pZip->m_pNeeds_keepalive = NULL;\n    }\n    /* Archive is being read via a user provided read function - make sure the user has specified a write function too. */\n    else if (!pZip->m_pWrite)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    /* Start writing new files at the archive's current central directory location. */\n    /* TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer. */\n    pZip->m_archive_size = pZip->m_central_directory_file_ofs;\n    pZip->m_central_directory_file_ofs = 0;\n\n    /* Clear the sorted central dir offsets, they aren't useful or maintained now. */\n    /* Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow. */\n    /* TODO: We could easily maintain the sorted central directory offsets. */\n    mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets);\n\n    pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;\n\n    return MZ_TRUE;\n}\n\nmz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename)\n{\n    return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0);\n}\n\n/* TODO: pArchive_name is a terrible name here! */\nmz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags)\n{\n    return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0);\n}\n\ntypedef struct\n{\n    mz_zip_archive *m_pZip;\n    mz_uint64 m_cur_archive_file_ofs;\n    mz_uint64 m_comp_size;\n} mz_zip_writer_add_state;\n\nstatic mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser)\n{\n    mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser;\n    if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len)\n        return MZ_FALSE;\n\n    pState->m_cur_archive_file_ofs += len;\n    pState->m_comp_size += len;\n    return MZ_TRUE;\n}\n\n#define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2)\n#define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3)\nstatic mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs)\n{\n    mz_uint8 *pDst = pBuf;\n    mz_uint32 field_size = 0;\n\n    MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID);\n    MZ_WRITE_LE16(pDst + 2, 0);\n    pDst += sizeof(mz_uint16) * 2;\n\n    if (pUncomp_size)\n    {\n        MZ_WRITE_LE64(pDst, *pUncomp_size);\n        pDst += sizeof(mz_uint64);\n        field_size += sizeof(mz_uint64);\n    }\n\n    if (pComp_size)\n    {\n        MZ_WRITE_LE64(pDst, *pComp_size);\n        pDst += sizeof(mz_uint64);\n        field_size += sizeof(mz_uint64);\n    }\n\n    if (pLocal_header_ofs)\n    {\n        MZ_WRITE_LE64(pDst, *pLocal_header_ofs);\n        pDst += sizeof(mz_uint64);\n        field_size += sizeof(mz_uint64);\n    }\n\n    MZ_WRITE_LE16(pBuf + 2, field_size);\n\n    return (mz_uint32)(pDst - pBuf);\n}\n\nstatic mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date)\n{\n    (void)pZip;\n    memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE);\n    MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date);\n    MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32);\n    MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX));\n    MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX));\n    MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size);\n    return MZ_TRUE;\n}\n\nstatic mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst,\n                                                       mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size,\n                                                       mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32,\n                                                       mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date,\n                                                       mz_uint64 local_header_ofs, mz_uint32 ext_attributes)\n{\n    (void)pZip;\n    memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);\n    MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date);\n    MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32);\n    MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX));\n    MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX));\n    MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size);\n    MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size);\n    MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes);\n    MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX));\n    return MZ_TRUE;\n}\n\nstatic mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size,\n                                                const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size,\n                                                mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32,\n                                                mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date,\n                                                mz_uint64 local_header_ofs, mz_uint32 ext_attributes,\n                                                const char *user_extra_data, mz_uint user_extra_data_len)\n{\n    mz_zip_internal_state *pState = pZip->m_pState;\n    mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size;\n    size_t orig_central_dir_size = pState->m_central_dir.m_size;\n    mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];\n\n    if (!pZip->m_pState->m_zip64)\n    {\n        if (local_header_ofs > 0xFFFFFFFF)\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);\n    }\n\n    /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */\n    if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + user_extra_data_len + comment_size) >= MZ_UINT32_MAX)\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);\n\n    if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, (mz_uint16)(extra_size + user_extra_data_len), comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes))\n        return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);\n\n    if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) ||\n        (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) ||\n        (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) ||\n        (!mz_zip_array_push_back(pZip, &pState->m_central_dir, user_extra_data, user_extra_data_len)) ||\n        (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) ||\n        (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &central_dir_ofs, 1)))\n    {\n        /* Try to resize the central directory array back into its original state. */\n        mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);\n        return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n    }\n\n    return MZ_TRUE;\n}\n\nstatic mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name)\n{\n    /* Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. */\n    if (*pArchive_name == '/')\n        return MZ_FALSE;\n\n    /* Making sure the name does not contain drive letters or DOS style backward slashes is the responsibility of the program using miniz*/\n\n    return MZ_TRUE;\n}\n\nstatic mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip)\n{\n    mz_uint32 n;\n    if (!pZip->m_file_offset_alignment)\n        return 0;\n    n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1));\n    return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1));\n}\n\nstatic mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n)\n{\n    char buf[4096];\n    memset(buf, 0, MZ_MIN(sizeof(buf), n));\n    while (n)\n    {\n        mz_uint32 s = MZ_MIN(sizeof(buf), n);\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s)\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n        cur_file_ofs += s;\n        n -= s;\n    }\n    return MZ_TRUE;\n}\n\nmz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,\n                                 mz_uint64 uncomp_size, mz_uint32 uncomp_crc32)\n{\n    return mz_zip_writer_add_mem_ex_v2(pZip, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, uncomp_size, uncomp_crc32, NULL, NULL, 0, NULL, 0);\n}\n\nmz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size,\n                                    mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified,\n                                    const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len)\n{\n    mz_uint16 method = 0, dos_time = 0, dos_date = 0;\n    mz_uint level, ext_attributes = 0, num_alignment_padding_bytes;\n    mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0;\n    size_t archive_name_size;\n    mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];\n    tdefl_compressor *pComp = NULL;\n    mz_bool store_data_uncompressed;\n    mz_zip_internal_state *pState;\n    mz_uint8 *pExtra_data = NULL;\n    mz_uint32 extra_size = 0;\n    mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE];\n    mz_uint16 bit_flags = 0;\n\n    if ((int)level_and_flags < 0)\n        level_and_flags = MZ_DEFAULT_LEVEL;\n\n    if (uncomp_size || (buf_size && !(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))\n        bit_flags |= MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR;\n\n    if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME))\n        bit_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8;\n\n    level = level_and_flags & 0xF;\n    store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA));\n\n    if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    pState = pZip->m_pState;\n\n    if (pState->m_zip64)\n    {\n        if (pZip->m_total_files == MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);\n    }\n    else\n    {\n        if (pZip->m_total_files == MZ_UINT16_MAX)\n        {\n            pState->m_zip64 = MZ_TRUE;\n            /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */\n        }\n        if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF))\n        {\n            pState->m_zip64 = MZ_TRUE;\n            /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */\n        }\n    }\n\n    if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (!mz_zip_writer_validate_archive_name(pArchive_name))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);\n\n#ifndef MINIZ_NO_TIME\n    if (last_modified != NULL)\n    {\n        mz_zip_time_t_to_dos_time(*last_modified, &dos_time, &dos_date);\n    }\n    else\n    {\n        MZ_TIME_T cur_time;\n        time(&cur_time);\n        mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date);\n    }\n#endif /* #ifndef MINIZ_NO_TIME */\n\n\tif (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))\n\t{\n\t\tuncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size);\n\t\tuncomp_size = buf_size;\n\t\tif (uncomp_size <= 3)\n\t\t{\n\t\t\tlevel = 0;\n\t\t\tstore_data_uncompressed = MZ_TRUE;\n\t\t}\n\t}\n\n    archive_name_size = strlen(pArchive_name);\n    if (archive_name_size > MZ_UINT16_MAX)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);\n\n    num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);\n\n    /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */\n    if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX)\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);\n\n    if (!pState->m_zip64)\n    {\n        /* Bail early if the archive would obviously become too large */\n        if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size\n\t\t\t+ MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + user_extra_data_len +\n\t\t\tpState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + user_extra_data_central_len\n\t\t\t+ MZ_ZIP_DATA_DESCRIPTER_SIZE32) > 0xFFFFFFFF)\n        {\n            pState->m_zip64 = MZ_TRUE;\n            /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */\n        }\n    }\n\n    if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/'))\n    {\n        /* Set DOS Subdirectory attribute bit. */\n        ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG;\n\n        /* Subdirectories cannot contain data. */\n        if ((buf_size) || (uncomp_size))\n            return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n    }\n\n    /* Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) */\n    if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1)))\n        return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n\n    if ((!store_data_uncompressed) && (buf_size))\n    {\n        if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor))))\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n    }\n\n    if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes))\n    {\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);\n        return MZ_FALSE;\n    }\n\n    local_dir_header_ofs += num_alignment_padding_bytes;\n    if (pZip->m_file_offset_alignment)\n    {\n        MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0);\n    }\n    cur_archive_file_ofs += num_alignment_padding_bytes;\n\n    MZ_CLEAR_OBJ(local_dir_header);\n\n    if (!store_data_uncompressed || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))\n    {\n        method = MZ_DEFLATED;\n    }\n\n    if (pState->m_zip64)\n    {\n        if (uncomp_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX)\n        {\n            pExtra_data = extra_data;\n            extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL,\n                                                               (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);\n        }\n\n        if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, bit_flags, dos_time, dos_date))\n            return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n        cur_archive_file_ofs += sizeof(local_dir_header);\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)\n        {\n            pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n        }\n        cur_archive_file_ofs += archive_name_size;\n\n        if (pExtra_data != NULL)\n        {\n            if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size)\n                return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n            cur_archive_file_ofs += extra_size;\n        }\n    }\n    else\n    {\n        if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX))\n            return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);\n        if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, bit_flags, dos_time, dos_date))\n            return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n        cur_archive_file_ofs += sizeof(local_dir_header);\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)\n        {\n            pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n        }\n        cur_archive_file_ofs += archive_name_size;\n    }\n\n\tif (user_extra_data_len > 0)\n\t{\n\t\tif (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len)\n\t\t\treturn mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n\t\tcur_archive_file_ofs += user_extra_data_len;\n\t}\n\n    if (store_data_uncompressed)\n    {\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size)\n        {\n            pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n        }\n\n        cur_archive_file_ofs += buf_size;\n        comp_size = buf_size;\n    }\n    else if (buf_size)\n    {\n        mz_zip_writer_add_state state;\n\n        state.m_pZip = pZip;\n        state.m_cur_archive_file_ofs = cur_archive_file_ofs;\n        state.m_comp_size = 0;\n\n        if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) ||\n            (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE))\n        {\n            pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);\n            return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED);\n        }\n\n        comp_size = state.m_comp_size;\n        cur_archive_file_ofs = state.m_cur_archive_file_ofs;\n    }\n\n    pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);\n    pComp = NULL;\n\n    if (uncomp_size)\n    {\n        mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64];\n        mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32;\n\n        MZ_ASSERT(bit_flags & MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR);\n\n        MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID);\n        MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32);\n        if (pExtra_data == NULL)\n        {\n            if (comp_size > MZ_UINT32_MAX)\n                return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);\n\n            MZ_WRITE_LE32(local_dir_footer + 8, comp_size);\n            MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size);\n        }\n        else\n        {\n            MZ_WRITE_LE64(local_dir_footer + 8, comp_size);\n            MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size);\n            local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64;\n        }\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size)\n            return MZ_FALSE;\n\n        cur_archive_file_ofs += local_dir_footer_size;\n    }\n\n    if (pExtra_data != NULL)\n    {\n        extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL,\n                                                           (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);\n    }\n\n    if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment,\n                                          comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes,\n                                          user_extra_data_central, user_extra_data_central_len))\n        return MZ_FALSE;\n\n    pZip->m_total_files++;\n    pZip->m_archive_size = cur_archive_file_ofs;\n\n    return MZ_TRUE;\n}\n\nmz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,\n                                const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len)\n{\n    mz_uint16 gen_flags = (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE) ? 0 : MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR;\n    mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes;\n    mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0;\n    mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0;\n    size_t archive_name_size;\n    mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];\n    mz_uint8 *pExtra_data = NULL;\n    mz_uint32 extra_size = 0;\n    mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE];\n    mz_zip_internal_state *pState;\n    mz_uint64 file_ofs = 0, cur_archive_header_file_ofs;\n\n    if (!(level_and_flags & MZ_ZIP_FLAG_ASCII_FILENAME))\n        gen_flags |= MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8;\n\n    if ((int)level_and_flags < 0)\n        level_and_flags = MZ_DEFAULT_LEVEL;\n    level = level_and_flags & 0xF;\n\n    /* Sanity checks */\n    if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    pState = pZip->m_pState;\n\n    if ((!pState->m_zip64) && (max_size > MZ_UINT32_MAX))\n    {\n        /* Source file is too large for non-zip64 */\n        /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */\n        pState->m_zip64 = MZ_TRUE;\n    }\n\n    /* We could support this, but why? */\n    if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (!mz_zip_writer_validate_archive_name(pArchive_name))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);\n\n    if (pState->m_zip64)\n    {\n        if (pZip->m_total_files == MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);\n    }\n    else\n    {\n        if (pZip->m_total_files == MZ_UINT16_MAX)\n        {\n            pState->m_zip64 = MZ_TRUE;\n            /*return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); */\n        }\n    }\n\n    archive_name_size = strlen(pArchive_name);\n    if (archive_name_size > MZ_UINT16_MAX)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);\n\n    num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);\n\n    /* miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet */\n    if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX)\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);\n\n    if (!pState->m_zip64)\n    {\n        /* Bail early if the archive would obviously become too large */\n        if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE\n\t\t\t+ archive_name_size + comment_size + user_extra_data_len + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 1024\n\t\t\t+ MZ_ZIP_DATA_DESCRIPTER_SIZE32 + user_extra_data_central_len) > 0xFFFFFFFF)\n        {\n            pState->m_zip64 = MZ_TRUE;\n            /*return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE); */\n        }\n    }\n\n#ifndef MINIZ_NO_TIME\n    if (pFile_time)\n    {\n        mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date);\n    }\n#endif\n\n    if (max_size <= 3)\n        level = 0;\n\n    if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes))\n    {\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n    }\n\n    cur_archive_file_ofs += num_alignment_padding_bytes;\n    local_dir_header_ofs = cur_archive_file_ofs;\n\n    if (pZip->m_file_offset_alignment)\n    {\n        MZ_ASSERT((cur_archive_file_ofs & (pZip->m_file_offset_alignment - 1)) == 0);\n    }\n\n    if (max_size && level)\n    {\n        method = MZ_DEFLATED;\n    }\n\n    MZ_CLEAR_OBJ(local_dir_header);\n    if (pState->m_zip64)\n    {\n        if (max_size >= MZ_UINT32_MAX || local_dir_header_ofs >= MZ_UINT32_MAX)\n        {\n            pExtra_data = extra_data;\n            if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)\n                extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL,\n                                                               (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL,\n                                                                (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);\n            else\n                extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, NULL,\n                                                                   NULL,\n                                                                   (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);\n        }\n\n        if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len), 0, 0, 0, method, gen_flags, dos_time, dos_date))\n            return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n        cur_archive_file_ofs += sizeof(local_dir_header);\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)\n        {\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n        }\n\n        cur_archive_file_ofs += archive_name_size;\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, extra_data, extra_size) != extra_size)\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n        cur_archive_file_ofs += extra_size;\n    }\n    else\n    {\n        if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX))\n            return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);\n        if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, (mz_uint16)user_extra_data_len, 0, 0, 0, method, gen_flags, dos_time, dos_date))\n            return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n        cur_archive_file_ofs += sizeof(local_dir_header);\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)\n        {\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n        }\n\n        cur_archive_file_ofs += archive_name_size;\n    }\n\n    if (user_extra_data_len > 0)\n    {\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, user_extra_data, user_extra_data_len) != user_extra_data_len)\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n        cur_archive_file_ofs += user_extra_data_len;\n    }\n\n    if (max_size)\n    {\n        void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE);\n        if (!pRead_buf)\n        {\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n        }\n\n        if (!level)\n        {\n            while (1)\n            {\n                size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE);\n                if (n == 0)\n                    break;\n\n                if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size))\n                {\n                    pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);\n                    return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n                }\n                if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)\n                {\n                    pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);\n                    return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n                }\n                file_ofs += n;\n                uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);\n                cur_archive_file_ofs += n;\n            }\n            uncomp_size = file_ofs;\n            comp_size = uncomp_size;\n        }\n        else\n        {\n            mz_bool result = MZ_FALSE;\n            mz_zip_writer_add_state state;\n            tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor));\n            if (!pComp)\n            {\n                pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);\n                return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n            }\n\n            state.m_pZip = pZip;\n            state.m_cur_archive_file_ofs = cur_archive_file_ofs;\n            state.m_comp_size = 0;\n\n            if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY)\n            {\n                pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);\n                pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);\n                return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);\n            }\n\n            for (;;)\n            {\n                tdefl_status status;\n                tdefl_flush flush = TDEFL_NO_FLUSH;\n\n                size_t n = read_callback(callback_opaque, file_ofs, pRead_buf, MZ_ZIP_MAX_IO_BUF_SIZE);\n                if ((n > MZ_ZIP_MAX_IO_BUF_SIZE) || (file_ofs + n > max_size))\n                {\n                    mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n                    break;\n                }\n\n                file_ofs += n;\n                uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);\n\n                if (pZip->m_pNeeds_keepalive != NULL && pZip->m_pNeeds_keepalive(pZip->m_pIO_opaque))\n                    flush = TDEFL_FULL_FLUSH;\n\n                if (n == 0)\n                    flush = TDEFL_FINISH;\n\n                status = tdefl_compress_buffer(pComp, pRead_buf, n, flush);\n                if (status == TDEFL_STATUS_DONE)\n                {\n                    result = MZ_TRUE;\n                    break;\n                }\n                else if (status != TDEFL_STATUS_OKAY)\n                {\n                    mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED);\n                    break;\n                }\n            }\n\n            pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);\n\n            if (!result)\n            {\n                pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);\n                return MZ_FALSE;\n            }\n\n            uncomp_size = file_ofs;\n            comp_size = state.m_comp_size;\n            cur_archive_file_ofs = state.m_cur_archive_file_ofs;\n        }\n\n        pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);\n    }\n\n    if (!(level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE))\n    {\n        mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64];\n        mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE32;\n\n        MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID);\n        MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32);\n        if (pExtra_data == NULL)\n        {\n            if (comp_size > MZ_UINT32_MAX)\n                return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);\n\n            MZ_WRITE_LE32(local_dir_footer + 8, comp_size);\n            MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size);\n        }\n        else\n        {\n            MZ_WRITE_LE64(local_dir_footer + 8, comp_size);\n            MZ_WRITE_LE64(local_dir_footer + 16, uncomp_size);\n            local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64;\n        }\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, local_dir_footer_size) != local_dir_footer_size)\n            return MZ_FALSE;\n\n        cur_archive_file_ofs += local_dir_footer_size;\n    }\n\n    if (level_and_flags & MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE)\n    {\n        if (pExtra_data != NULL)\n        {\n            extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (max_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL,\n                                                               (max_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);\n        }\n\n        if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header,\n                                                   (mz_uint16)archive_name_size, (mz_uint16)(extra_size + user_extra_data_len),\n                                                   (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : uncomp_size, \n                                                    (max_size >= MZ_UINT32_MAX) ? MZ_UINT32_MAX : comp_size,\n                                                   uncomp_crc32, method, gen_flags, dos_time, dos_date))\n            return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);\n\n        cur_archive_header_file_ofs = local_dir_header_ofs;\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n        if (pExtra_data != NULL)\n        {\n            cur_archive_header_file_ofs += sizeof(local_dir_header);\n\n            if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, pArchive_name, archive_name_size) != archive_name_size)\n            {\n                return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n            }\n\n            cur_archive_header_file_ofs += archive_name_size;\n\n            if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_header_file_ofs, extra_data, extra_size) != extra_size)\n                return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n            cur_archive_header_file_ofs += extra_size;\n        }\n    }\n\n    if (pExtra_data != NULL)\n    {\n        extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL,\n                                                           (uncomp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);\n    }\n\n    if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, (mz_uint16)extra_size, pComment, comment_size,\n                                          uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes,\n                                          user_extra_data_central, user_extra_data_central_len))\n        return MZ_FALSE;\n\n    pZip->m_total_files++;\n    pZip->m_archive_size = cur_archive_file_ofs;\n\n    return MZ_TRUE;\n}\n\n#ifndef MINIZ_NO_STDIO\n\nstatic size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)\n{\n\tMZ_FILE *pSrc_file = (MZ_FILE *)pOpaque;\n\tmz_int64 cur_ofs = MZ_FTELL64(pSrc_file);\n\n\tif (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pSrc_file, (mz_int64)file_ofs, SEEK_SET))))\n\t\treturn 0;\n\n\treturn MZ_FREAD(pBuf, 1, n, pSrc_file);\n}\n\nmz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,\n\tconst char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len)\n{\n\treturn mz_zip_writer_add_read_buf_callback(pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, pFile_time, pComment, comment_size, level_and_flags,\n\t\tuser_extra_data, user_extra_data_len, user_extra_data_central, user_extra_data_central_len);\n}\n\nmz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)\n{\n    MZ_FILE *pSrc_file = NULL;\n    mz_uint64 uncomp_size = 0;\n    MZ_TIME_T file_modified_time;\n    MZ_TIME_T *pFile_time = NULL;\n    mz_bool status;\n\n    memset(&file_modified_time, 0, sizeof(file_modified_time));\n\n#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO)\n    pFile_time = &file_modified_time;\n    if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time))\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED);\n#endif\n\n    pSrc_file = MZ_FOPEN(pSrc_filename, \"rb\");\n    if (!pSrc_file)\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);\n\n    MZ_FSEEK64(pSrc_file, 0, SEEK_END);\n    uncomp_size = MZ_FTELL64(pSrc_file);\n    MZ_FSEEK64(pSrc_file, 0, SEEK_SET);\n\n    status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, comment_size, level_and_flags, NULL, 0, NULL, 0);\n\n    MZ_FCLOSE(pSrc_file);\n\n    return status;\n}\n#endif /* #ifndef MINIZ_NO_STDIO */\n\nstatic mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint32_t ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start)\n{\n    /* + 64 should be enough for any new zip64 data */\n    if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE))\n        return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n\n    mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE);\n\n    if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start))\n    {\n        mz_uint8 new_ext_block[64];\n        mz_uint8 *pDst = new_ext_block;\n        mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID);\n        mz_write_le16(pDst + sizeof(mz_uint16), 0);\n        pDst += sizeof(mz_uint16) * 2;\n\n        if (pUncomp_size)\n        {\n            mz_write_le64(pDst, *pUncomp_size);\n            pDst += sizeof(mz_uint64);\n        }\n\n        if (pComp_size)\n        {\n            mz_write_le64(pDst, *pComp_size);\n            pDst += sizeof(mz_uint64);\n        }\n\n        if (pLocal_header_ofs)\n        {\n            mz_write_le64(pDst, *pLocal_header_ofs);\n            pDst += sizeof(mz_uint64);\n        }\n\n        if (pDisk_start)\n        {\n            mz_write_le32(pDst, *pDisk_start);\n            pDst += sizeof(mz_uint32);\n        }\n\n        mz_write_le16(new_ext_block + sizeof(mz_uint16), (mz_uint16)((pDst - new_ext_block) - sizeof(mz_uint16) * 2));\n\n        if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block))\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n    }\n\n    if ((pExt) && (ext_len))\n    {\n        mz_uint32 extra_size_remaining = ext_len;\n        const mz_uint8 *pExtra_data = pExt;\n\n        do\n        {\n            mz_uint32 field_id, field_data_size, field_total_size;\n\n            if (extra_size_remaining < (sizeof(mz_uint16) * 2))\n                return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n            field_id = MZ_READ_LE16(pExtra_data);\n            field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));\n            field_total_size = field_data_size + sizeof(mz_uint16) * 2;\n\n            if (field_total_size > extra_size_remaining)\n                return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n            if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)\n            {\n                if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size))\n                    return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n            }\n\n            pExtra_data += field_total_size;\n            extra_size_remaining -= field_total_size;\n        } while (extra_size_remaining);\n    }\n\n    return MZ_TRUE;\n}\n\n/* TODO: This func is now pretty freakin complex due to zip64, split it up? */\nmz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index)\n{\n    mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size;\n    mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs;\n    mz_uint64 cur_src_file_ofs, cur_dst_file_ofs;\n    mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];\n    mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;\n    mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];\n    size_t orig_central_dir_size;\n    mz_zip_internal_state *pState;\n    void *pBuf;\n    const mz_uint8 *pSrc_central_header;\n    mz_zip_archive_file_stat src_file_stat;\n    mz_uint32 src_filename_len, src_comment_len, src_ext_len;\n    mz_uint32 local_header_filename_size, local_header_extra_len;\n    mz_uint64 local_header_comp_size, local_header_uncomp_size;\n    mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE;\n\n    /* Sanity checks */\n    if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    pState = pZip->m_pState;\n\n    /* Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible */\n    if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    /* Get pointer to the source central dir header and crack it */\n    if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index)))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n    src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS);\n    src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS);\n    src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS);\n    src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len;\n\n    /* TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data) */\n    if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX)\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);\n\n    num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);\n\n    if (!pState->m_zip64)\n    {\n        if (pZip->m_total_files == MZ_UINT16_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);\n    }\n    else\n    {\n        /* TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing. */\n        if (pZip->m_total_files == MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);\n    }\n\n    if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL))\n        return MZ_FALSE;\n\n    cur_src_file_ofs = src_file_stat.m_local_header_ofs;\n    cur_dst_file_ofs = pZip->m_archive_size;\n\n    /* Read the source archive's local dir header */\n    if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n\n    if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n\n    cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;\n\n    /* Compute the total size we need to copy (filename+extra data+compressed data) */\n    local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS);\n    local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);\n    local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS);\n    local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS);\n    src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size;\n\n    /* Try to find a zip64 extended information field */\n    if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX)))\n    {\n        mz_zip_array file_data_array;\n        const mz_uint8 *pExtra_data;\n        mz_uint32 extra_size_remaining = local_header_extra_len;\n\n        mz_zip_array_init(&file_data_array, 1);\n        if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE))\n        {\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n        }\n\n        if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len)\n        {\n            mz_zip_array_clear(pZip, &file_data_array);\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n        }\n\n        pExtra_data = (const mz_uint8 *)file_data_array.m_p;\n\n        do\n        {\n            mz_uint32 field_id, field_data_size, field_total_size;\n\n            if (extra_size_remaining < (sizeof(mz_uint16) * 2))\n            {\n                mz_zip_array_clear(pZip, &file_data_array);\n                return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n            }\n\n            field_id = MZ_READ_LE16(pExtra_data);\n            field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));\n            field_total_size = field_data_size + sizeof(mz_uint16) * 2;\n\n            if (field_total_size > extra_size_remaining)\n            {\n                mz_zip_array_clear(pZip, &file_data_array);\n                return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n            }\n\n            if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)\n            {\n                const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32);\n\n                if (field_data_size < sizeof(mz_uint64) * 2)\n                {\n                    mz_zip_array_clear(pZip, &file_data_array);\n                    return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);\n                }\n\n                local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data);\n                local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); /* may be 0 if there's a descriptor */\n\n                found_zip64_ext_data_in_ldir = MZ_TRUE;\n                break;\n            }\n\n            pExtra_data += field_total_size;\n            extra_size_remaining -= field_total_size;\n        } while (extra_size_remaining);\n\n        mz_zip_array_clear(pZip, &file_data_array);\n    }\n\n    if (!pState->m_zip64)\n    {\n        /* Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor). */\n        /* We also check when the archive is finalized so this doesn't need to be perfect. */\n        mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) +\n                                            pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64;\n\n        if (approx_new_archive_size >= MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);\n    }\n\n    /* Write dest archive padding */\n    if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes))\n        return MZ_FALSE;\n\n    cur_dst_file_ofs += num_alignment_padding_bytes;\n\n    local_dir_header_ofs = cur_dst_file_ofs;\n    if (pZip->m_file_offset_alignment)\n    {\n        MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0);\n    }\n\n    /* The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip */\n    if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n    cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;\n\n    /* Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor */\n    if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining)))))\n        return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n\n    while (src_archive_bytes_remaining)\n    {\n        n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining);\n        if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n)\n        {\n            pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n        }\n        cur_src_file_ofs += n;\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)\n        {\n            pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n        }\n        cur_dst_file_ofs += n;\n\n        src_archive_bytes_remaining -= n;\n    }\n\n    /* Now deal with the optional data descriptor */\n    bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);\n    if (bit_flags & 8)\n    {\n        /* Copy data descriptor */\n        if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir))\n        {\n            /* src is zip64, dest must be zip64 */\n\n            /* name\t\t\tuint32_t's */\n            /* id\t\t\t\t1 (optional in zip64?) */\n            /* crc\t\t\t1 */\n            /* comp_size\t2 */\n            /* uncomp_size 2 */\n            if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6))\n            {\n                pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);\n                return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n            }\n\n            n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5);\n        }\n        else\n        {\n            /* src is NOT zip64 */\n            mz_bool has_id;\n\n            if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4)\n            {\n                pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);\n                return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);\n            }\n\n            has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID);\n\n            if (pZip->m_pState->m_zip64)\n            {\n                /* dest is zip64, so upgrade the data descriptor */\n                const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0));\n                const mz_uint32 src_crc32 = pSrc_descriptor[0];\n                const mz_uint64 src_comp_size = pSrc_descriptor[1];\n                const mz_uint64 src_uncomp_size = pSrc_descriptor[2];\n\n                mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID);\n                mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32);\n                mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size);\n                mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size);\n\n                n = sizeof(mz_uint32) * 6;\n            }\n            else\n            {\n                /* dest is NOT zip64, just copy it as-is */\n                n = sizeof(mz_uint32) * (has_id ? 4 : 3);\n            }\n        }\n\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)\n        {\n            pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n        }\n\n        cur_src_file_ofs += n;\n        cur_dst_file_ofs += n;\n    }\n    pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);\n\n    /* Finally, add the new central dir header */\n    orig_central_dir_size = pState->m_central_dir.m_size;\n\n    memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);\n\n    if (pState->m_zip64)\n    {\n        /* This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included. */\n        const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len;\n        mz_zip_array new_ext_block;\n\n        mz_zip_array_init(&new_ext_block, sizeof(mz_uint8));\n\n        MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX);\n        MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX);\n        MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX);\n\n        if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL))\n        {\n            mz_zip_array_clear(pZip, &new_ext_block);\n            return MZ_FALSE;\n        }\n\n        MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size);\n\n        if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))\n        {\n            mz_zip_array_clear(pZip, &new_ext_block);\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n        }\n\n        if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len))\n        {\n            mz_zip_array_clear(pZip, &new_ext_block);\n            mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n        }\n\n        if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size))\n        {\n            mz_zip_array_clear(pZip, &new_ext_block);\n            mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n        }\n\n        if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len))\n        {\n            mz_zip_array_clear(pZip, &new_ext_block);\n            mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n        }\n\n        mz_zip_array_clear(pZip, &new_ext_block);\n    }\n    else\n    {\n        /* sanity checks */\n        if (cur_dst_file_ofs > MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);\n\n        if (local_dir_header_ofs >= MZ_UINT32_MAX)\n            return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);\n\n        MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs);\n\n        if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n\n        if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size))\n        {\n            mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);\n            return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n        }\n    }\n\n    /* This shouldn't trigger unless we screwed up during the initial sanity checks */\n    if (pState->m_central_dir.m_size >= MZ_UINT32_MAX)\n    {\n        /* TODO: Support central dirs >= 32-bits in size */\n        mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);\n        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);\n    }\n\n    n = (mz_uint32)orig_central_dir_size;\n    if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1))\n    {\n        mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);\n        return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);\n    }\n\n    pZip->m_total_files++;\n    pZip->m_archive_size = cur_dst_file_ofs;\n\n    return MZ_TRUE;\n}\n\nmz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)\n{\n    mz_zip_internal_state *pState;\n    mz_uint64 central_dir_ofs, central_dir_size;\n    mz_uint8 hdr[256];\n\n    if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    pState = pZip->m_pState;\n\n    if (pState->m_zip64)\n    {\n        if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX))\n            return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);\n    }\n    else\n    {\n        if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX))\n            return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);\n    }\n\n    central_dir_ofs = 0;\n    central_dir_size = 0;\n    if (pZip->m_total_files)\n    {\n        /* Write central directory */\n        central_dir_ofs = pZip->m_archive_size;\n        central_dir_size = pState->m_central_dir.m_size;\n        pZip->m_central_directory_file_ofs = central_dir_ofs;\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size)\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n        pZip->m_archive_size += central_dir_size;\n    }\n\n    if (pState->m_zip64)\n    {\n        /* Write zip64 end of central directory header */\n        mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size;\n\n        MZ_CLEAR_OBJ(hdr);\n        MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG);\n        MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64));\n        MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); /* TODO: always Unix */\n        MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D);\n        MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files);\n        MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files);\n        MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size);\n        MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs);\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n        pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE;\n\n        /* Write zip64 end of central directory locator */\n        MZ_CLEAR_OBJ(hdr);\n        MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG);\n        MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr);\n        MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1);\n        if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE)\n            return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n        pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE;\n    }\n\n    /* Write end of central directory record */\n    MZ_CLEAR_OBJ(hdr);\n    MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG);\n    MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files));\n    MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files));\n    MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size));\n    MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs));\n\n    if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);\n\n#ifndef MINIZ_NO_STDIO\n    if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF))\n        return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED);\n#endif /* #ifndef MINIZ_NO_STDIO */\n\n    pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE;\n\n    pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED;\n    return MZ_TRUE;\n}\n\nmz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize)\n{\n    if ((!ppBuf) || (!pSize))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    *ppBuf = NULL;\n    *pSize = 0;\n\n    if ((!pZip) || (!pZip->m_pState))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (pZip->m_pWrite != mz_zip_heap_write_func)\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    if (!mz_zip_writer_finalize_archive(pZip))\n        return MZ_FALSE;\n\n    *ppBuf = pZip->m_pState->m_pMem;\n    *pSize = pZip->m_pState->m_mem_size;\n    pZip->m_pState->m_pMem = NULL;\n    pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0;\n\n    return MZ_TRUE;\n}\n\nmz_bool mz_zip_writer_end(mz_zip_archive *pZip)\n{\n    return mz_zip_writer_end_internal(pZip, MZ_TRUE);\n}\n\n#ifndef MINIZ_NO_STDIO\nmz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)\n{\n    return mz_zip_add_mem_to_archive_file_in_place_v2(pZip_filename, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, NULL);\n}\n\nmz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr)\n{\n    mz_bool status, created_new_archive = MZ_FALSE;\n    mz_zip_archive zip_archive;\n    struct MZ_FILE_STAT_STRUCT file_stat;\n    mz_zip_error actual_err = MZ_ZIP_NO_ERROR;\n\n    mz_zip_zero_struct(&zip_archive);\n    if ((int)level_and_flags < 0)\n        level_and_flags = MZ_DEFAULT_LEVEL;\n\n    if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION))\n    {\n        if (pErr)\n            *pErr = MZ_ZIP_INVALID_PARAMETER;\n        return MZ_FALSE;\n    }\n\n    if (!mz_zip_writer_validate_archive_name(pArchive_name))\n    {\n        if (pErr)\n            *pErr = MZ_ZIP_INVALID_FILENAME;\n        return MZ_FALSE;\n    }\n\n    /* Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten. */\n    /* So be sure to compile with _LARGEFILE64_SOURCE 1 */\n    if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0)\n    {\n        /* Create a new archive. */\n        if (!mz_zip_writer_init_file_v2(&zip_archive, pZip_filename, 0, level_and_flags))\n        {\n            if (pErr)\n                *pErr = zip_archive.m_last_error;\n            return MZ_FALSE;\n        }\n\n        created_new_archive = MZ_TRUE;\n    }\n    else\n    {\n        /* Append to an existing archive. */\n        if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0))\n        {\n            if (pErr)\n                *pErr = zip_archive.m_last_error;\n            return MZ_FALSE;\n        }\n\n        if (!mz_zip_writer_init_from_reader_v2(&zip_archive, pZip_filename, level_and_flags))\n        {\n            if (pErr)\n                *pErr = zip_archive.m_last_error;\n\n            mz_zip_reader_end_internal(&zip_archive, MZ_FALSE);\n\n            return MZ_FALSE;\n        }\n    }\n\n    status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0);\n    actual_err = zip_archive.m_last_error;\n\n    /* Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) */\n    if (!mz_zip_writer_finalize_archive(&zip_archive))\n    {\n        if (!actual_err)\n            actual_err = zip_archive.m_last_error;\n\n        status = MZ_FALSE;\n    }\n\n    if (!mz_zip_writer_end_internal(&zip_archive, status))\n    {\n        if (!actual_err)\n            actual_err = zip_archive.m_last_error;\n\n        status = MZ_FALSE;\n    }\n\n    if ((!status) && (created_new_archive))\n    {\n        /* It's a new archive and something went wrong, so just delete it. */\n        int ignoredStatus = MZ_DELETE_FILE(pZip_filename);\n        (void)ignoredStatus;\n    }\n\n    if (pErr)\n        *pErr = actual_err;\n\n    return status;\n}\n\nvoid *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr)\n{\n    mz_uint32 file_index;\n    mz_zip_archive zip_archive;\n    void *p = NULL;\n\n    if (pSize)\n        *pSize = 0;\n\n    if ((!pZip_filename) || (!pArchive_name))\n    {\n        if (pErr)\n            *pErr = MZ_ZIP_INVALID_PARAMETER;\n\n        return NULL;\n    }\n\n    mz_zip_zero_struct(&zip_archive);\n    if (!mz_zip_reader_init_file_v2(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0))\n    {\n        if (pErr)\n            *pErr = zip_archive.m_last_error;\n\n        return NULL;\n    }\n\n    if (mz_zip_reader_locate_file_v2(&zip_archive, pArchive_name, pComment, flags, &file_index))\n    {\n        p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags);\n    }\n\n    mz_zip_reader_end_internal(&zip_archive, p != NULL);\n\n    if (pErr)\n        *pErr = zip_archive.m_last_error;\n\n    return p;\n}\n\nvoid *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags)\n{\n    return mz_zip_extract_archive_file_to_heap_v2(pZip_filename, pArchive_name, NULL, pSize, flags, NULL);\n}\n\n#endif /* #ifndef MINIZ_NO_STDIO */\n\n#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */\n\n/* ------------------- Misc utils */\n\nmz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip)\n{\n    return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID;\n}\n\nmz_zip_type mz_zip_get_type(mz_zip_archive *pZip)\n{\n    return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID;\n}\n\nmz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num)\n{\n    mz_zip_error prev_err;\n\n    if (!pZip)\n        return MZ_ZIP_INVALID_PARAMETER;\n\n    prev_err = pZip->m_last_error;\n\n    pZip->m_last_error = err_num;\n    return prev_err;\n}\n\nmz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip)\n{\n    if (!pZip)\n        return MZ_ZIP_INVALID_PARAMETER;\n\n    return pZip->m_last_error;\n}\n\nmz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip)\n{\n    return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR);\n}\n\nmz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip)\n{\n    mz_zip_error prev_err;\n\n    if (!pZip)\n        return MZ_ZIP_INVALID_PARAMETER;\n\n    prev_err = pZip->m_last_error;\n\n    pZip->m_last_error = MZ_ZIP_NO_ERROR;\n    return prev_err;\n}\n\nconst char *mz_zip_get_error_string(mz_zip_error mz_err)\n{\n    switch (mz_err)\n    {\n        case MZ_ZIP_NO_ERROR:\n            return \"no error\";\n        case MZ_ZIP_UNDEFINED_ERROR:\n            return \"undefined error\";\n        case MZ_ZIP_TOO_MANY_FILES:\n            return \"too many files\";\n        case MZ_ZIP_FILE_TOO_LARGE:\n            return \"file too large\";\n        case MZ_ZIP_UNSUPPORTED_METHOD:\n            return \"unsupported method\";\n        case MZ_ZIP_UNSUPPORTED_ENCRYPTION:\n            return \"unsupported encryption\";\n        case MZ_ZIP_UNSUPPORTED_FEATURE:\n            return \"unsupported feature\";\n        case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR:\n            return \"failed finding central directory\";\n        case MZ_ZIP_NOT_AN_ARCHIVE:\n            return \"not a ZIP archive\";\n        case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED:\n            return \"invalid header or archive is corrupted\";\n        case MZ_ZIP_UNSUPPORTED_MULTIDISK:\n            return \"unsupported multidisk archive\";\n        case MZ_ZIP_DECOMPRESSION_FAILED:\n            return \"decompression failed or archive is corrupted\";\n        case MZ_ZIP_COMPRESSION_FAILED:\n            return \"compression failed\";\n        case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE:\n            return \"unexpected decompressed size\";\n        case MZ_ZIP_CRC_CHECK_FAILED:\n            return \"CRC-32 check failed\";\n        case MZ_ZIP_UNSUPPORTED_CDIR_SIZE:\n            return \"unsupported central directory size\";\n        case MZ_ZIP_ALLOC_FAILED:\n            return \"allocation failed\";\n        case MZ_ZIP_FILE_OPEN_FAILED:\n            return \"file open failed\";\n        case MZ_ZIP_FILE_CREATE_FAILED:\n            return \"file create failed\";\n        case MZ_ZIP_FILE_WRITE_FAILED:\n            return \"file write failed\";\n        case MZ_ZIP_FILE_READ_FAILED:\n            return \"file read failed\";\n        case MZ_ZIP_FILE_CLOSE_FAILED:\n            return \"file close failed\";\n        case MZ_ZIP_FILE_SEEK_FAILED:\n            return \"file seek failed\";\n        case MZ_ZIP_FILE_STAT_FAILED:\n            return \"file stat failed\";\n        case MZ_ZIP_INVALID_PARAMETER:\n            return \"invalid parameter\";\n        case MZ_ZIP_INVALID_FILENAME:\n            return \"invalid filename\";\n        case MZ_ZIP_BUF_TOO_SMALL:\n            return \"buffer too small\";\n        case MZ_ZIP_INTERNAL_ERROR:\n            return \"internal error\";\n        case MZ_ZIP_FILE_NOT_FOUND:\n            return \"file not found\";\n        case MZ_ZIP_ARCHIVE_TOO_LARGE:\n            return \"archive is too large\";\n        case MZ_ZIP_VALIDATION_FAILED:\n            return \"validation failed\";\n        case MZ_ZIP_WRITE_CALLBACK_FAILED:\n            return \"write calledback failed\";\n        default:\n            break;\n    }\n\n    return \"unknown error\";\n}\n\n/* Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh. */\nmz_bool mz_zip_is_zip64(mz_zip_archive *pZip)\n{\n    if ((!pZip) || (!pZip->m_pState))\n        return MZ_FALSE;\n\n    return pZip->m_pState->m_zip64;\n}\n\nsize_t mz_zip_get_central_dir_size(mz_zip_archive *pZip)\n{\n    if ((!pZip) || (!pZip->m_pState))\n        return 0;\n\n    return pZip->m_pState->m_central_dir.m_size;\n}\n\nmz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip)\n{\n    return pZip ? pZip->m_total_files : 0;\n}\n\nmz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip)\n{\n    if (!pZip)\n        return 0;\n    return pZip->m_archive_size;\n}\n\nmz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip)\n{\n    if ((!pZip) || (!pZip->m_pState))\n        return 0;\n    return pZip->m_pState->m_file_archive_start_ofs;\n}\n\nMZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip)\n{\n    if ((!pZip) || (!pZip->m_pState))\n        return 0;\n    return pZip->m_pState->m_pFile;\n}\n\nsize_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n)\n{\n    if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead))\n        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n\n    return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n);\n}\n\nmz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size)\n{\n    mz_uint n;\n    const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);\n    if (!p)\n    {\n        if (filename_buf_size)\n            pFilename[0] = '\\0';\n        mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);\n        return 0;\n    }\n    n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);\n    if (filename_buf_size)\n    {\n        n = MZ_MIN(n, filename_buf_size - 1);\n        memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);\n        pFilename[n] = '\\0';\n    }\n    return n + 1;\n}\n\nmz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat)\n{\n    return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL);\n}\n\nmz_bool mz_zip_end(mz_zip_archive *pZip)\n{\n    if (!pZip)\n        return MZ_FALSE;\n\n    if (pZip->m_zip_mode == MZ_ZIP_MODE_READING)\n        return mz_zip_reader_end(pZip);\n#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS\n    else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))\n        return mz_zip_writer_end(pZip);\n#endif\n\n    return MZ_FALSE;\n}\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/\n"
  },
  {
    "path": "src/miniz/miniz.h",
    "content": "#define MINIZ_NO_TIME\n#define MINIZ_NO_ZLIB_APIS\n#define MINIZ_NO_MALLOC\n#define MINIZ_NO_ARCHIVE_APIS\n#define MINIZ_NO_STDIO\n#define MINIZ_NO_ARCHIVE_WRITING_APIS\n#define MINIZ_NO_ZLIB_COMPATIBLE_NAME\n#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES\n\n#define MINIZ_EXPORT\n/* miniz.c 2.2.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing\n   See \"unlicense\" statement at the end of this file.\n   Rich Geldreich <richgel99@gmail.com>, last updated Oct. 13, 2013\n   Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt\n\n   Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define\n   MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros).\n\n   * Low-level Deflate/Inflate implementation notes:\n\n     Compression: Use the \"tdefl\" API's. The compressor supports raw, static, and dynamic blocks, lazy or\n     greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses\n     approximately as well as zlib.\n\n     Decompression: Use the \"tinfl\" API's. The entire decompressor is implemented as a single function\n     coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory\n     block large enough to hold the entire file.\n\n     The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation.\n\n   * zlib-style API notes:\n\n     miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in\n     zlib replacement in many apps:\n        The z_stream struct, optional memory allocation callbacks\n        deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound\n        inflateInit/inflateInit2/inflate/inflateReset/inflateEnd\n        compress, compress2, compressBound, uncompress\n        CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines.\n        Supports raw deflate streams or standard zlib streams with adler-32 checking.\n\n     Limitations:\n      The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries.\n      I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but\n      there are no guarantees that miniz.c pulls this off perfectly.\n\n   * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by\n     Alex Evans. Supports 1-4 bytes/pixel images.\n\n   * ZIP archive API notes:\n\n     The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to\n     get the job done with minimal fuss. There are simple API's to retrieve file information, read files from\n     existing archives, create new archives, append new files to existing archives, or clone archive data from\n     one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h),\n     or you can specify custom file read/write callbacks.\n\n     - Archive reading: Just call this function to read a single file from a disk archive:\n\n      void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name,\n        size_t *pSize, mz_uint zip_flags);\n\n     For more complex cases, use the \"mz_zip_reader\" functions. Upon opening an archive, the entire central\n     directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files.\n\n     - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file:\n\n     int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);\n\n     The locate operation can optionally check file comments too, which (as one example) can be used to identify\n     multiple versions of the same file in an archive. This function uses a simple linear search through the central\n     directory, so it's not very fast.\n\n     Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and\n     retrieve detailed info on each file by calling mz_zip_reader_file_stat().\n\n     - Archive creation: Use the \"mz_zip_writer\" functions. The ZIP writer immediately writes compressed file data\n     to disk and builds an exact image of the central directory in memory. The central directory image is written\n     all at once at the end of the archive file when the archive is finalized.\n\n     The archive writer can optionally align each file's local header and file data to any power of 2 alignment,\n     which can be useful when the archive will be read from optical media. Also, the writer supports placing\n     arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still\n     readable by any ZIP tool.\n\n     - Archive appending: The simple way to add a single file to an archive is to call this function:\n\n      mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name,\n        const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);\n\n     The archive will be created if it doesn't already exist, otherwise it'll be appended to.\n     Note the appending is done in-place and is not an atomic operation, so if something goes wrong\n     during the operation it's possible the archive could be left without a central directory (although the local\n     file headers and file data will be fine, so the archive will be recoverable).\n\n     For more complex archive modification scenarios:\n     1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to\n     preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the\n     compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and\n     you're done. This is safe but requires a bunch of temporary disk space or heap memory.\n\n     2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(),\n     append new files as needed, then finalize the archive which will write an updated central directory to the\n     original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a\n     possibility that the archive's central directory could be lost with this method if anything goes wrong, though.\n\n     - ZIP archive support limitations:\n     No spanning support. Extraction functions can only handle unencrypted, stored or deflated files.\n     Requires streams capable of seeking.\n\n   * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the\n     below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it.\n\n   * Important: For best perf. be sure to customize the below macros for your target platform:\n     #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1\n     #define MINIZ_LITTLE_ENDIAN 1\n     #define MINIZ_HAS_64BIT_REGISTERS 1\n\n   * On platforms using glibc, Be sure to \"#define _LARGEFILE64_SOURCE 1\" before including miniz.c to ensure miniz\n     uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files\n     (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes).\n*/\n#pragma once\n\n\n\n/* Defines to completely disable specific portions of miniz.c: \n   If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */\n\n/* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */\n/*#define MINIZ_NO_STDIO */\n\n/* If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or */\n/* get/set file times, and the C run-time funcs that get/set times won't be called. */\n/* The current downside is the times written to your archives will be from 1979. */\n/*#define MINIZ_NO_TIME */\n\n/* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */\n/*#define MINIZ_NO_ARCHIVE_APIS */\n\n/* Define MINIZ_NO_ARCHIVE_WRITING_APIS to disable all writing related ZIP archive API's. */\n/*#define MINIZ_NO_ARCHIVE_WRITING_APIS */\n\n/* Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. */\n/*#define MINIZ_NO_ZLIB_APIS */\n\n/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */\n/*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */\n\n/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. \n   Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc\n   callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user\n   functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */\n/*#define MINIZ_NO_MALLOC */\n\n#if defined(__TINYC__) && (defined(__linux) || defined(__linux__))\n/* TODO: Work around \"error: include file 'sys\\utime.h' when compiling with tcc on Linux */\n#define MINIZ_NO_TIME\n#endif\n\n#include <stddef.h>\n\n#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS)\n#include <time.h>\n#endif\n\n#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__)\n/* MINIZ_X86_OR_X64_CPU is only used to help set the below macros. */\n#define MINIZ_X86_OR_X64_CPU 1\n#else\n#define MINIZ_X86_OR_X64_CPU 0\n#endif\n\n#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU\n/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */\n#define MINIZ_LITTLE_ENDIAN 1\n#else\n#define MINIZ_LITTLE_ENDIAN 0\n#endif\n\n/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */\n#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES)\n#if MINIZ_X86_OR_X64_CPU\n/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */\n#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1\n#define MINIZ_UNALIGNED_USE_MEMCPY\n#else\n#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0\n#endif\n#endif\n\n#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__)\n/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). */\n#define MINIZ_HAS_64BIT_REGISTERS 1\n#else\n#define MINIZ_HAS_64BIT_REGISTERS 0\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* ------------------- zlib-style API Definitions. */\n\n/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! */\ntypedef unsigned long mz_ulong;\n\n/* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */\nMINIZ_EXPORT void mz_free(void *p);\n\n#define MZ_ADLER32_INIT (1)\n/* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */\nMINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len);\n\n#define MZ_CRC32_INIT (0)\n/* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */\nMINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len);\n\n/* Compression strategies. */\nenum\n{\n    MZ_DEFAULT_STRATEGY = 0,\n    MZ_FILTERED = 1,\n    MZ_HUFFMAN_ONLY = 2,\n    MZ_RLE = 3,\n    MZ_FIXED = 4\n};\n\n/* Method */\n#define MZ_DEFLATED 8\n\n/* Heap allocation callbacks.\nNote that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */\ntypedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size);\ntypedef void (*mz_free_func)(void *opaque, void *address);\ntypedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size);\n\n/* Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. */\nenum\n{\n    MZ_NO_COMPRESSION = 0,\n    MZ_BEST_SPEED = 1,\n    MZ_BEST_COMPRESSION = 9,\n    MZ_UBER_COMPRESSION = 10,\n    MZ_DEFAULT_LEVEL = 6,\n    MZ_DEFAULT_COMPRESSION = -1\n};\n\n#define MZ_VERSION \"10.2.0\"\n#define MZ_VERNUM 0xA100\n#define MZ_VER_MAJOR 10\n#define MZ_VER_MINOR 2\n#define MZ_VER_REVISION 0\n#define MZ_VER_SUBREVISION 0\n\n#ifndef MINIZ_NO_ZLIB_APIS\n\n/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). */\nenum\n{\n    MZ_NO_FLUSH = 0,\n    MZ_PARTIAL_FLUSH = 1,\n    MZ_SYNC_FLUSH = 2,\n    MZ_FULL_FLUSH = 3,\n    MZ_FINISH = 4,\n    MZ_BLOCK = 5\n};\n\n/* Return status codes. MZ_PARAM_ERROR is non-standard. */\nenum\n{\n    MZ_OK = 0,\n    MZ_STREAM_END = 1,\n    MZ_NEED_DICT = 2,\n    MZ_ERRNO = -1,\n    MZ_STREAM_ERROR = -2,\n    MZ_DATA_ERROR = -3,\n    MZ_MEM_ERROR = -4,\n    MZ_BUF_ERROR = -5,\n    MZ_VERSION_ERROR = -6,\n    MZ_PARAM_ERROR = -10000\n};\n\n/* Window bits */\n#define MZ_DEFAULT_WINDOW_BITS 15\n\nstruct mz_internal_state;\n\n/* Compression/decompression stream struct. */\ntypedef struct mz_stream_s\n{\n    const unsigned char *next_in; /* pointer to next byte to read */\n    unsigned int avail_in;        /* number of bytes available at next_in */\n    mz_ulong total_in;            /* total number of bytes consumed so far */\n\n    unsigned char *next_out; /* pointer to next byte to write */\n    unsigned int avail_out;  /* number of bytes that can be written to next_out */\n    mz_ulong total_out;      /* total number of bytes produced so far */\n\n    char *msg;                       /* error msg (unused) */\n    struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */\n\n    mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */\n    mz_free_func zfree;   /* optional heap free function (defaults to free) */\n    void *opaque;         /* heap alloc function user pointer */\n\n    int data_type;     /* data_type (unused) */\n    mz_ulong adler;    /* adler32 of the source or uncompressed data */\n    mz_ulong reserved; /* not used */\n} mz_stream;\n\ntypedef mz_stream *mz_streamp;\n\n/* Returns the version string of miniz.c. */\nMINIZ_EXPORT const char *mz_version(void);\n\n/* mz_deflateInit() initializes a compressor with default options: */\n/* Parameters: */\n/*  pStream must point to an initialized mz_stream struct. */\n/*  level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. */\n/*  level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. */\n/*  (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) */\n/* Return values: */\n/*  MZ_OK on success. */\n/*  MZ_STREAM_ERROR if the stream is bogus. */\n/*  MZ_PARAM_ERROR if the input parameters are bogus. */\n/*  MZ_MEM_ERROR on out of memory. */\nMINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level);\n\n/* mz_deflateInit2() is like mz_deflate(), except with more control: */\n/* Additional parameters: */\n/*   method must be MZ_DEFLATED */\n/*   window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */\n/*   mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */\nMINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy);\n\n/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */\nMINIZ_EXPORT int mz_deflateReset(mz_streamp pStream);\n\n/* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */\n/* Parameters: */\n/*   pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */\n/*   flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. */\n/* Return values: */\n/*   MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). */\n/*   MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. */\n/*   MZ_STREAM_ERROR if the stream is bogus. */\n/*   MZ_PARAM_ERROR if one of the parameters is invalid. */\n/*   MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */\nMINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush);\n\n/* mz_deflateEnd() deinitializes a compressor: */\n/* Return values: */\n/*  MZ_OK on success. */\n/*  MZ_STREAM_ERROR if the stream is bogus. */\nMINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream);\n\n/* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */\nMINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len);\n\n/* Single-call compression functions mz_compress() and mz_compress2(): */\n/* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */\nMINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);\nMINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level);\n\n/* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */\nMINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len);\n\n/* Initializes a decompressor. */\nMINIZ_EXPORT int mz_inflateInit(mz_streamp pStream);\n\n/* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */\n/* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */\nMINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits);\n\n/* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */\nMINIZ_EXPORT int mz_inflateReset(mz_streamp pStream);\n\n/* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */\n/* Parameters: */\n/*   pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. */\n/*   flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. */\n/*   On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). */\n/*   MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. */\n/* Return values: */\n/*   MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. */\n/*   MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. */\n/*   MZ_STREAM_ERROR if the stream is bogus. */\n/*   MZ_DATA_ERROR if the deflate stream is invalid. */\n/*   MZ_PARAM_ERROR if one of the parameters is invalid. */\n/*   MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */\n/*   with more input data, or with more room in the output buffer (except when using single call decompression, described above). */\nMINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush);\n\n/* Deinitializes a decompressor. */\nMINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream);\n\n/* Single-call decompression. */\n/* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */\nMINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len);\nMINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len);\n\n/* Returns a string description of the specified error code, or NULL if the error code is invalid. */\nMINIZ_EXPORT const char *mz_error(int err);\n\n/* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */\n/* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */\n#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES\ntypedef unsigned char Byte;\ntypedef unsigned int uInt;\ntypedef mz_ulong uLong;\ntypedef Byte Bytef;\ntypedef uInt uIntf;\ntypedef char charf;\ntypedef int intf;\ntypedef void *voidpf;\ntypedef uLong uLongf;\ntypedef void *voidp;\ntypedef void *const voidpc;\n#define Z_NULL 0\n#define Z_NO_FLUSH MZ_NO_FLUSH\n#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH\n#define Z_SYNC_FLUSH MZ_SYNC_FLUSH\n#define Z_FULL_FLUSH MZ_FULL_FLUSH\n#define Z_FINISH MZ_FINISH\n#define Z_BLOCK MZ_BLOCK\n#define Z_OK MZ_OK\n#define Z_STREAM_END MZ_STREAM_END\n#define Z_NEED_DICT MZ_NEED_DICT\n#define Z_ERRNO MZ_ERRNO\n#define Z_STREAM_ERROR MZ_STREAM_ERROR\n#define Z_DATA_ERROR MZ_DATA_ERROR\n#define Z_MEM_ERROR MZ_MEM_ERROR\n#define Z_BUF_ERROR MZ_BUF_ERROR\n#define Z_VERSION_ERROR MZ_VERSION_ERROR\n#define Z_PARAM_ERROR MZ_PARAM_ERROR\n#define Z_NO_COMPRESSION MZ_NO_COMPRESSION\n#define Z_BEST_SPEED MZ_BEST_SPEED\n#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION\n#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION\n#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY\n#define Z_FILTERED MZ_FILTERED\n#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY\n#define Z_RLE MZ_RLE\n#define Z_FIXED MZ_FIXED\n#define Z_DEFLATED MZ_DEFLATED\n#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS\n#define alloc_func mz_alloc_func\n#define free_func mz_free_func\n#define internal_state mz_internal_state\n#define z_stream mz_stream\n#define deflateInit mz_deflateInit\n#define deflateInit2 mz_deflateInit2\n#define deflateReset mz_deflateReset\n#define deflate mz_deflate\n#define deflateEnd mz_deflateEnd\n#define deflateBound mz_deflateBound\n#define compress mz_compress\n#define compress2 mz_compress2\n#define compressBound mz_compressBound\n#define inflateInit mz_inflateInit\n#define inflateInit2 mz_inflateInit2\n#define inflateReset mz_inflateReset\n#define inflate mz_inflate\n#define inflateEnd mz_inflateEnd\n#define uncompress mz_uncompress\n#define uncompress2 mz_uncompress2\n#define crc32 mz_crc32\n#define adler32 mz_adler32\n#define MAX_WBITS 15\n#define MAX_MEM_LEVEL 9\n#define zError mz_error\n#define ZLIB_VERSION MZ_VERSION\n#define ZLIB_VERNUM MZ_VERNUM\n#define ZLIB_VER_MAJOR MZ_VER_MAJOR\n#define ZLIB_VER_MINOR MZ_VER_MINOR\n#define ZLIB_VER_REVISION MZ_VER_REVISION\n#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION\n#define zlibVersion mz_version\n#define zlib_version mz_version()\n#endif /* #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES */\n\n#endif /* MINIZ_NO_ZLIB_APIS */\n\n#ifdef __cplusplus\n}\n#endif\n\n\n\n\n\n#pragma once\n#include <assert.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n\n\n\n/* ------------------- Types and macros */\ntypedef unsigned char mz_uint8;\ntypedef signed short mz_int16;\ntypedef unsigned short mz_uint16;\ntypedef unsigned int mz_uint32;\ntypedef unsigned int mz_uint;\ntypedef int64_t mz_int64;\ntypedef uint64_t mz_uint64;\ntypedef int mz_bool;\n\n#define MZ_FALSE (0)\n#define MZ_TRUE (1)\n\n/* Works around MSVC's spammy \"warning C4127: conditional expression is constant\" message. */\n#ifdef _MSC_VER\n#define MZ_MACRO_END while (0, 0)\n#else\n#define MZ_MACRO_END while (0)\n#endif\n\n#ifdef MINIZ_NO_STDIO\n#define MZ_FILE void *\n#else\n#include <stdio.h>\n#define MZ_FILE FILE\n#endif /* #ifdef MINIZ_NO_STDIO */\n\n#ifdef MINIZ_NO_TIME\ntypedef struct mz_dummy_time_t_tag\n{\n    int m_dummy;\n} mz_dummy_time_t;\n#define MZ_TIME_T mz_dummy_time_t\n#else\n#define MZ_TIME_T time_t\n#endif\n\n#define MZ_ASSERT(x) assert(x)\n\n#ifdef MINIZ_NO_MALLOC\n#define MZ_MALLOC(x) NULL\n#define MZ_FREE(x) (void)x, ((void)0)\n#define MZ_REALLOC(p, x) NULL\n#else\n#define MZ_MALLOC(x) malloc(x)\n#define MZ_FREE(x) free(x)\n#define MZ_REALLOC(p, x) realloc(p, x)\n#endif\n\n#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b))\n#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b))\n#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj))\n\n#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN\n#define MZ_READ_LE16(p) *((const mz_uint16 *)(p))\n#define MZ_READ_LE32(p) *((const mz_uint32 *)(p))\n#else\n#define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U))\n#define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))\n#endif\n\n#define MZ_READ_LE64(p) (((mz_uint64)MZ_READ_LE32(p)) | (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) << 32U))\n\n#ifdef _MSC_VER\n#define MZ_FORCEINLINE __forceinline\n#elif defined(__GNUC__)\n#define MZ_FORCEINLINE __inline__ __attribute__((__always_inline__))\n#else\n#define MZ_FORCEINLINE inline\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nextern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size);\nextern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address);\nextern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size);\n\n#define MZ_UINT16_MAX (0xFFFFU)\n#define MZ_UINT32_MAX (0xFFFFFFFFU)\n\n#ifdef __cplusplus\n}\n#endif\n #pragma once\n\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n/* ------------------- Low-level Compression API Definitions */\n\n/* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). */\n#define TDEFL_LESS_MEMORY 0\n\n/* tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): */\n/* TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). */\nenum\n{\n    TDEFL_HUFFMAN_ONLY = 0,\n    TDEFL_DEFAULT_MAX_PROBES = 128,\n    TDEFL_MAX_PROBES_MASK = 0xFFF\n};\n\n/* TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. */\n/* TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). */\n/* TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. */\n/* TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). */\n/* TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) */\n/* TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. */\n/* TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. */\n/* TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. */\n/* The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). */\nenum\n{\n    TDEFL_WRITE_ZLIB_HEADER = 0x01000,\n    TDEFL_COMPUTE_ADLER32 = 0x02000,\n    TDEFL_GREEDY_PARSING_FLAG = 0x04000,\n    TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000,\n    TDEFL_RLE_MATCHES = 0x10000,\n    TDEFL_FILTER_MATCHES = 0x20000,\n    TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000,\n    TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000\n};\n\n/* High level compression functions: */\n/* tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). */\n/* On entry: */\n/*  pSrc_buf, src_buf_len: Pointer and size of source block to compress. */\n/*  flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. */\n/* On return: */\n/*  Function returns a pointer to the compressed data, or NULL on failure. */\n/*  *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */\n/*  The caller must free() the returned block when it's no longer needed. */\nMINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);\n\n/* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */\n/* Returns 0 on failure. */\nMINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);\n\n/* Compresses an image to a compressed PNG file in memory. */\n/* On entry: */\n/*  pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. */\n/*  The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. */\n/*  level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL */\n/*  If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). */\n/* On return: */\n/*  Function returns a pointer to the compressed data, or NULL on failure. */\n/*  *pLen_out will be set to the size of the PNG image file. */\n/*  The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */\nMINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip);\nMINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out);\n\n/* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */\ntypedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser);\n\n/* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */\nMINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);\n\nenum\n{\n    TDEFL_MAX_HUFF_TABLES = 3,\n    TDEFL_MAX_HUFF_SYMBOLS_0 = 288,\n    TDEFL_MAX_HUFF_SYMBOLS_1 = 32,\n    TDEFL_MAX_HUFF_SYMBOLS_2 = 19,\n    TDEFL_LZ_DICT_SIZE = 32768,\n    TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1,\n    TDEFL_MIN_MATCH_LEN = 3,\n    TDEFL_MAX_MATCH_LEN = 258\n};\n\n/* TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). */\n#if TDEFL_LESS_MEMORY\nenum\n{\n    TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024,\n    TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10,\n    TDEFL_MAX_HUFF_SYMBOLS = 288,\n    TDEFL_LZ_HASH_BITS = 12,\n    TDEFL_LEVEL1_HASH_SIZE_MASK = 4095,\n    TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3,\n    TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS\n};\n#else\nenum\n{\n    TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024,\n    TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10,\n    TDEFL_MAX_HUFF_SYMBOLS = 288,\n    TDEFL_LZ_HASH_BITS = 15,\n    TDEFL_LEVEL1_HASH_SIZE_MASK = 4095,\n    TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3,\n    TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS\n};\n#endif\n\n/* The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. */\ntypedef enum {\n    TDEFL_STATUS_BAD_PARAM = -2,\n    TDEFL_STATUS_PUT_BUF_FAILED = -1,\n    TDEFL_STATUS_OKAY = 0,\n    TDEFL_STATUS_DONE = 1\n} tdefl_status;\n\n/* Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums */\ntypedef enum {\n    TDEFL_NO_FLUSH = 0,\n    TDEFL_SYNC_FLUSH = 2,\n    TDEFL_FULL_FLUSH = 3,\n    TDEFL_FINISH = 4\n} tdefl_flush;\n\n/* tdefl's compression state structure. */\ntypedef struct\n{\n    tdefl_put_buf_func_ptr m_pPut_buf_func;\n    void *m_pPut_buf_user;\n    mz_uint m_flags, m_max_probes[2];\n    int m_greedy_parsing;\n    mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size;\n    mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end;\n    mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer;\n    mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish;\n    tdefl_status m_prev_return_status;\n    const void *m_pIn_buf;\n    void *m_pOut_buf;\n    size_t *m_pIn_buf_size, *m_pOut_buf_size;\n    tdefl_flush m_flush;\n    const mz_uint8 *m_pSrc;\n    size_t m_src_buf_left, m_out_buf_ofs;\n    mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1];\n    mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];\n    mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];\n    mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS];\n    mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE];\n    mz_uint16 m_next[TDEFL_LZ_DICT_SIZE];\n    mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE];\n    mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE];\n} tdefl_compressor;\n\n/* Initializes the compressor. */\n/* There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. */\n/* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */\n/* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */\n/* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */\nMINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);\n\n/* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */\nMINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush);\n\n/* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */\n/* tdefl_compress_buffer() always consumes the entire input buffer. */\nMINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush);\n\nMINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d);\nMINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d);\n\n/* Create tdefl_compress() flags given zlib-style compression parameters. */\n/* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */\n/* window_bits may be -15 (raw deflate) or 15 (zlib) */\n/* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */\nMINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy);\n\n#ifndef MINIZ_NO_MALLOC\n/* Allocate the tdefl_compressor structure in C so that */\n/* non-C language bindings to tdefl_ API don't need to worry about */\n/* structure size and allocation mechanism. */\nMINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void);\nMINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp);\n#endif\n\n#ifdef __cplusplus\n}\n#endif\n #pragma once\n\n/* ------------------- Low-level Decompression API Definitions */\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n/* Decompression flags used by tinfl_decompress(). */\n/* TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. */\n/* TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. */\n/* TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). */\n/* TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. */\nenum\n{\n    TINFL_FLAG_PARSE_ZLIB_HEADER = 1,\n    TINFL_FLAG_HAS_MORE_INPUT = 2,\n    TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4,\n    TINFL_FLAG_COMPUTE_ADLER32 = 8\n};\n\n/* High level decompression functions: */\n/* tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). */\n/* On entry: */\n/*  pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. */\n/* On return: */\n/*  Function returns a pointer to the decompressed data, or NULL on failure. */\n/*  *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */\n/*  The caller must call mz_free() on the returned block when it's no longer needed. */\nMINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags);\n\n/* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */\n/* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */\n#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1))\nMINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags);\n\n/* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */\n/* Returns 1 on success or 0 on failure. */\ntypedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser);\nMINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags);\n\nstruct tinfl_decompressor_tag;\ntypedef struct tinfl_decompressor_tag tinfl_decompressor;\n\n#ifndef MINIZ_NO_MALLOC\n/* Allocate the tinfl_decompressor structure in C so that */\n/* non-C language bindings to tinfl_ API don't need to worry about */\n/* structure size and allocation mechanism. */\nMINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void);\nMINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp);\n#endif\n\n/* Max size of LZ dictionary. */\n#define TINFL_LZ_DICT_SIZE 32768\n\n/* Return status. */\ntypedef enum {\n    /* This flags indicates the inflator needs 1 or more input bytes to make forward progress, but the caller is indicating that no more are available. The compressed data */\n    /* is probably corrupted. If you call the inflator again with more bytes it'll try to continue processing the input but this is a BAD sign (either the data is corrupted or you called it incorrectly). */\n    /* If you call it again with no input you'll just get TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS again. */\n    TINFL_STATUS_FAILED_CANNOT_MAKE_PROGRESS = -4,\n\n    /* This flag indicates that one or more of the input parameters was obviously bogus. (You can try calling it again, but if you get this error the calling code is wrong.) */\n    TINFL_STATUS_BAD_PARAM = -3,\n\n    /* This flags indicate the inflator is finished but the adler32 check of the uncompressed data didn't match. If you call it again it'll return TINFL_STATUS_DONE. */\n    TINFL_STATUS_ADLER32_MISMATCH = -2,\n\n    /* This flags indicate the inflator has somehow failed (bad code, corrupted input, etc.). If you call it again without resetting via tinfl_init() it it'll just keep on returning the same status failure code. */\n    TINFL_STATUS_FAILED = -1,\n\n    /* Any status code less than TINFL_STATUS_DONE must indicate a failure. */\n\n    /* This flag indicates the inflator has returned every byte of uncompressed data that it can, has consumed every byte that it needed, has successfully reached the end of the deflate stream, and */\n    /* if zlib headers and adler32 checking enabled that it has successfully checked the uncompressed data's adler32. If you call it again you'll just get TINFL_STATUS_DONE over and over again. */\n    TINFL_STATUS_DONE = 0,\n\n    /* This flag indicates the inflator MUST have more input data (even 1 byte) before it can make any more forward progress, or you need to clear the TINFL_FLAG_HAS_MORE_INPUT */\n    /* flag on the next call if you don't have any more source data. If the source data was somehow corrupted it's also possible (but unlikely) for the inflator to keep on demanding input to */\n    /* proceed, so be sure to properly set the TINFL_FLAG_HAS_MORE_INPUT flag. */\n    TINFL_STATUS_NEEDS_MORE_INPUT = 1,\n\n    /* This flag indicates the inflator definitely has 1 or more bytes of uncompressed data available, but it cannot write this data into the output buffer. */\n    /* Note if the source compressed data was corrupted it's possible for the inflator to return a lot of uncompressed data to the caller. I've been assuming you know how much uncompressed data to expect */\n    /* (either exact or worst case) and will stop calling the inflator and fail after receiving too much. In pure streaming scenarios where you have no idea how many bytes to expect this may not be possible */\n    /* so I may need to add some code to address this. */\n    TINFL_STATUS_HAS_MORE_OUTPUT = 2\n} tinfl_status;\n\n/* Initializes the decompressor to its initial state. */\n#define tinfl_init(r)     \\\n    do                    \\\n    {                     \\\n        (r)->m_state = 0; \\\n    }                     \\\n    MZ_MACRO_END\n#define tinfl_get_adler32(r) (r)->m_check_adler32\n\n/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */\n/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */\nMINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags);\n\n/* Internal/private bits follow. */\nenum\n{\n    TINFL_MAX_HUFF_TABLES = 3,\n    TINFL_MAX_HUFF_SYMBOLS_0 = 288,\n    TINFL_MAX_HUFF_SYMBOLS_1 = 32,\n    TINFL_MAX_HUFF_SYMBOLS_2 = 19,\n    TINFL_FAST_LOOKUP_BITS = 10,\n    TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS\n};\n\ntypedef struct\n{\n    mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0];\n    mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2];\n} tinfl_huff_table;\n\n#if MINIZ_HAS_64BIT_REGISTERS\n#define TINFL_USE_64BIT_BITBUF 1\n#else\n#define TINFL_USE_64BIT_BITBUF 0\n#endif\n\n#if TINFL_USE_64BIT_BITBUF\ntypedef mz_uint64 tinfl_bit_buf_t;\n#define TINFL_BITBUF_SIZE (64)\n#else\ntypedef mz_uint32 tinfl_bit_buf_t;\n#define TINFL_BITBUF_SIZE (32)\n#endif\n\nstruct tinfl_decompressor_tag\n{\n    mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES];\n    tinfl_bit_buf_t m_bit_buf;\n    size_t m_dist_from_out_buf_start;\n    tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES];\n    mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137];\n};\n\n#ifdef __cplusplus\n}\n#endif\n \n#pragma once\n\n\n/* ------------------- ZIP archive reading/writing */\n\n#ifndef MINIZ_NO_ARCHIVE_APIS\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nenum\n{\n    /* Note: These enums can be reduced as needed to save memory or stack space - they are pretty conservative. */\n    MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024,\n    MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 512,\n    MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 512\n};\n\ntypedef struct\n{\n    /* Central directory file index. */\n    mz_uint32 m_file_index;\n\n    /* Byte offset of this entry in the archive's central directory. Note we currently only support up to UINT_MAX or less bytes in the central dir. */\n    mz_uint64 m_central_dir_ofs;\n\n    /* These fields are copied directly from the zip's central dir. */\n    mz_uint16 m_version_made_by;\n    mz_uint16 m_version_needed;\n    mz_uint16 m_bit_flag;\n    mz_uint16 m_method;\n\n#ifndef MINIZ_NO_TIME\n    MZ_TIME_T m_time;\n#endif\n\n    /* CRC-32 of uncompressed data. */\n    mz_uint32 m_crc32;\n\n    /* File's compressed size. */\n    mz_uint64 m_comp_size;\n\n    /* File's uncompressed size. Note, I've seen some old archives where directory entries had 512 bytes for their uncompressed sizes, but when you try to unpack them you actually get 0 bytes. */\n    mz_uint64 m_uncomp_size;\n\n    /* Zip internal and external file attributes. */\n    mz_uint16 m_internal_attr;\n    mz_uint32 m_external_attr;\n\n    /* Entry's local header file offset in bytes. */\n    mz_uint64 m_local_header_ofs;\n\n    /* Size of comment in bytes. */\n    mz_uint32 m_comment_size;\n\n    /* MZ_TRUE if the entry appears to be a directory. */\n    mz_bool m_is_directory;\n\n    /* MZ_TRUE if the entry uses encryption/strong encryption (which miniz_zip doesn't support) */\n    mz_bool m_is_encrypted;\n\n    /* MZ_TRUE if the file is not encrypted, a patch file, and if it uses a compression method we support. */\n    mz_bool m_is_supported;\n\n    /* Filename. If string ends in '/' it's a subdirectory entry. */\n    /* Guaranteed to be zero terminated, may be truncated to fit. */\n    char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE];\n\n    /* Comment field. */\n    /* Guaranteed to be zero terminated, may be truncated to fit. */\n    char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE];\n\n} mz_zip_archive_file_stat;\n\ntypedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n);\ntypedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n);\ntypedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque);\n\nstruct mz_zip_internal_state_tag;\ntypedef struct mz_zip_internal_state_tag mz_zip_internal_state;\n\ntypedef enum {\n    MZ_ZIP_MODE_INVALID = 0,\n    MZ_ZIP_MODE_READING = 1,\n    MZ_ZIP_MODE_WRITING = 2,\n    MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3\n} mz_zip_mode;\n\ntypedef enum {\n    MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100,\n    MZ_ZIP_FLAG_IGNORE_PATH = 0x0200,\n    MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400,\n    MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800,\n    MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG = 0x1000, /* if enabled, mz_zip_reader_locate_file() will be called on each file as its validated to ensure the func finds the file in the central dir (intended for testing) */\n    MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY = 0x2000,     /* validate the local headers, but don't decompress the entire file and check the crc32 */\n    MZ_ZIP_FLAG_WRITE_ZIP64 = 0x4000,               /* always use the zip64 file format, instead of the original zip file format with automatic switch to zip64. Use as flags parameter with mz_zip_writer_init*_v2 */\n    MZ_ZIP_FLAG_WRITE_ALLOW_READING = 0x8000,\n    MZ_ZIP_FLAG_ASCII_FILENAME = 0x10000,\n    /*After adding a compressed file, seek back\n    to local file header and set the correct sizes*/\n    MZ_ZIP_FLAG_WRITE_HEADER_SET_SIZE = 0x20000\n} mz_zip_flags;\n\ntypedef enum {\n    MZ_ZIP_TYPE_INVALID = 0,\n    MZ_ZIP_TYPE_USER,\n    MZ_ZIP_TYPE_MEMORY,\n    MZ_ZIP_TYPE_HEAP,\n    MZ_ZIP_TYPE_FILE,\n    MZ_ZIP_TYPE_CFILE,\n    MZ_ZIP_TOTAL_TYPES\n} mz_zip_type;\n\n/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or modify this enum. */\ntypedef enum {\n    MZ_ZIP_NO_ERROR = 0,\n    MZ_ZIP_UNDEFINED_ERROR,\n    MZ_ZIP_TOO_MANY_FILES,\n    MZ_ZIP_FILE_TOO_LARGE,\n    MZ_ZIP_UNSUPPORTED_METHOD,\n    MZ_ZIP_UNSUPPORTED_ENCRYPTION,\n    MZ_ZIP_UNSUPPORTED_FEATURE,\n    MZ_ZIP_FAILED_FINDING_CENTRAL_DIR,\n    MZ_ZIP_NOT_AN_ARCHIVE,\n    MZ_ZIP_INVALID_HEADER_OR_CORRUPTED,\n    MZ_ZIP_UNSUPPORTED_MULTIDISK,\n    MZ_ZIP_DECOMPRESSION_FAILED,\n    MZ_ZIP_COMPRESSION_FAILED,\n    MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE,\n    MZ_ZIP_CRC_CHECK_FAILED,\n    MZ_ZIP_UNSUPPORTED_CDIR_SIZE,\n    MZ_ZIP_ALLOC_FAILED,\n    MZ_ZIP_FILE_OPEN_FAILED,\n    MZ_ZIP_FILE_CREATE_FAILED,\n    MZ_ZIP_FILE_WRITE_FAILED,\n    MZ_ZIP_FILE_READ_FAILED,\n    MZ_ZIP_FILE_CLOSE_FAILED,\n    MZ_ZIP_FILE_SEEK_FAILED,\n    MZ_ZIP_FILE_STAT_FAILED,\n    MZ_ZIP_INVALID_PARAMETER,\n    MZ_ZIP_INVALID_FILENAME,\n    MZ_ZIP_BUF_TOO_SMALL,\n    MZ_ZIP_INTERNAL_ERROR,\n    MZ_ZIP_FILE_NOT_FOUND,\n    MZ_ZIP_ARCHIVE_TOO_LARGE,\n    MZ_ZIP_VALIDATION_FAILED,\n    MZ_ZIP_WRITE_CALLBACK_FAILED,\n    MZ_ZIP_TOTAL_ERRORS\n} mz_zip_error;\n\ntypedef struct\n{\n    mz_uint64 m_archive_size;\n    mz_uint64 m_central_directory_file_ofs;\n\n    /* We only support up to UINT32_MAX files in zip64 mode. */\n    mz_uint32 m_total_files;\n    mz_zip_mode m_zip_mode;\n    mz_zip_type m_zip_type;\n    mz_zip_error m_last_error;\n\n    mz_uint64 m_file_offset_alignment;\n\n    mz_alloc_func m_pAlloc;\n    mz_free_func m_pFree;\n    mz_realloc_func m_pRealloc;\n    void *m_pAlloc_opaque;\n\n    mz_file_read_func m_pRead;\n    mz_file_write_func m_pWrite;\n    mz_file_needs_keepalive m_pNeeds_keepalive;\n    void *m_pIO_opaque;\n\n    mz_zip_internal_state *m_pState;\n\n} mz_zip_archive;\n\ntypedef struct\n{\n    mz_zip_archive *pZip;\n    mz_uint flags;\n\n    int status;\n#ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS\n    mz_uint file_crc32;\n#endif\n    mz_uint64 read_buf_size, read_buf_ofs, read_buf_avail, comp_remaining, out_buf_ofs, cur_file_ofs;\n    mz_zip_archive_file_stat file_stat;\n    void *pRead_buf;\n    void *pWrite_buf;\n\n    size_t out_blk_remain;\n\n    tinfl_decompressor inflator;\n\n} mz_zip_reader_extract_iter_state;\n\n/* -------- ZIP reading */\n\n/* Inits a ZIP archive reader. */\n/* These functions read and validate the archive's central directory. */\nMINIZ_EXPORT mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags);\n\nMINIZ_EXPORT mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags);\n\n#ifndef MINIZ_NO_STDIO\n/* Read a archive from a disk file. */\n/* file_start_ofs is the file offset where the archive actually begins, or 0. */\n/* actual_archive_size is the true total size of the archive, which may be smaller than the file's actual size on disk. If zero the entire file is treated as the archive. */\nMINIZ_EXPORT mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags);\nMINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size);\n\n/* Read an archive from an already opened FILE, beginning at the current file position. */\n/* The archive is assumed to be archive_size bytes long. If archive_size is 0, then the entire rest of the file is assumed to contain the archive. */\n/* The FILE will NOT be closed when mz_zip_reader_end() is called. */\nMINIZ_EXPORT mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags);\n#endif\n\n/* Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. */\nMINIZ_EXPORT mz_bool mz_zip_reader_end(mz_zip_archive *pZip);\n\n/* -------- ZIP reading or writing */\n\n/* Clears a mz_zip_archive struct to all zeros. */\n/* Important: This must be done before passing the struct to any mz_zip functions. */\nMINIZ_EXPORT void mz_zip_zero_struct(mz_zip_archive *pZip);\n\nMINIZ_EXPORT mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip);\nMINIZ_EXPORT mz_zip_type mz_zip_get_type(mz_zip_archive *pZip);\n\n/* Returns the total number of files in the archive. */\nMINIZ_EXPORT mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip);\n\nMINIZ_EXPORT mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip);\nMINIZ_EXPORT mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip);\nMINIZ_EXPORT MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip);\n\n/* Reads n bytes of raw archive data, starting at file offset file_ofs, to pBuf. */\nMINIZ_EXPORT size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n);\n\n/* All mz_zip funcs set the m_last_error field in the mz_zip_archive struct. These functions retrieve/manipulate this field. */\n/* Note that the m_last_error functionality is not thread safe. */\nMINIZ_EXPORT mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num);\nMINIZ_EXPORT mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip);\nMINIZ_EXPORT mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip);\nMINIZ_EXPORT mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip);\nMINIZ_EXPORT const char *mz_zip_get_error_string(mz_zip_error mz_err);\n\n/* MZ_TRUE if the archive file entry is a directory entry. */\nMINIZ_EXPORT mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index);\n\n/* MZ_TRUE if the file is encrypted/strong encrypted. */\nMINIZ_EXPORT mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index);\n\n/* MZ_TRUE if the compression method is supported, and the file is not encrypted, and the file is not a compressed patch file. */\nMINIZ_EXPORT mz_bool mz_zip_reader_is_file_supported(mz_zip_archive *pZip, mz_uint file_index);\n\n/* Retrieves the filename of an archive file entry. */\n/* Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. */\nMINIZ_EXPORT mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size);\n\n/* Attempts to locates a file in the archive's central directory. */\n/* Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH */\n/* Returns -1 if the file cannot be found. */\nMINIZ_EXPORT int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags);\nMINIZ_EXPORT mz_bool mz_zip_reader_locate_file_v2(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *file_index);\n\n/* Returns detailed information about an archive file entry. */\nMINIZ_EXPORT mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat);\n\n/* MZ_TRUE if the file is in zip64 format. */\n/* A file is considered zip64 if it contained a zip64 end of central directory marker, or if it contained any zip64 extended file information fields in the central directory. */\nMINIZ_EXPORT mz_bool mz_zip_is_zip64(mz_zip_archive *pZip);\n\n/* Returns the total central directory size in bytes. */\n/* The current max supported size is <= MZ_UINT32_MAX. */\nMINIZ_EXPORT size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip);\n\n/* Extracts a archive file to a memory buffer using no memory allocation. */\n/* There must be at least enough room on the stack to store the inflator's state (~34KB or so). */\nMINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);\nMINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size);\n\n/* Extracts a archive file to a memory buffer. */\nMINIZ_EXPORT mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags);\nMINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags);\n\n/* Extracts a archive file to a dynamically allocated heap buffer. */\n/* The memory will be allocated via the mz_zip_archive's alloc/realloc functions. */\n/* Returns NULL and sets the last error on failure. */\nMINIZ_EXPORT void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags);\nMINIZ_EXPORT void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags);\n\n/* Extracts a archive file using a callback function to output the file's data. */\nMINIZ_EXPORT mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);\nMINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags);\n\n/* Extract a file iteratively */\nMINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags);\nMINIZ_EXPORT mz_zip_reader_extract_iter_state* mz_zip_reader_extract_file_iter_new(mz_zip_archive *pZip, const char *pFilename, mz_uint flags);\nMINIZ_EXPORT size_t mz_zip_reader_extract_iter_read(mz_zip_reader_extract_iter_state* pState, void* pvBuf, size_t buf_size);\nMINIZ_EXPORT mz_bool mz_zip_reader_extract_iter_free(mz_zip_reader_extract_iter_state* pState);\n\n#ifndef MINIZ_NO_STDIO\n/* Extracts a archive file to a disk file and sets its last accessed and modified times. */\n/* This function only extracts files, not archive directory records. */\nMINIZ_EXPORT mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags);\nMINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags);\n\n/* Extracts a archive file starting at the current position in the destination FILE stream. */\nMINIZ_EXPORT mz_bool mz_zip_reader_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *File, mz_uint flags);\nMINIZ_EXPORT mz_bool mz_zip_reader_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags);\n#endif\n\n#if 0\n/* TODO */\n\ttypedef void *mz_zip_streaming_extract_state_ptr;\n\tmz_zip_streaming_extract_state_ptr mz_zip_streaming_extract_begin(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags);\n\tuint64_t mz_zip_streaming_extract_get_size(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);\n\tuint64_t mz_zip_streaming_extract_get_cur_ofs(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);\n\tmz_bool mz_zip_streaming_extract_seek(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, uint64_t new_ofs);\n\tsize_t mz_zip_streaming_extract_read(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState, void *pBuf, size_t buf_size);\n\tmz_bool mz_zip_streaming_extract_end(mz_zip_archive *pZip, mz_zip_streaming_extract_state_ptr pState);\n#endif\n\n/* This function compares the archive's local headers, the optional local zip64 extended information block, and the optional descriptor following the compressed data vs. the data in the central directory. */\n/* It also validates that each file can be successfully uncompressed unless the MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY is specified. */\nMINIZ_EXPORT mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags);\n\n/* Validates an entire archive by calling mz_zip_validate_file() on each file. */\nMINIZ_EXPORT mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags);\n\n/* Misc utils/helpers, valid for ZIP reading or writing */\nMINIZ_EXPORT mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr);\nMINIZ_EXPORT mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr);\n\n/* Universal end function - calls either mz_zip_reader_end() or mz_zip_writer_end(). */\nMINIZ_EXPORT mz_bool mz_zip_end(mz_zip_archive *pZip);\n\n/* -------- ZIP writing */\n\n#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS\n\n/* Inits a ZIP archive writer. */\n/*Set pZip->m_pWrite (and pZip->m_pIO_opaque) before calling mz_zip_writer_init or mz_zip_writer_init_v2*/\n/*The output is streamable, i.e. file_ofs in mz_file_write_func always increases only by n*/\nMINIZ_EXPORT mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size);\nMINIZ_EXPORT mz_bool mz_zip_writer_init_v2(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags);\n\nMINIZ_EXPORT mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size);\nMINIZ_EXPORT mz_bool mz_zip_writer_init_heap_v2(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags);\n\n#ifndef MINIZ_NO_STDIO\nMINIZ_EXPORT mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning);\nMINIZ_EXPORT mz_bool mz_zip_writer_init_file_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags);\nMINIZ_EXPORT mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags);\n#endif\n\n/* Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. */\n/* For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. */\n/* For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). */\n/* Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. */\n/* Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before */\n/* the archive is finalized the file's central directory will be hosed. */\nMINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename);\nMINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags);\n\n/* Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. */\n/* To add a directory entry, call this method with an archive name ending in a forwardslash with an empty buffer. */\n/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */\nMINIZ_EXPORT mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags);\n\n/* Like mz_zip_writer_add_mem(), except you can specify a file comment field, and optionally supply the function with already compressed data. */\n/* uncomp_size/uncomp_crc32 are only used if the MZ_ZIP_FLAG_COMPRESSED_DATA flag is specified. */\nMINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,\n                                              mz_uint64 uncomp_size, mz_uint32 uncomp_crc32);\n\nMINIZ_EXPORT mz_bool mz_zip_writer_add_mem_ex_v2(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,\n                                                 mz_uint64 uncomp_size, mz_uint32 uncomp_crc32, MZ_TIME_T *last_modified, const char *user_extra_data_local, mz_uint user_extra_data_local_len,\n                                                 const char *user_extra_data_central, mz_uint user_extra_data_central_len);\n\n/* Adds the contents of a file to an archive. This function also records the disk file's modified time into the archive. */\n/* File data is supplied via a read callback function. User mz_zip_writer_add_(c)file to add a file directly.*/\nMINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback(mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void* callback_opaque, mz_uint64 max_size,\n\tconst MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len,\n\tconst char *user_extra_data_central, mz_uint user_extra_data_central_len);\n\n\n#ifndef MINIZ_NO_STDIO\n/* Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. */\n/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */\nMINIZ_EXPORT mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);\n\n/* Like mz_zip_writer_add_file(), except the file data is read from the specified FILE stream. */\nMINIZ_EXPORT mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size,\n                                const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len,\n                                const char *user_extra_data_central, mz_uint user_extra_data_central_len);\n#endif\n\n/* Adds a file to an archive by fully cloning the data from another archive. */\n/* This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data (it may add or modify the zip64 local header extra data field), and the optional descriptor following the compressed data. */\nMINIZ_EXPORT mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index);\n\n/* Finalizes the archive by writing the central directory records followed by the end of central directory record. */\n/* After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). */\n/* An archive must be manually finalized by calling this function for it to be valid. */\nMINIZ_EXPORT mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip);\n\n/* Finalizes a heap archive, returning a poiner to the heap block and its size. */\n/* The heap block will be allocated using the mz_zip_archive's alloc/realloc callbacks. */\nMINIZ_EXPORT mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize);\n\n/* Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. */\n/* Note for the archive to be valid, it *must* have been finalized before ending (this function will not do it for you). */\nMINIZ_EXPORT mz_bool mz_zip_writer_end(mz_zip_archive *pZip);\n\n/* -------- Misc. high-level helper functions: */\n\n/* mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. */\n/* Note this is NOT a fully safe operation. If it crashes or dies in some way your archive can be left in a screwed up state (without a central directory). */\n/* level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. */\n/* TODO: Perhaps add an option to leave the existing central dir in place in case the add dies? We could then truncate the file (so the old central dir would be at the end) if something goes wrong. */\nMINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags);\nMINIZ_EXPORT mz_bool mz_zip_add_mem_to_archive_file_in_place_v2(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr);\n\n/* Reads a single file from an archive into a heap block. */\n/* If pComment is not NULL, only the file with the specified comment will be extracted. */\n/* Returns NULL on failure. */\nMINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags);\nMINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr);\n\n#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS */\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* MINIZ_NO_ARCHIVE_APIS */\n"
  },
  {
    "path": "src/morph/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/morph/intr.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Morph inspect / layout / interleave\n * ===================================\n *\n * Inspect builds a hierarchical view over a morph controller and its targets,\n * suitable for GPU-side blending. The view mirrors the diagram in\n * controller.h:\n *\n *   AkMorphInspectView\n *     ├─ base    : AkMorphInspectTargetView                (always built)\n *     │             └─ morphable[]  : per primitive\n *     │                  └─ input[] : per semantic (POSITION, NORMAL, ...)\n *     └─ targets : AkMorphInspectTargetView linked list    (one per blend shape;\n *                  base is prepended here when includeBaseShape == true)\n *\n * Inspect is static analysis: collects inputs, computes per-vertex stride and\n * per-target buffer size. It does NOT apply weights — those are runtime values\n * (see ak_morphHasOverride and the runtime evaluator/uniform path).\n *\n * Multi-primitive: each TargetView contains one Morphable per primitive of\n * the base mesh; multi-primitive support is structural here. Whether the\n * loaders populate multi-primitive chains is a separate concern.\n */\n\n#include \"../common.h\"\n#include \"../accessor.h\"\n#include <string.h>\n\n/*============================================================================\n * Inspect helpers (file-local)\n *============================================================================*/\n\nAK_EXPORT\nconst AkMorphPreset*\nak_morphPresetByName(AkMorph    * __restrict morph,\n                     const char * __restrict name) {\n  AkMorphPreset *preset;\n  uint32_t       i;\n\n  if (!morph || !name || !morph->presets || morph->presetCount == 0)\n    return NULL;\n\n  preset = morph->presets;\n  for (i = 0; i < morph->presetCount; i++) {\n    if (preset[i].name && strcmp(preset[i].name, name) == 0)\n      return &preset[i];\n  }\n\n  return NULL;\n}\n\nAK_EXPORT\nbool\nak_morphApplyPreset(AkMorph    * __restrict morph,\n                    const char * __restrict presetName,\n                    float      * __restrict outWeights,\n                    uint32_t                capacity) {\n  const AkMorphPreset *preset;\n  AkFloatArray        *weights;\n  uint32_t             i;\n\n  if (!morph || !outWeights || capacity < morph->targetCount)\n    return false;\n\n  preset = ak_morphPresetByName(morph, presetName);\n  if (!preset || !(weights = preset->weights))\n    return false;\n\n  if (weights->count != morph->targetCount)\n    return false;\n\n  for (i = 0; i < morph->targetCount; i++)\n    outWeights[i] = weights->items[i];\n\n  return true;\n}\n\n/**\n * @brief initial weight for the i-th morph target.\n *\n * Precedence:\n *   morph.defaultWeights[idx]  → controller-level default (highest)\n *   mesh.weights[idx]           → mesh-level default\n *   0.0                         → fallback\n *\n * Instance-level overrideWeights is intentionally NOT consulted here:\n * inspect is static prep, override is a runtime concern.\n */\nAK_INLINE\nfloat\nak_morphInspect_initialWeight(const AkMorph *morph,\n                              const AkMesh  *mesh,\n                              uint32_t       targetIdx) {\n  AkFloatArray *arr;\n\n  if ((arr = morph->defaultWeights) && targetIdx < arr->count)\n    return arr->items[targetIdx];\n\n  if (mesh && (arr = mesh->weights) && targetIdx < arr->count)\n    return arr->items[targetIdx];\n\n  return 0.0f;\n}\n\n/**\n * @brief true iff the input semantic is in the desired filter (or no filter).\n */\nAK_INLINE\nbool\nak_morphInspect_isDesired(AkInputSemantic   sem,\n                          AkInputSemantic  *desired,\n                          uint8_t           desiredCount) {\n  uint8_t i;\n\n  if (desiredCount == 0) return true;\n\n  for (i = 0; i < desiredCount; i++) {\n    if (desired[i] == sem) return true;\n  }\n  return false;\n}\n\n/**\n * @brief true iff `inp` matches one of the base inputs by (semantic, set).\n */\nAK_INLINE\nbool\nak_morphInspect_inBase(const AkInput              *inp,\n                       const AkMorphInspectMorphable *base) {\n  AkMorphInspectInput *bi;\n  AkInput             *binp;\n\n  if (!inp || !base) return false;\n\n  for (bi = base->input; bi; bi = bi->next) {\n    binp = bi->input;\n    if (binp->semantic == inp->semantic && binp->set == inp->set)\n      return true;\n  }\n  return false;\n}\n\n/**\n * @brief append one AkMorphInspectInput to a morphable's input chain.\n */\nAK_INLINE\nAkMorphInspectInput *\nak_morphInspect_appendInput(AkHeap                   *heap,\n                            AkMorphInspectMorphable  *m,\n                            AkMorphInspectInput     **last,\n                            AkInput                  *input,\n                            bool                      inBaseMesh) {\n  AkMorphInspectInput *ii;\n\n  ii             = ak_heap_calloc(heap, m, sizeof(*ii));\n  ii->input      = input;\n  ii->inBaseMesh = inBaseMesh;\n\n  AK_APPEND_FLINK(m->input, (*last), ii);\n  m->inputsCount++;\n\n  return ii;\n}\n\n/**\n * @brief allocate a Morphable, append to the TargetView's morphable chain.\n */\nAK_INLINE\nAkMorphInspectMorphable *\nak_morphInspect_appendMorphable(AkHeap                     *heap,\n                                AkMorphInspectTargetView   *tv,\n                                AkMorphInspectMorphable   **last,\n                                float                       weight) {\n  AkMorphInspectMorphable *m;\n\n  m         = ak_heap_calloc(heap, tv, sizeof(*m));\n  m->weight = weight;\n\n  AK_APPEND_FLINK(tv->morphable, (*last), m);\n  tv->nTargets++;       /* nTargets here = primitive count of this target view */\n\n  return m;\n}\n\n/**\n * @brief allocate a TargetView, append to the View's targets chain.\n */\nAK_INLINE\nAkMorphInspectTargetView *\nak_morphInspect_appendTargetView(AkHeap                      *heap,\n                                 AkMorphInspectView          *view,\n                                 AkMorphInspectTargetView   **last) {\n  AkMorphInspectTargetView *tv;\n\n  tv = ak_heap_calloc(heap, view, sizeof(*tv));\n  AK_APPEND_FLINK(view->targets, (*last), tv);\n  view->nTargets++;\n\n  return tv;\n}\n\n/**\n * @brief find the POSITION input in an input chain (NULL if absent).\n */\nAK_INLINE\nAkInput *\nak_morphInspect_findPosition(AkInput *first) {\n  AkInput *inp;\n\n  for (inp = first; inp; inp = inp->next) {\n    if (inp->semantic == AK_INPUT_POSITION) return inp;\n  }\n  return NULL;\n}\n\n/*============================================================================\n * ak_morphInspect\n *============================================================================*/\n\nAK_EXPORT\nAkResult\nak_morphInspect(AkGeometry * __restrict baseMesh,\n                AkMorph    * __restrict morph,\n                AkInputSemantic         desiredInputs[],\n                uint8_t                 desiredInputsCount,\n                bool                    includeBaseShape,\n                bool                    ignoreUncommonInputs) {\n  AkHeap                     *heap;\n  AkMorphInspectView         *view;\n  AkMorphInspectTargetView   *tv,        *lastTV;\n  AkMorphInspectMorphable    *m,         *lastM;\n  AkMorphInspectMorphable    *baseM,     *mIter;\n  AkMorphInspectInput        *lastInput;\n  AkMorphTarget              *target;\n  AkObject                   *gdataObj,  *targetObj;\n  AkMesh                     *mesh,      *targetMesh;\n  AkMeshPrimitive            *prim,      *targetPrim;\n  AkInput                    *inp,       *posInp;\n  AkAccessor                 *acc;\n  AkMorphable                *morphable;\n  AkGeometry                 *targetGeom;\n  AkFloatArray               *iw;\n  void                       *targetPtr;\n  uint32_t                    primIdx,   primCount;\n  uint32_t                    baseInputsCount;\n  uint32_t                    targetIdx;\n  uint32_t                    primVertCount, targetVertCount;\n  uint32_t                    primStride,    targetStride;\n  uint32_t                    expectedCount, i;\n  size_t                      baseBufOff, targetBufOff, tvSize;\n  bool                        inBase;\n\n  if (!baseMesh || !morph) return AK_ERR;\n\n  heap = ak_heap_getheap(morph);\n  view = ak_heap_calloc(heap, morph, sizeof(*view));\n\n  view->layout                = AK_MORPH_UNKNOWN;\n  view->includeBaseShape      = includeBaseShape;\n  view->ignoreUncommonInputs  = ignoreUncommonInputs;\n\n  /*========================================================================*/\n  /* Phase 1: validate base mesh                                            */\n  /*========================================================================*/\n\n  if (!(gdataObj = baseMesh->gdata)\n      || gdataObj->type != AK_GEOMETRY_MESH\n      || !(mesh    = ak_objGet(gdataObj))\n      || !(prim    = mesh->primitive)) {\n    return AK_ERR;\n  }\n\n  primCount = mesh->primitiveCount;\n  if (primCount == 0) return AK_ERR;\n\n  /*========================================================================*/\n  /* Phase 2: build base TargetView                                         */\n  /*                                                                        */\n  /* Always built. Even when includeBaseShape == false, base inputs are     */\n  /* needed to match against target inputs (ignoreUncommonInputs filter).   */\n  /*========================================================================*/\n\n  tv          = ak_heap_calloc(heap, view, sizeof(*tv));\n  view->base  = tv;\n\n  baseInputsCount = 0;\n  lastM           = NULL;\n  baseBufOff      = 0;\n\n  primIdx = 0;\n  for (prim = mesh->primitive; prim && primIdx < primCount;\n       prim = prim->next, primIdx++) {\n    if (!prim->pos || !(acc = prim->pos->accessor)) continue;\n\n    primVertCount = (uint32_t)acc->count;\n    if (primVertCount == 0) continue;\n\n    m              = ak_morphInspect_appendMorphable(heap, tv, &lastM, 0.0f);\n    m->vertexCount = primVertCount;\n    lastInput      = NULL;\n\n    primStride = 0;\n    for (inp = prim->input; inp; inp = inp->next) {\n      if (!ak_morphInspect_isDesired(inp->semantic, desiredInputs,\n                                     desiredInputsCount))\n        continue;\n      if (!(acc = inp->accessor) || acc->count != primVertCount)\n        continue;\n\n      ak_morphInspect_appendInput(heap, m, &lastInput, inp, true);\n      primStride += (uint32_t)acc->fillByteSize;\n      baseInputsCount++;\n    }\n\n    m->stridePerVertex = primStride;\n    m->bufferOffset    = baseBufOff;\n    m->bufferSize      = (size_t)primStride * primVertCount;\n    baseBufOff        += m->bufferSize;\n\n    m->lastInput = lastInput;\n\n  }\n\n  if (baseInputsCount == 0) return AK_ERR;\n\n  /*========================================================================*/\n  /* Phase 3: build TargetView per AkMorphTarget                            */\n  /*========================================================================*/\n\n  lastTV    = NULL;\n  targetIdx = 0;\n\n  for (target = morph->target; target;\n       target = target->next, targetIdx++) {\n    if (!(targetObj = target->target)\n        || !(targetPtr = ak_objGet(targetObj)))\n      continue;\n\n    tv    = ak_morphInspect_appendTargetView(heap, view, &lastTV);\n    lastM = NULL;\n\n    targetBufOff = 0;\n    baseM        = view->base ? view->base->morphable : NULL;\n\n    /* polymorphic dispatch on AkMorphableType (kept as enum slot in\n       AkObject.type — see controller.h) */\n    switch (targetObj->type) {\n      case AK_MORPHABLE_MORPHABLE: {\n        /* glTF-style: AkMorphable chain, one per primitive. Walk\n           the target's morphable chain in lockstep with the base\n           morphables so each target primitive gets sized against\n           its corresponding base primitive's vertex count rather\n           than a single global anchor. */\n        morphable = targetPtr;\n        primIdx   = 0;\n\n        for (; morphable && primIdx < primCount;\n             morphable = morphable->next, primIdx++) {\n          expectedCount = baseM ? baseM->vertexCount : 0;\n\n          if (!(posInp = ak_morphInspect_findPosition(morphable->input))\n              || !(acc = posInp->accessor)\n              || (expectedCount > 0 && (uint32_t)acc->count != expectedCount))\n            goto stepBaseM_a;\n          targetVertCount = (uint32_t)acc->count;\n\n          m = ak_morphInspect_appendMorphable(\n              heap, tv, &lastM,\n              ak_morphInspect_initialWeight(morph, mesh, targetIdx));\n          m->vertexCount = targetVertCount;\n          lastInput      = NULL;\n\n          targetStride = 0;\n          for (inp = morphable->input; inp; inp = inp->next) {\n            if (!ak_morphInspect_isDesired(inp->semantic, desiredInputs,\n                                           desiredInputsCount))\n              continue;\n            if (!(acc = inp->accessor) || acc->count != targetVertCount)\n              continue;\n\n            inBase = ak_morphInspect_inBase(inp, baseM);\n            if (ignoreUncommonInputs && !inBase) continue;\n\n            ak_morphInspect_appendInput(heap, m, &lastInput, inp, inBase);\n            targetStride += (uint32_t)acc->fillByteSize;\n\n          }\n\n          m->stridePerVertex = targetStride;\n          m->bufferOffset    = targetBufOff;\n          m->bufferSize      = (size_t)targetStride * targetVertCount;\n          targetBufOff      += m->bufferSize;\n          m->lastInput       = lastInput;\n\nstepBaseM_a:\n          if (baseM) baseM = baseM->next;\n        }\n        break;\n      }\n\n      case AK_MORPHABLE_GEOMETRY: {\n        /* DAE-style: pointer-storage payload — wrap holds an AkGeometry*\n           in its inline slot. ak_objGetTarget hides the deref. */\n        targetGeom = ak_objGetTarget(targetObj);\n        if (!targetGeom\n            || !(gdataObj   = targetGeom->gdata)\n            || gdataObj->type != AK_GEOMETRY_MESH\n            || !(targetMesh = ak_objGet(gdataObj)))\n          continue;\n\n        primIdx = 0;\n        for (targetPrim = targetMesh->primitive;\n             targetPrim && primIdx < primCount;\n             targetPrim = targetPrim->next, primIdx++) {\n          expectedCount = baseM ? baseM->vertexCount : 0;\n\n          if (!targetPrim->pos\n              || !(acc = targetPrim->pos->accessor)\n              || (expectedCount > 0 && (uint32_t)acc->count != expectedCount))\n            goto stepBaseM_b;\n          targetVertCount = (uint32_t)acc->count;\n\n          m = ak_morphInspect_appendMorphable(\n              heap, tv, &lastM,\n              ak_morphInspect_initialWeight(morph, mesh, targetIdx));\n          m->vertexCount = targetVertCount;\n          lastInput      = NULL;\n\n          targetStride = 0;\n          for (inp = targetPrim->input; inp; inp = inp->next) {\n            if (!ak_morphInspect_isDesired(inp->semantic, desiredInputs,\n                                           desiredInputsCount))\n              continue;\n            if (!(acc = inp->accessor) || acc->count != targetVertCount)\n              continue;\n\n            inBase = ak_morphInspect_inBase(inp, baseM);\n            if (ignoreUncommonInputs && !inBase) continue;\n\n            ak_morphInspect_appendInput(heap, m, &lastInput, inp, inBase);\n            targetStride += (uint32_t)acc->fillByteSize;\n\n          }\n\n          m->stridePerVertex = targetStride;\n          m->bufferOffset    = targetBufOff;\n          m->bufferSize      = (size_t)targetStride * targetVertCount;\n          targetBufOff      += m->bufferSize;\n          m->lastInput       = lastInput;\n\nstepBaseM_b:\n          if (baseM) baseM = baseM->next;\n        }\n        break;\n      }\n\n      default:\n        /* unknown target kind — TargetView already linked, leave empty */\n        break;\n    }\n  }\n\n  /*========================================================================*/\n  /* Phase 4: prepend base to view->targets when includeBaseShape           */\n  /*========================================================================*/\n\n  if (includeBaseShape) {\n    view->base->next = view->targets;\n    view->targets    = view->base;\n    view->nTargets++;\n  }\n\n  /*========================================================================*/\n  /* Phase 5: target slice sizes                                            */\n  /*========================================================================*/\n\n  /* Per-morphable bufferSize is the source of truth — sum across\n     a target's morphables to get its slice size, then sum across\n     targets for the whole-buffer size. */\n  view->interleaveTotalBufferSize = 0;\n  for (tv = view->targets; tv; tv = tv->next) {\n    tvSize = 0;\n    for (mIter = tv->morphable; mIter; mIter = mIter->next) {\n      tvSize += mIter->bufferSize;\n    }\n    tv->interleaveBufferSize = tvSize;\n    view->interleaveTotalBufferSize += tvSize;\n  }\n\n  /* convenience cache: per-blend-shape initial weights for runtime\n     (length = morph->targetCount; base shape is NOT included here) */\n  if (morph->targetCount > 0) {\n    iw = ak_heap_calloc(heap, view,\n                        sizeof(*iw) + sizeof(AkFloat) * morph->targetCount);\n    iw->count = morph->targetCount;\n    for (i = 0; i < morph->targetCount; i++) {\n      iw->items[i] = ak_morphInspect_initialWeight(morph, mesh, i);\n    }\n    view->initialWeights = iw;\n  }\n\n  morph->inspectResult = view;\n  return AK_OK;\n}\n\n/*============================================================================\n * ak_morphInspectPrepareLayout\n *============================================================================*/\n\nAK_EXPORT\nAkResult\nak_morphInspectPrepareLayout(AkMorphInspectView * __restrict inspectView,\n                             AkMorphInterleaveLayout         layout) {\n  AkMorphInspectTargetView  *targetView, *base;\n  AkMorphInspectMorphable   *inspMorphable;\n  AkMorphInspectMorphable   *baseMorph;\n  AkMorphInspectInput       *tinp,       *tinpt;\n  AkInput                   *inp;\n  AkAccessor                *acc;\n  uint32_t                   inpOff;\n  bool                       includeBaseShape, ignoreUncommonInputs;\n\n  if (!inspectView\n      || !(base = inspectView->base)\n      || !(inspMorphable = base->morphable)\n      || !(tinp = inspMorphable->input))\n    return AK_ERR;\n\n  if (inspectView->layout == layout) return AK_OK;\n\n  includeBaseShape     = inspectView->includeBaseShape;\n  ignoreUncommonInputs = inspectView->ignoreUncommonInputs;\n\n  /* Layout model:\n   *\n   *   destBuff = [target0 slice][target1 slice][target2 slice] ...\n   *      target slice = [morphable0 sub-slice][morphable1 sub-slice] ...\n   *           sub-slice = vertexCount × stridePerVertex bytes\n   *\n   * `intrOffset` for each input is the byte offset *within* its\n   * morphable's stride row (0 .. morphable.stridePerVertex). The\n   * morphable's `bufferOffset` (set during inspect) places the\n   * sub-slice within its target's slice. The interleave pass\n   * advances `dst` by `targetView->interleaveBufferSize` per\n   * target, then for each morphable computes\n   *   `dst_morph = dst_target + morphable.bufferOffset`\n   * and writes inputs at\n   *   `dst_morph + input.intrOffset + stride*k`.\n   *\n   * P1P2N1N2 = within each morphable, walk inputs grouped by\n   *            base-input order (POSITION → NORMAL → TANGENT, ...)\n   * NATURAL  = within each morphable, walk inputs in authored\n   *            order.\n   */\n  switch (layout) {\n    case AK_MORPH_P1P2N1N2: {\n      for (targetView = inspectView->targets;\n           targetView;\n           targetView = targetView->next) {\n        baseMorph     = inspectView->base ? inspectView->base->morphable : NULL;\n        inspMorphable = targetView->morphable;\n        while (inspMorphable) {\n          inpOff = 0;\n\n          /* Use the paired base morphable's input ordering as the\n             template, when available; this gives target inputs\n             offsets matching base order. */\n          if (baseMorph) {\n            for (tinp = baseMorph->input; tinp; tinp = tinp->next) {\n              if (!tinp->inTarget && !includeBaseShape) continue;\n              for (tinpt = inspMorphable->input; tinpt; tinpt = tinpt->next) {\n                if (!(tinpt->input->semantic == tinp->input->semantic\n                      && tinpt->input->set == tinp->input->set)\n                    || (ignoreUncommonInputs && !tinpt->inBaseMesh))\n                  continue;\n\n                inp               = tinpt->input;\n                acc               = inp->accessor;\n                tinpt->intrOffset = inpOff;\n                inpOff           += (uint32_t)acc->fillByteSize;\n                break;  /* one target input assigned per base input */\n              }\n            }\n          }\n\n          /* Trailing target-only inputs (rare). */\n          if (!ignoreUncommonInputs) {\n            for (tinpt = inspMorphable->input; tinpt; tinpt = tinpt->next) {\n              if (tinpt->inBaseMesh) continue;\n              inp                = tinpt->input;\n              acc                = inp->accessor;\n              tinpt->intrOffset  = inpOff;\n              inpOff            += (uint32_t)acc->fillByteSize;\n            }\n          }\n\n          inspMorphable = inspMorphable->next;\n          if (baseMorph) baseMorph = baseMorph->next;\n        }\n      }\n      inspectView->layout = layout;\n      return AK_OK;\n    }\n\n    case AK_MORPH_NATURAL: {\n      /* Per-morphable scoped offsets in authored input order. */\n      for (targetView = inspectView->targets;\n           targetView;\n           targetView = targetView->next) {\n        for (inspMorphable = targetView->morphable;\n             inspMorphable;\n             inspMorphable = inspMorphable->next) {\n          inpOff = 0;\n          for (tinp = inspMorphable->input; tinp; tinp = tinp->next) {\n            inp              = tinp->input;\n            acc              = inp->accessor;\n            tinp->intrOffset = inpOff;\n            inpOff          += (uint32_t)acc->fillByteSize;\n          }\n        }\n      }\n      inspectView->layout = layout;\n      return AK_OK;\n    }\n\n    default:\n      break;\n  }\n\n  return AK_ERR;\n}\n\nstatic\nAkResult\nak_morphInterleaveInternal(AkGeometry         * __restrict baseMesh,\n                           AkMorph            * __restrict morph,\n                           AkMorphInterleaveLayout         layout,\n                           void               * __restrict destBuff,\n                           AkMorphProgressFn                progress,\n                           void               * __restrict userdata) {\n  AkMorphInspectView        *morphView;\n  AkMorphInspectTargetView  *targetView;\n  AkMorphInspectMorphable   *inspMorphable;\n  AkMorphInspectInput       *tinp;\n  AkInput                   *inp;\n  AkAccessor                *acc;\n  AkBuffer                  *buf;\n  char                      *src,        *dst;\n  char                      *targetDst,  *morphDst;\n  char                      *sp,         *dp;\n  uint32_t                   srcStride,  compSize;\n  uint32_t                   k,          intrOffset;\n  uint32_t                   mStride,    mCount;\n  uint32_t                   targetIdx;\n\n  /* lazy-inspect with default options if caller didn't run it explicitly */\n  if (!(morphView = morph->inspectResult)) {\n    if (ak_morphInspect(baseMesh, morph, NULL, 0, false, true) != AK_OK\n        || !(morphView = morph->inspectResult))\n      return AK_ERR;\n  }\n\n  if (layout != morphView->layout\n      && ak_morphInspectPrepareLayout(morphView, layout) != AK_OK)\n    return AK_ERR;\n\n  if (!(targetView = morphView->targets)) return AK_ERR;\n\n  dst = (char *)destBuff;\n  targetIdx = 0;\n\n  for (; targetView; targetView = targetView->next) {\n    if (progress && !progress(morph, targetIdx, morphView->nTargets, userdata))\n      return AK_ERR;\n\n    /* Per morphable: walk inputs, write at the morphable's\n       sub-slice using its own per-primitive stride and vertex\n       count. */\n    targetDst = dst;\n    for (inspMorphable = targetView->morphable;\n         inspMorphable && (tinp = inspMorphable->input);\n         inspMorphable = inspMorphable->next) {\n      morphDst = targetDst + inspMorphable->bufferOffset;\n      mStride  = inspMorphable->stridePerVertex;\n      mCount   = inspMorphable->vertexCount;\n\n      for (; tinp\n             && (inp = tinp->input)\n             && (acc = inp->accessor)\n             && (buf = acc->buffer)\n             && (src = (char *)buf->data + acc->byteOffset);\n           tinp = tinp->next) {\n        srcStride  = (uint32_t)acc->byteStride;\n        compSize   = (uint32_t)acc->fillByteSize;\n        intrOffset = tinp->intrOffset;\n\n        dp = morphDst + intrOffset;\n        sp = src;\n        switch (compSize) {\n          case 4:\n            for (k = 0; k < mCount; k++) {\n              memcpy(dp, sp, 4);\n              dp += mStride;\n              sp += srcStride;\n            }\n            break;\n          case 8:\n            for (k = 0; k < mCount; k++) {\n              memcpy(dp, sp, 8);\n              dp += mStride;\n              sp += srcStride;\n            }\n            break;\n          case 12:\n            for (k = 0; k < mCount; k++) {\n              memcpy(dp, sp, 12);\n              dp += mStride;\n              sp += srcStride;\n            }\n            break;\n          case 16:\n            for (k = 0; k < mCount; k++) {\n              memcpy(dp, sp, 16);\n              dp += mStride;\n              sp += srcStride;\n            }\n            break;\n          default:\n            for (k = 0; k < mCount; k++) {\n              memcpy(dp, sp, compSize);\n              dp += mStride;\n              sp += srcStride;\n            }\n            break;\n        }\n      }\n    }\n\n    /* Advance to the next target's slice. */\n    dst += targetView->interleaveBufferSize;\n    targetIdx++;\n  }\n\n  if (progress && !progress(morph, targetIdx, morphView->nTargets, userdata))\n    return AK_ERR;\n\n  return AK_OK;\n}\n\n/*============================================================================\n * ak_morphInterleave\n *============================================================================*/\n\nAK_EXPORT\nAkResult\nak_morphInterleave(AkGeometry * __restrict baseMesh,\n                   AkMorph    * __restrict morph,\n                   AkMorphInterleaveLayout layout,\n                   void       * __restrict destBuff) {\n  return ak_morphInterleaveInternal(baseMesh,\n                                    morph,\n                                    layout,\n                                    destBuff,\n                                    NULL,\n                                    NULL);\n}\n\nAK_EXPORT\nAkResult\nak_morphInterleaveWithProgress(AkGeometry         * __restrict baseMesh,\n                               AkMorph            * __restrict morph,\n                               AkMorphInterleaveLayout         layout,\n                               void               * __restrict destBuff,\n                               AkMorphProgressFn                progress,\n                               void               * __restrict userdata) {\n  return ak_morphInterleaveInternal(baseMesh,\n                                    morph,\n                                    layout,\n                                    destBuff,\n                                    progress,\n                                    userdata);\n}\n"
  },
  {
    "path": "src/node/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/node/node.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../../include/ak/assetkit.h\"\n#include \"../../include/ak/node.h\"\n#include \"../../include/ak/coord-util.h\"\n#include \"../../include/ak/instance.h\"\n#include <assert.h>\n#include <string.h>\n\nAK_EXPORT\nvoid\nak_addSubNode(AkNode * __restrict parent,\n              AkNode * __restrict subnode,\n              bool                fixCoordSys) {\n  assert(parent != subnode);\n\n  if (subnode->parent) {\n    if (subnode->parent->chld == subnode) {\n      if (subnode->next)\n        subnode->parent->chld = subnode->next;\n      else\n        subnode->parent->chld = subnode->prev;\n    }\n  }\n\n  if (subnode->next)\n    subnode->next->prev = subnode->prev;\n\n  if (subnode->prev)\n    subnode->prev->next = subnode->next;\n\n  if (parent->chld)\n    parent->chld->prev = subnode;\n\n  subnode->next   = parent->chld;\n  subnode->prev   = NULL;\n  subnode->parent = parent;\n  parent->chld    = subnode;\n\n  /* fix node transforms after attached to new node */\n  if (fixCoordSys)\n    ak_fixNodeCoordSys(subnode);\n}\n\nAK_EXPORT\nAkNode *\nak_nodeMake(AkDoc      * __restrict doc,\n            AkNode     * __restrict parent,\n            const char * name) {\n  AkHeap *heap;\n  AkNode *node;\n  void   *memparent;\n\n  if (!doc) return NULL;\n\n  heap      = ak_heap_getheap(doc);\n  memparent = parent ? (void *)parent : (void *)doc;\n  node      = ak_heap_calloc(heap, memparent, sizeof(*node));\n  node->visible = true;\n\n  if (name)\n    node->name = ak_heap_strdup(heap, node, name);\n\n  /* Attach to the parent's children chain. fixCoordSys=false — the\n     caller is presumed to know the desired orientation; baking\n     coord-sys conversions into a programmatically created node\n     would surprise more often than help. */\n  if (parent)\n    ak_addSubNode(parent, node, false);\n\n  return node;\n}\n\nAK_EXPORT\nAkNode *\nak_nodeFindChildByName(AkNode     * __restrict parent,\n                       const char * name) {\n  AkNode *child;\n\n  if (!parent || !name) return NULL;\n\n  for (child = parent->chld; child; child = child->next) {\n    if (child->name && strcmp(child->name, name) == 0)\n      return child;\n  }\n\n  return NULL;\n}\n\nAK_EXPORT\nAkNode *\nak_nodeFindOrMakeChild(AkDoc      * __restrict doc,\n                       AkNode     * __restrict parent,\n                       const char * name) {\n  AkNode *existing;\n\n  existing = ak_nodeFindChildByName(parent, name);\n  if (existing) return existing;\n\n  return ak_nodeMake(doc, parent, name);\n}\n\nAK_EXPORT\nAkInstanceBase *\nak_nodeAttachCamera(AkNode   * __restrict node,\n                    AkCamera * __restrict cam) {\n  AkHeap         *heap;\n  AkInstanceBase *inst, *head;\n\n  if (!node || !cam) return NULL;\n\n  heap       = ak_heap_getheap(node);\n  inst       = ak_instanceMake(heap, node, cam);\n  inst->type = AK_INSTANCE_CAMERA;\n  /* `ak_instanceMake` doesn't backfill `node`; do it here so\n     downstream APIs (`ak_instanceName`, list-unlink, move-to-subnode)\n     don't see a NULL parent. */\n  inst->node = node;\n\n  /* Chain in front of any existing camera instance(s) on the node.\n     Maintain `prev` on the existing head so the chain stays\n     doubly-linked. */\n  head         = node->camera;\n  inst->next   = head;\n  inst->prev   = NULL;\n  if (head) {\n    head->prev = inst;\n  }\n  node->camera = inst;\n\n  return inst;\n}\n\nAK_EXPORT\nAkInstanceBase *\nak_nodeAttachLight(AkNode  * __restrict node,\n                   AkLight * __restrict light) {\n  AkHeap         *heap;\n  AkInstanceBase *inst, *head;\n\n  if (!node || !light) return NULL;\n\n  heap       = ak_heap_getheap(node);\n  inst       = ak_instanceMake(heap, node, light);\n  inst->type = AK_INSTANCE_LIGHT;\n  inst->node = node;\n\n  head        = node->light;\n  inst->next  = head;\n  inst->prev  = NULL;\n  if (head) {\n    head->prev = inst;\n  }\n  node->light = inst;\n\n  return inst;\n}\n\nAK_EXPORT\nvoid\nak_nodeSetTransformMatrix(AkNode * __restrict node,\n                          const float matrix[16]) {\n  AkHeap   *heap;\n  AkObject *obj;\n  AkMatrix *mat;\n\n  if (!node || !matrix) return;\n\n  heap = ak_heap_getheap(node);\n\n  /* Wrap the matrix in a typed AkObject (AKT_MATRIX). The inline\n     payload `data[]` is sized to hold one AkMatrix; ak_objAlloc\n     also sets pData to point at it. */\n  obj = ak_objAlloc(heap, node, sizeof(AkMatrix), AKT_MATRIX, true);\n  mat = (AkMatrix *)ak_objGet(obj);\n  memcpy(mat->val, matrix, sizeof(float) * 16);\n\n  /* Lazily allocate the AkTransform shell — newly-created nodes\n     (e.g. via ak_nodeMake) have transform == NULL until the first\n     write. */\n  if (!node->transform)\n    node->transform = ak_heap_calloc(heap, node, sizeof(AkTransform));\n\n  /* Replace any existing transform item chain with the single\n     matrix. The AKT_MATRIX form composes the same world-pose, so\n     callers don't have to track a TRS chain themselves. */\n  node->transform->item = obj;\n}\n\nAK_EXPORT\nAkNode *\nak_sceneFindRoot(AkVisualScene * __restrict scene,\n                 const char * name) {\n  AkNode *node;\n\n  if (!scene || !name) return NULL;\n\n  for (node = scene->node; node; node = node->next) {\n    if (node->name && strcmp(node->name, name) == 0)\n      return node;\n  }\n\n  return NULL;\n}\n\nAK_EXPORT\nAkNode *\nak_sceneFindOrMakeRoot(AkDoc         * __restrict doc,\n                       AkVisualScene * __restrict scene,\n                       const char * name) {\n  AkHeap *heap;\n  AkNode *node;\n  AkNode *last;\n\n  if (!doc || !scene) return NULL;\n\n  if ((node = ak_sceneFindRoot(scene, name)))\n    return node;\n\n  /* Allocate the new root node parented on the visual scene so its\n     lifetime tracks the scene's. ak_nodeMake parents under another\n     AkNode, which is the wrong shape here — we want a root, not a\n     child — so we allocate directly. */\n  heap = ak_heap_getheap(doc);\n  node = ak_heap_calloc(heap, scene, sizeof(*node));\n  node->visible = true;\n  if (name)\n    node->name = ak_heap_strdup(heap, node, name);\n\n  /* Append to the visual scene's root chain. Empty scene → become\n     the head; otherwise walk to the tail and link in. */\n  if (!scene->node) {\n    scene->node = node;\n  } else {\n    for (last = scene->node; last->next; last = last->next) { }\n    last->next = node;\n    node->prev = last;\n  }\n\n  return node;\n}\n"
  },
  {
    "path": "src/platform/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME}\n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/platform/dylib.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _GNU_SOURCE\n#  define _GNU_SOURCE\n#endif\n\n#include \"dylib.h\"\n\n#ifdef AK_WINAPI\n#  include <windows.h>\n#else\n#  include <dlfcn.h>\n#endif\n#include <stdio.h>\n\nstatic const char AK_DYLIB_ANCHOR = 0;\n\nAK_HIDE\nvoid*\nak_dylib_open(const char * __restrict path) {\n  if (!path)\n    return NULL;\n\n#ifdef AK_WINAPI\n  return (void *)LoadLibraryA(path);\n#else\n  return dlopen(path, RTLD_LAZY | RTLD_LOCAL);\n#endif\n}\n\nstatic\nvoid*\nak_dylib_openSibling(const char * __restrict file) {\n  char   modpath[1024];\n  char   path[1024];\n  char  *sep;\n  char  *sep2;\n  size_t dirlen;\n  size_t filelen;\n  int    len;\n\n  if (!file)\n    return NULL;\n\n#ifdef AK_WINAPI\n  {\n    HMODULE mod;\n    DWORD   n;\n\n    mod = NULL;\n    if (!GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS\n                            | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,\n                            (LPCSTR)&AK_DYLIB_ANCHOR,\n                            &mod))\n      return NULL;\n\n    n = GetModuleFileNameA(mod, modpath, sizeof(modpath));\n    if (n == 0 || n >= sizeof(modpath))\n      return NULL;\n  }\n#else\n  {\n    Dl_info info;\n\n    if (!dladdr((const void *)&AK_DYLIB_ANCHOR, &info) || !info.dli_fname)\n      return NULL;\n\n    len = snprintf(modpath, sizeof(modpath), \"%s\", info.dli_fname);\n    if (len <= 0 || (size_t)len >= sizeof(modpath))\n      return NULL;\n  }\n#endif\n\n  sep  = strrchr(modpath, '/');\n  sep2 = strrchr(modpath, '\\\\');\n  if (!sep || (sep2 && sep2 > sep))\n    sep = sep2;\n  if (!sep)\n    return NULL;\n\n  dirlen  = (size_t)(sep - modpath) + 1;\n  filelen = strlen(file);\n  if (dirlen + filelen >= sizeof(path))\n    return NULL;\n\n  memcpy(path, modpath, dirlen);\n  memcpy(path + dirlen, file, filelen + 1);\n\n  return ak_dylib_open(path);\n}\n\nAK_HIDE\nvoid*\nak_dylib_openName(const char * __restrict name) {\n  char path[256];\n  void *lib;\n  int  len;\n\n  if (!name)\n    return NULL;\n\n#ifdef AK_WINAPI\n  len = snprintf(path, sizeof(path), \"%s.dll\", name);\n#elif defined(__APPLE__)\n  len = snprintf(path, sizeof(path), \"lib%s.dylib\", name);\n#else\n  len = snprintf(path, sizeof(path), \"lib%s.so\", name);\n#endif\n\n  if (len <= 0 || (size_t)len >= sizeof(path))\n    return NULL;\n\n  if ((lib = ak_dylib_openSibling(path)))\n    return lib;\n\n  return ak_dylib_open(path);\n}\n\nAK_HIDE\nvoid*\nak_dylib_sym(void       * __restrict lib,\n             const char * __restrict name) {\n  if (!lib || !name)\n    return NULL;\n\n#ifdef AK_WINAPI\n  return (void *)GetProcAddress((HMODULE)lib, name);\n#else\n  return dlsym(lib, name);\n#endif\n}\n\nAK_HIDE\nvoid\nak_dylib_close(void * __restrict lib) {\n  if (!lib)\n    return;\n\n#ifdef AK_WINAPI\n  FreeLibrary((HMODULE)lib);\n#else\n  dlclose(lib);\n#endif\n}\n"
  },
  {
    "path": "src/platform/dylib.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_platform_dylib_h\n#define ak_platform_dylib_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid*\nak_dylib_open(const char * __restrict path);\n\nAK_HIDE\nvoid*\nak_dylib_openName(const char * __restrict name);\n\nAK_HIDE\nvoid*\nak_dylib_sym(void       * __restrict lib,\n             const char * __restrict name);\n\nAK_HIDE\nvoid\nak_dylib_close(void * __restrict lib);\n\n#endif /* ak_platform_dylib_h */\n"
  },
  {
    "path": "src/profile.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n#include \"profile.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\nAkProfileType *ak__profileTypes;\nuint32_t       ak__profileTypesCount;\nchar           ak__platform[64];\n\nAkProfile*\nak_profile(struct AkEffect * __restrict effect,\n           AkProfile       * __restrict after) {\n  AkHeapNode   *hnodeParent, *hnode;\n  AkProfileType profileType;\n\n  hnodeParent = ak__alignof(effect);\n  profileType = ak_profileType(effect);\n\n  /* get next profile */\n  if (after) {\n    hnode = ak__alignof(after);\n    hnode = hnode->next;\n  } else {\n    hnode = hnodeParent->chld;\n  }\n\n  while (hnode) {\n    if (ak_typeidh(hnode) == AKT_PROFILE) {\n      AkProfile *profile;\n      profile = ak__alignas(hnode);\n      if (profile->type == profileType)\n        return profile;\n    }\n    hnode = hnode->next;\n  }\n\n  return NULL;\n}\n\nAkProfileType\nak_profileType(struct AkEffect * __restrict effect) {\n  AkProfileType defaultProfile;\n  bool          useEffectProfile;\n  uint32_t      i;\n  bool          foundProfile;\n\n  useEffectProfile = ak_opt_get(AK_OPT_EFFECT_PROFILE);\n  foundProfile     = false;\n\n  /* check if profile type is overridden by effect or not if option is set */\n  /* to use this the AK_OPT_EFFECT_PROFILE option must set to true         */\n  if (useEffectProfile) {\n    for (i = 0; i < ak__profileTypesCount; i++) {\n      if (effect->bestProfile == ak__profileTypes[i]) {\n        foundProfile = true;\n        break;\n      }\n    }\n\n    if (foundProfile)\n      return effect->bestProfile;\n  }\n\n  /* check default profile is supported or not */\n  defaultProfile = (AkProfileType)ak_opt_get(AK_OPT_DEFAULT_PROFILE);\n  for (i = 0; i < ak__profileTypesCount; i++) {\n    if (defaultProfile == ak__profileTypes[i]) {\n      foundProfile = true;\n      break;\n    }\n  }\n\n  if (foundProfile)\n    return defaultProfile;\n\n  /* return first supported profile */\n  /* default is profile_COMMON -> [0] or [count - 1] */\n  return ak__profileTypes[0];\n}\n\nuint32_t\nak_supportedProfiles(AkProfileType ** profileTypes) {\n  *profileTypes = ak__profileTypes;\n  return ak__profileTypesCount;\n}\n\nvoid\nak_setSupportedProfiles(AkProfileType profileTypes[],\n                        uint32_t      count) {\n  uint32_t i;\n  bool     foundCommon;\n\n  foundCommon = false;\n  for (i = 0; i < count; i++) {\n    if (profileTypes[i] == AK_PROFILE_TYPE_COMMON) {\n      foundCommon = true;\n      break;\n    }\n  }\n\n  if (!foundCommon)\n    count++;\n\n  if (ak__profileTypes)\n    ak_free(ak__profileTypes);\n\n  ak__profileTypes = ak_malloc(NULL, sizeof(AkProfileType) * count);\n  memcpy(ak__profileTypes, profileTypes, sizeof(AkProfileType) * count);\n\n  /* make sure that common is exist */\n  ak__profileTypes[count - 1] = AK_PROFILE_TYPE_COMMON;\n  ak__profileTypesCount = count;\n}\n\nconst char*\nak_platform(void) {\n  return ak__platform;\n}\n\nvoid\nak_setPlatform(const char platform[64]) {\n  strcpy(ak__platform, platform);\n}\n\nAK_HIDE\nvoid\nak_profile_init(void) {\n  /* default platform */\n  strcpy(ak__platform, \"GL\");\n\n  ak__profileTypes = ak_malloc(NULL, sizeof(AkProfileType));\n  *ak__profileTypes = AK_PROFILE_TYPE_COMMON;\n  ak__profileTypesCount = 1;\n}\n\nAK_HIDE\nvoid\nak_profile_deinit(void) {\n  ak_free(ak__profileTypes);\n}\n\nAK_EXPORT\nAkProfileCommon*\nak_getProfileCommon(struct AkEffect * __restrict effect) {\n  AkProfile *profile;\n\n  profile = effect->profile;\n  while (profile) {\n    if (profile->type == AK_PROFILE_TYPE_COMMON)\n      break;\n    profile = profile->next;\n  }\n\n  return (AkProfileCommon *)profile;\n}\n\nAK_EXPORT\nAkTechniqueFxCommon*\nak_getProfileTechniqueCommon(struct AkEffect * __restrict effect) {\n  AkProfileCommon     *profileCommon;\n  AkTechniqueFx       *techn;\n  AkTechniqueFxCommon *technCommon;\n\n  if ((profileCommon = ak_getProfileCommon(effect))\n      && (techn = profileCommon->technique)) {\n    return techn->common;\n  }\n\n  return NULL;\n}\n"
  },
  {
    "path": "src/profile.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_src_profile_h\n#define ak_src_profile_h\n\n#include \"common.h\"\n\nAK_HIDE\nvoid\nak_profile_init(void);\n\nAK_HIDE\nvoid\nak_profile_deinit(void);\n\n#endif /* ak_src_profile_h */\n"
  },
  {
    "path": "src/resc/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/resc/path.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../../include/ak/path.h\"\n#include \"../../include/ak/string.h\"\n\n#include <limits.h>\n\n#ifdef _MSC_VER\n#  ifndef PATH_MAX\n#    define PATH_MAX 260\n#  endif\n#endif\n\n#define CHR_SLASH       '/'\n#define CHR_BACK_SLASH  '\\\\'\n#define CHR_COLON       ':'\n#define STR_SLASH       \"/\"\n#define APPEND_SLASH                                                          \\\n  do {                                                                        \\\n    *buf++ = CHR_SLASH;                                                       \\\n    len++;                                                                    \\\n  } while (0)\n\n#define AK_STRTRM_SET(X)                                                      \\\n  do {                                                                        \\\n    *it2 = *it1;                                                              \\\n    len++;                                                                    \\\n    it1++;                                                                    \\\n    it2++;                                                                    \\\n    skp = X;                                                                  \\\n  } while (0)\n\nAK_EXPORT\nconst char *\nak_path_fragment(const char *path) {\n  return strrchr(path, '#');\n}\n\nAK_EXPORT\nint\nak_path_isfile(const char *path) {\n  const char *it;\n\n  it = path;\n  while (*it == ' ')\n    it++;\n\n  if (*it == '/' || *it == '\\\\')\n    return 1;\n\n  if (strstr(it, \"://\"))\n    if (strncasecmp(\"file\", it, strstr(it, \"://\") - it) != 0)\n      return 0;\n\n  return 1;\n}\n\nAK_EXPORT\nchar*\nak_path_dir(AkHeap     * __restrict heap,\n            void       * __restrict memparent,\n            const char * __restrict path) {\n  char *dir;\n  char *slash;\n\n  slash = strrchr(path, '/');\n  if (!slash)\n    return ak_heap_strdup(heap, memparent, \"./\");\n\n  dir = ak_heap_strndup(heap,\n                        memparent,\n                        path,\n                        slash - path);\n  return dir;\n}\n\nAK_EXPORT\nsize_t\nak_path_trim(const char *path,\n             char *trimmed) {\n  const char *it1;\n  char       *it2;\n  size_t      len;\n  int         skp;\n  int         proto;\n  int         local;\n\n  len = skp = proto = 0;\n  it1 = path;\n  it2 = trimmed;\n\n  while (*it1 == ' ')\n    it1++;\n\n  local = *it1 == '/' || *it1 == '\\\\';\n\n  while (*it1) {\n    if (*it1 == '/' || *it1 == '\\\\') {\n      if (skp != 0) {\n        if (proto != 1) {\n          it1++;\n          continue;\n        }\n\n        proto = 2;\n      }\n\n      AK_STRTRM_SET(1);\n    } else {\n      proto = !proto && *it1 == ':' && !local;\n      AK_STRTRM_SET(0);\n    }\n  }\n\n  while (*--it2 && (*it2 == ' ' || *it2 == '/' || *it2 == '\\\\'))\n    len--;\n\n  *(it2 + (len > 0)) = '\\0';\n\n  return len;\n}\n\nAK_EXPORT\nint\nak_path_join(char   *fragments[],\n             char   *buf,\n             size_t *size) {\n  const char *frag;\n  const char *frag_end;\n  size_t      len;\n  size_t      frag_len;\n  size_t      frag_idx;\n  size_t      frag_idx_v;\n  char        c;\n\n  if (!fragments\n      || !*fragments /* there are no fragments to join */\n      || !buf\n      || !size)\n    return -EINVAL;\n\n  frag_idx_v = frag_idx = len = 0;\n\n  /* build path */\n  while ((frag = fragments[frag_idx++])) {\n    frag_len = strlen(frag);\n    frag_end = frag + frag_len - 1;\n\n    if (frag_len == 0)\n      continue;\n\n    /* starts with slash e.g. /usr */\n    c = *frag;\n    if (frag_idx_v == 0\n        && (c == CHR_SLASH || c == CHR_BACK_SLASH)) {\n      APPEND_SLASH;\n      frag++;\n\n      /* avoid extra slash after non-protocol frag \n         like /http:/ -> /http:// \n       */\n      frag_idx_v++;\n    }\n\n    /* l-trim */\n    while ((c = *frag) != '\\0'\n           && (c == CHR_SLASH || c == CHR_BACK_SLASH))\n      frag++;\n\n    /* r-trim */\n    while (frag_end > frag\n           && (c = *frag_end) != '\\0'\n           && (c == CHR_SLASH || c == CHR_BACK_SLASH))\n      frag_end--;\n\n    /* only slashes */\n    if (!*frag) {\n      if (len == 0)\n        APPEND_SLASH;\n\n      continue;\n    }\n\n    if (len > 1 && *(buf - 1) != CHR_SLASH)\n      APPEND_SLASH;\n\n    /* protocol e.g. http://, tcp:// */\n    if (len > 1 && frag_idx_v == 1 && *(buf - 2) == CHR_COLON)\n      APPEND_SLASH;\n\n    frag_len = ++frag_end - frag;\n    len += frag_len;\n\n    memcpy(buf, frag, frag_len);\n\n    buf += frag_len;\n    frag_idx_v++;\n  }\n\n  memset(buf, '\\0', 1);\n  *size = len;\n\n  return 0;\n}\n\nAK_EXPORT\nconst char*\nak_fullpath(AkDoc       * __restrict doc,\n            const char  * __restrict ref,\n            char        * __restrict buf) {\n  size_t      pathlen;\n  const char *ptr;\n  char       *fileprefix  = \"file:///\";\n  char       *fragments[] = {\n    (char *)doc->inf->dir,\n    \"/\",\n    (char *)ref,\n    NULL\n  };\n\n  if (strncmp(ref, fileprefix, strlen(fileprefix)) == 0) {\n    return ref + strlen(fileprefix)\n#ifndef _WIN32\n    - 1\n#endif\n    ;\n  }\n\n  ak_path_join(fragments, buf, &pathlen);\n\n  ptr = ak_strltrim_fast(buf);\n\n  return ptr;\n}\n"
  },
  {
    "path": "src/resc/resource.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"resource.h\"\n#include \"../../include/ak/path.h\"\n\n#include <string.h>\n#include <stdlib.h>\n#include <limits.h>\n#include <math.h>\n#include <string.h>\n#include <stdio.h>\n\nstatic AkFetchFromURLHandler ak__urlhandler = NULL;\n\nstatic AkHeap ak__resc_heap = {\n  .flags = 0\n};\n\n#define resc_heap &ak__resc_heap\n\nAK_EXPORT\nvoid\nak_setFetchFromURLHandler(AkFetchFromURLHandler handler) {\n  ak__urlhandler = handler;\n}\n\nAkResource *\nak_resc_ins(const char *url) {\n  void       *pFoundResc;\n  AkResource *resc, *foundResc;\n  char       *tmp, *trimmedURL;\n  const char *fragment;\n  size_t      trimmedURLSize;\n  AkResult    ret;\n\n  tmp = malloc(sizeof(*tmp) * strlen(url));\n\n  ak_path_trim(url, tmp);\n\n  /* ak_path_fragment returns strrchr(path, '#') which is NULL when the\n     URL has no fragment — common for malformed DAE source attributes\n     (\"source=foo\" instead of \"source=#foo\", typical of Blender/Maya\n     exports). Treat the whole string as the resource identifier in that\n     case rather than dereferencing NULL. */\n  fragment = ak_path_fragment(tmp);\n  if (fragment && strlen(fragment) > 0)\n    trimmedURLSize = fragment - tmp;\n  else\n    trimmedURLSize = strlen(tmp);\n\n  trimmedURL = ak_heap_strndup(resc_heap,\n                               NULL,\n                               tmp,\n                               trimmedURLSize);\n  free(tmp);\n\n  ret = ak_heap_getMemById(resc_heap,\n                           trimmedURL,\n                           &pFoundResc);\n\n  if (ret != AK_EFOUND) {\n    foundResc = pFoundResc;\n    foundResc->refc++;\n\n    ak_free(trimmedURL);\n    return foundResc;\n  }\n\n  resc = ak_heap_calloc(resc_heap,\n                        NULL,\n                        sizeof(*resc));\n\n  resc->url = trimmedURL;\n  ak_heap_setpm(trimmedURL, resc);\n  ak_heap_setId(resc_heap,\n                ak__alignof(resc),\n                trimmedURL);\n\n  resc->refc++;\n\n  if (ak_path_isfile(url)) {\n    resc->isdwn    = true;\n    resc->islocal  = true;\n    resc->localurl = resc->url;\n    resc->result   = ak_load(&resc->doc,\n                             resc->url,\n                             AK_FILE_TYPE_COLLADA);\n    return resc;\n  }\n\n  /* download the file, file must be downloadable, e.g not live stream\n   * to do this the remote file must send file size\n   */\n\n  if (!ak__urlhandler) {\n    ak_free(resc);\n    return NULL;\n  }\n\n  resc->localurl = ak__urlhandler(resc->url);\n  ak_heap_setpm((void *)resc->localurl, resc);\n\n  resc->result = ak_load(&resc->doc,\n                         resc->url,\n                         AK_FILE_TYPE_COLLADA);\n  return resc;\n}\n\nvoid\nak_resc_ref(AkResource *resc) {\n  resc->refc++;\n}\n\nint\nak_resc_unref(AkResource *resc) {\n  resc->refc--;\n  if (resc->refc <= 0) {\n    if (resc->doc)\n      ak_free(resc->doc);\n\n    ak_free(resc);\n\n    return -1;\n  }\n\n  return 0;\n}\n\nint\nak_resc_unref_url(const char *url) {\n  void    *resc;\n  char    *trimmed;\n  AkResult ret;\n\n  trimmed = NULL;\n  ak_path_trim(url, trimmed);\n\n  if (trimmed) {\n    ret = ak_heap_getMemById(resc_heap,\n                             trimmed,\n                             &resc);\n\n    free(trimmed);\n\n    if (ret != AK_EFOUND)\n      return ak_resc_unref(resc);\n  }\n\n  return -1;\n}\n\nvoid\nak_resc_print(void) {\n  ak_heap_printKeys(resc_heap);\n}\n\nAK_HIDE\nvoid\nak_resc_init(void) {\n  ak_heap_init(resc_heap, NULL, NULL, NULL);\n}\n\nAK_HIDE\nvoid\nak_resc_deinit(void) {\n  ak_heap_destroy(resc_heap);\n}\n"
  },
  {
    "path": "src/resc/resource.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_resource_h\n#define ak_resource_h\n\n#include \"../common.h\"\n#include <time.h>\n\ntypedef struct AkResource {\n  AkDoc      *doc;\n  const char *url;\n  const char *localurl;\n  int64_t     refc;\n  time_t      dwntime;\n  bool        isdwn;\n  bool        islocal;\n  AkResult    result;\n} AkResource;\n\nAK_HIDE\nvoid\nak_resc_init(void);\n\nAK_HIDE\nvoid\nak_resc_deinit(void);\n\nAkResource *\nak_resc_ins(const char *url);\n\nvoid\nak_resc_ref(AkResource *resc);\n\nint\nak_resc_unref(AkResource *resc);\n\nint\nak_resc_unref_url(const char *url);\n\nvoid\nak_resc_print(void);\n\n#endif /* ak_resource_h */\n"
  },
  {
    "path": "src/resc/url.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include \"../../include/ak/path.h\"\n#include \"resource.h\"\n\n#include <string.h>\n#include <stdlib.h>\n#include <assert.h>\n#include <limits.h>\n\n#ifdef _MSC_VER\n#  ifndef PATH_MAX\n#    define PATH_MAX 260\n#  endif\n#endif\n\nvoid\nak_url_init(void  *parent,\n            char  *urlstring,\n            AkURL *dest) {\n\n  assert(parent && \"parent must be malloced by ak_heap_alloc/calloc\");\n\n  if (*urlstring == '#') {\n    AkHeap *heap;\n\n    dest->reserved = NULL;\n    heap           = ak_heap_getheap(parent);\n    dest->doc      = ak_heap_data(heap);\n    dest->url      = ak_heap_strdup(heap,\n                                    parent,\n                                    urlstring);\n    return;\n  }\n\n  /* TODO: */\n  dest->reserved = ak_resc_ins(urlstring);\n  dest->url      = ak_path_fragment(urlstring);\n  dest->doc      = ((AkResource *)dest->reserved)->doc;\n}\n\nvoid\nak_url_dup(AkURL *src,\n           void  *parent,\n           AkURL *dest) {\n\n  assert(parent && \"parent must be malloced by ak_heap_alloc/calloc\");\n\n  memcpy(dest, src, sizeof(AkURL));\n  if (!src->reserved) {\n    AkHeap *heap;\n    heap      = ak_heap_getheap(parent);\n    dest->url = ak_heap_strdup(heap, parent, dest->url);\n    return;\n  }\n\n  /* TODO: */\n}\n\nvoid\nak_url_init_with_id(AkHeapAllocator *alc,\n                    void            *parent,\n                    char            *idstirng,\n                    AkURL           *dest) {\n  char *urlstring;\n  urlstring = ak_url_string(alc, idstirng);\n  ak_url_init(parent, urlstring, dest);\n  alc->free(urlstring);\n}\n\nchar *\nak_url_string(AkHeapAllocator *alc, char *id) {\n  char *urlstring;\n\n  urlstring  = alc->malloc(strlen(id) + 2);\n  *urlstring = '#';\n  strcpy(urlstring + 1, id);\n\n  return urlstring;\n}\n\nvoid\nak_url_ref(AkURL *url) {\n  if (!url->reserved)\n    return;\n\n  ak_resc_ref(url->reserved);\n}\n\nvoid\nak_url_unref(AkURL *url) {\n  if (!url->reserved)\n    return;\n\n  if (ak_resc_unref(url->reserved) < 0) {\n    url->reserved = NULL;\n    url->url      = NULL;\n  }\n}\n\nAK_EXPORT\nvoid *\nak_getObjectByUrl(AkURL * __restrict url) {\n  if (url->ptr)\n    return url->ptr;\n\n  if (url->doc)\n    return ak_getObjectById(url->doc, url->url + 1);\n\n  return NULL;\n}\n\nconst char*\nak_getFile(const char *url) {\n  if (ak_path_isfile(url))\n    return url;\n\n  /* download the file, file must be downloadable, e.g not live stream\n   * to do this the remote file must send file size\n   */\n\n  /* return local URL\n   \n     TODO: option for cache time,\n           option for how to store this file,\n           option for when to remove it\n   */\n  return NULL;\n}\n\nchar*\nak_getFileFrom(AkDoc *doc, const char *url) {\n  char        pathbuf[PATH_MAX];\n  const char *path;\n\n  path = ak_fullpath(doc, url, pathbuf);\n  return ak_strdup(NULL, path);\n}\n\nAK_EXPORT\nvoid\nak_retainURL(void * __restrict obj, AkURL * __restrict url) {\n  AkHeap     *heap;\n  AkHeapNode *hnode;\n  AkUrlNode  *urlNode;\n  int        *refc;\n  void       *found, *last, **emptyslot;\n  size_t      len;\n  AkResult    ret;\n\n  heap  = ak_heap_getheap(obj);\n  hnode = ak__alignof(obj);\n\n  /* check if object is available */\n  ret = ak_heap_getNodeByURL(heap, url, &hnode);\n  if (ret != AK_OK || !hnode)\n    return;\n\n  urlNode   = ak_heap_ext_add(heap, hnode, AK_HEAP_NODE_FLAGS_URL);\n  refc      = ak_heap_ext_add(heap, hnode, AK_HEAP_NODE_FLAGS_REFC);\n  len       = urlNode->len;\n  found     = urlNode->urls[0];\n  last      = urlNode->urls[len];\n  emptyslot = NULL;\n\n  while (found != last) {\n    /* already retained */\n    if (found == url)\n      return;\n\n    if (found == NULL && !emptyslot)\n      emptyslot = found;\n  }\n\n  /* retain */\n  (*refc)++;\n\n  if (emptyslot) {\n    *emptyslot = url;\n    return;\n  }\n\n  /* append url to retained url lists */\n  urlNode->len  = len + 1;\n  urlNode->urls = heap->allocator->realloc(urlNode->urls,\n                                           sizeof(void *) * urlNode->len);\n\n  urlNode->urls[len] = url;\n}\n\nAK_EXPORT\nvoid\nak_releaseURL(void * __restrict obj, AkURL * __restrict url) {\n  AkHeapNode *hnode;\n  AkUrlNode  *urlNode;\n  void       *urlobj;\n  void       **found, **it, *last;\n  size_t      len;\n\n  hnode = ak__alignof(obj);\n\n  /* check if object is available */\n  if (!(urlobj = ak_getObjectByUrl(url)))\n    return;\n\n  urlNode = ak_heap_ext_get(hnode, AK_HEAP_NODE_FLAGS_URL);\n  len     = urlNode->len;\n  it      = urlNode->urls[0];\n  last    = urlNode->urls[len];\n  found   = NULL;\n\n  while (it != last) {\n    /* already retained */\n    if (*it == url) {\n      found = it;\n      break;\n    }\n\n    it++;\n  }\n\n  if (!found)\n    return;\n\n  /* empty slot */\n  *found = NULL;\n\n  ak_release(urlobj);\n}\n"
  },
  {
    "path": "src/sid.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n#include \"../include/ak/profile.h\"\n#include \"sid.h\"\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\nAkHeap ak__sidconst_heap = {\n  .flags = 0\n};\n\n#define sidconst_heap &ak__sidconst_heap\n\nAK_EXPORT\nconst char *\nak_sid_get(void *memnode) {\n  AkHeapNode *heapNode;\n  AkSIDNode  *sidnode;\n\n  heapNode = ak__alignof(memnode);\n  sidnode  = ak_heap_ext_get(heapNode, AK_HEAP_NODE_FLAGS_SID);\n\n  if (!sidnode)\n    return NULL;\n\n  return sidnode->sid;\n}\n\nAK_EXPORT\nconst char *\nak_sid_geta(void *memnode,\n            void *memptr) {\n  AkHeapNode *heapNode;\n  AkSIDNode  *sidnode;\n  char        *ptr;\n  uint16_t    off;\n  size_t      sidCount;\n  size_t      i;\n\n  heapNode = ak__alignof(memnode);\n  sidnode  = ak_heap_ext_get(heapNode, AK_HEAP_NODE_FLAGS_SID);\n\n  if (!sidnode)\n    return NULL;\n\n  sidCount = *(size_t *)sidnode->sid;\n  off      = (uint16_t)((char *)memptr - (char *)memnode);\n  ptr      = (char *)sidnode->sid + sizeof(size_t);\n\n  for (i = 0; i < sidCount; i++) {\n    if (*(uint16_t *)ptr == off)\n      return ptr + sizeof(uint16_t);\n\n    ptr += sizeof(uint16_t) + sizeof(const char **);\n  }\n\n  return NULL;\n}\n\nAK_EXPORT\nvoid\nak_sid_set(void       *memnode,\n           const char * __restrict sid) {\n  AkSIDNode  *sidnode;\n  AkHeapNode *heapNode;\n  AkHeap     *heap;\n\n  heapNode = ak__alignof(memnode);\n  heap     = ak_heap_getheap(memnode);\n\n  sidnode = ak_heap_ext_add(heap, heapNode, AK_HEAP_NODE_FLAGS_SID);\n\n  sidnode->sid = sid;\n\n  /* mark parents */\n  while (heapNode->prev) {\n    /* we foound parent */\n    if (ak_heap_chld(heapNode->prev) == heapNode)\n      heapNode->prev->flags |= AK_HEAP_NODE_FLAGS_SID_CHLD;\n\n    heapNode = heapNode->prev;\n  }\n\n  /* TODO: invalidate refs */\n}\n\nAK_EXPORT\nvoid\nak_sid_seta(void       *memnode,\n            void       *memptr,\n            const char * __restrict sid) {\n  AkSIDNode  *sidnode;\n  AkHeapNode *heapNode;\n  AkHeap     *heap;\n  char       *sidptr;\n  uint16_t    off;\n  uintptr_t   sidptrval;\n  int         off0;\n  int         itmsize;\n\n  heapNode = ak__alignof(memnode);\n  heap     = ak_heap_getheap(memnode);\n\n  sidnode = ak_heap_ext_add(heap, heapNode, AK_HEAP_NODE_FLAGS_SID);\n  off0    = sizeof(size_t);\n  off     = (uint16_t)((char *)memptr - (char *)memnode);\n  itmsize = sizeof(uint16_t) + sizeof(uintptr_t);\n\n  if (!sidnode->sids) {\n    sidnode->sids = heap->allocator->calloc(1, off0 + itmsize);\n  } else {\n    size_t newsize;\n\n    newsize  = *(size_t *)((char *)sidnode->sids) + 1;\n    newsize  = newsize * itmsize;\n\n    sidnode->sids = heap->allocator->realloc(sidnode->sids, newsize + off0);\n  }\n\n  sidptr = sidnode->sids;\n  sidptr += off0 + itmsize * (*(size_t *)sidptr)++;\n\n  *(uint16_t *)sidptr = off;\n\n  sidptr   += sizeof(uint16_t);\n  sidptrval = (uintptr_t)sid;\n  memcpy(sidptr, &sidptrval, sizeof(uintptr_t));\n\n  /* mark parents */\n  while (heapNode->prev) {\n    /* we foound parent */\n    if (ak_heap_chld(heapNode->prev) == heapNode)\n      heapNode->prev->flags |= AK_HEAP_NODE_FLAGS_SID_CHLD;\n\n    heapNode = heapNode->prev;\n  }\n\n  /* TODO: invalidate refs */\n}\n\nAK_EXPORT\nvoid\nak_sid_dup(void *newMemnode,\n           void *oldMemnode) {\n  ak_sid_set(newMemnode, ak_heap_strdup(ak_heap_getheap(newMemnode),\n                                        newMemnode,\n                                        ak_sid_get(oldMemnode)));\n}\n\nvoid\nak_sid_destroy(AkHeap * __restrict heap,\n               AkSIDNode * __restrict snode) {\n  AkHeapAllocator *alc;\n\n  alc = heap->allocator;\n  if (snode->refs)\n    alc->free(snode->refs);\n\n  if (snode->sids)\n    alc->free(snode->sids);\n}\n\nAK_HIDE\nptrdiff_t\nak_sidElement(AkContext  * __restrict ctx,\n              const char * __restrict target,\n              void      ** __restrict idnode,\n              bool       * __restrict isdot) {\n  AkHeap     *heap;\n  AkDoc      *doc;\n  AkHeapNode *hnode, *parent;\n  const char *it;\n  char       *sidp;\n  AkResult    ret;\n\n  heap  = NULL;\n  it    = ak_strltrim_fast(target);\n  sidp  = strchr(target, '/');\n  *isdot = *it == '.' || !sidp;\n\n  if (*idnode && *isdot) {\n    hnode = ak__alignof(*idnode);\n    goto again;\n  }\n\n  doc  = ctx->doc;\n  heap = ak_heap_getheap(doc);\n\n\n  /* if there is no \"/\" then assume that this is \"./\" */\n  if (*isdot) {\n    AkSidConstr *constr;\n\n    /* .[attr] */\n    if (*it == '.' && !sidp) {\n      it++;\n      goto ret;\n    }\n\n    /* else ./sid.../sid.[attr] */\n\n    /* find id node */\n    hnode  = ak__alignof(target);\n\n  again:\n    constr = ak_sidConstraintsOf(ak_typeid((void *)target));\n    parent = ak_heap_parent(hnode);\n\n    /* find by constraint / semantic */\n    if (parent && constr) {\n      while (parent) {\n        AkSidConstrItem *constrItem;\n        AkTypeId pTypeId;\n        bool     found;\n\n        found      = false;\n        pTypeId    = ak_typeid(ak__alignas(parent));\n        constrItem = constr->item;\n        while (constrItem) {\n          if (constrItem->constr == pTypeId) {\n            found = true;\n            break;\n          }\n\n          constrItem = constrItem->next;\n        }\n\n        if (found)\n          break;\n\n        parent = ak_heap_parent(parent);\n      }\n    }\n\n    /* check id nodes */\n    else {\n      while (parent) {\n        void *id;\n\n        id = ak_heap_getId(heap, parent);\n        if (id)\n          break;\n\n        parent = ak_heap_parent(parent);\n      }\n    }\n\n    *idnode = ak__alignas(parent);\n\n    /* no need to offset again */\n    if (*idnode)\n      return 0;\n  } else if (sidp) {\n    char     *id;\n    ptrdiff_t idlen;\n\n    /* TODO: check # character and external ID element ? */\n    idlen = sidp - it;\n    id    = malloc(sizeof(char) * idlen + 1);\n\n    memcpy(id, it, idlen);\n    id[idlen] = '\\0';\n\n    ret = ak_heap_getNodeById(heap, (void *)id, &hnode);\n    free(id);\n    if (ret != AK_OK || !idnode)\n      goto err;\n\n    *idnode = ak__alignas(hnode);\n    it      = sidp;\n  } else {\n    goto err;\n  }\n\nret:\n  return it - target + 1;\nerr:\n  return -1;\n}\n\nAK_HIDE\nAkHeapNode*\nak_sid_profile(AkContext  * __restrict ctx,\n               AkHeapNode * __restrict parent,\n               AkHeapNode * __restrict after) {\n  AkProfile       *profile;\n  AkTechniqueHint *hint;\n  const char      *platform;\n\n  profile = ak_profile(ak__alignas(parent),\n                       after ? ak__alignas(after) : NULL);\n\n  /* check hint for profile */\n  if ((!ctx->techniqueHint\n       || !ctx->techniqueHint->profile)\n      && profile)\n    return ak__alignof(profile);\n\n  hint     = ctx->techniqueHint;\n  platform = ak_platform();\n  while (profile) {\n    if (profile->type == hint->profileType)\n      goto ret;\n\n    if (!(profile = ak_profile(ak__alignas(parent), profile)))\n      goto err;\n\n    /* check platform */\n    if (hint->platform) {\n      if (strcmp(platform, hint->platform) == 0)\n        profile = ak_profile(ak__alignas(parent), profile);\n    }\n  }\n\nret:\n  if (profile)\n    return ak__alignof(profile);\n\nerr:\n  return NULL;\n}\n\nAK_HIDE\nAkHeapNode*\nak_sid_technique(AkContext  * __restrict ctx,\n                 AkHeapNode * __restrict chld) {\n  AkHeapNode *orig;\n  orig = chld;\n\n  /* first check hint for technique */\n  if (ctx->techniqueHint && ctx->techniqueHint->ref) {\n    AkTechniqueHint *hint;\n    const char      *platform;\n\n    hint     = ctx->techniqueHint;\n    platform = ak_platform();\n\n    /* get desired technique */\n    while (chld) {\n      AkSIDNode *snode;\n      snode = ak_heap_ext_get(chld, AK_HEAP_NODE_FLAGS_SID);\n      if (snode->sid && strcmp(snode->sid, hint->ref) == 0)\n        goto ret;\n\n      if (!(chld = chld->next))\n        goto err;\n\n      /* check platform */\n      if (hint->platform) {\n        if (strcmp(platform, hint->platform) == 0)\n          chld = chld->next;\n      }\n    }\n  }\n\n  /* check global options to get active technique name */\n  else {\n    /* default: try to get sid=\"common\" or technique_common */\n    char **tnames, *tname;\n\n    /* get global techniques with order */\n    if (ak_typeidh(chld) == AKT_TECHNIQUE_FX)\n      tnames = (char **)ak_opt_get(AK_OPT_TECHNIQUE_FX);\n    else\n      tnames = (char **)ak_opt_get(AK_OPT_TECHNIQUE);\n\n    if (!*tnames)\n      goto err;\n\n    /* get desired technique */\n    while (chld) {\n      tname = *tnames;\n\n      do {\n        AkSIDNode *snode;\n        uint32_t   i;\n\n        i     = 0;\n        snode = ak_heap_ext_get(chld, AK_HEAP_NODE_FLAGS_SID);\n        if (snode\n            && snode->sid\n            && strcmp(snode->sid, tname) == 0)\n          goto ret;\n\n        tname = tnames[++i];\n      } while (tname);\n\n      chld = chld->next;\n    }\n  }\n\n  /* default: no technique found use first one */\n  chld = orig;\n\nret:\n  return chld;\n\nerr:\n  return NULL;\n}\n\nAK_HIDE\nAkHeapNode*\nak_sid_chldh(AkContext  * __restrict ctx,\n             AkHeapNode * __restrict parent,\n             AkHeapNode * __restrict after) {\n  AkHeapNode *chld;\n\n  /* select active profile if parent is <effect> */\n  if (ak_typeidh(parent) == AKT_EFFECT)\n    return ak_sid_profile(ctx, parent, after);\n\n  /* get child element */\n  if (after)\n    chld = after->next;\n  else\n    chld = ak_heap_chld(parent);\n\n  if (!chld)\n    return NULL;\n\n  /* check active technique if child element is <technique> */\n  if (ak_typeidh(chld) == AKT_TECHNIQUE_FX\n      || ak_typeidh(chld) == AKT_TECHNIQUE)\n    chld = ak_sid_technique(ctx, chld);\n\n  return chld;\n}\n\nAK_EXPORT\nvoid *\nak_sid_resolve(AkContext   * __restrict ctx,\n               const char  * __restrict target,\n               const char ** __restrict attribString) {\n  AkHeapNode  *idnode, *sidnode, *it, *chld;\n  char        *siddup, *sid_it, *saveptr, *attrLoc;\n  void        *found;\n  AkHeapNode **buf[2];\n  void        *elm;\n  size_t       bufl[2], bufc[2], bufi[2];\n  int          bufidx;\n  ptrdiff_t    sidoff;\n  bool         isdot;\n\n  elm    = NULL;\n  sidoff = ak_sidElement(ctx, target, &elm, &isdot);\n  if (sidoff == -1 || !elm)\n    return NULL;\n\n  bufi[0] = bufi[1] = bufc[0] = bufc[1] = bufidx = 0;\n  bufl[0] = bufl[1] = 4; /* start point */\n\n  buf[0]  = malloc(sizeof(*buf[0]) * bufl[0]);\n  buf[1]  = malloc(sizeof(*buf[0]) * bufl[0]);\n\n  if (attribString)\n    *attribString = NULL;\n\nagain:\n  idnode  = ak__alignof(elm);\n  sidnode = NULL;\n  found   = NULL;\n  siddup  = strdup(target + sidoff);\n  attrLoc = strchr(siddup, '.');\n  sid_it  = strtok_r(siddup, \"/ \\t\", &saveptr);\n\n  if (attrLoc)\n    attrLoc[0] = '\\0';\n\n  if (!idnode)\n    goto err;\n\n  /* maybe id node have sid too! */\n  if (idnode->flags & AK_HEAP_NODE_FLAGS_SID) {\n    chld = idnode;\n    \n    buf[bufidx][bufi[bufidx]] = chld;\n    bufc[bufidx]  = 1;\n  } else {\n    chld = ak_sid_chldh(ctx, idnode, NULL);\n    if (!chld)\n      goto err;\n    \n    while (chld) {\n      if (bufi[bufidx] == bufl[bufidx]) {\n        bufl[bufidx] += 16;\n        buf[bufidx]   = realloc(buf[bufidx],\n                                 sizeof(*buf[bufidx]) * bufl[bufidx]);\n      }\n\n      buf[bufidx][bufi[bufidx]] = chld;\n      bufi[bufidx]++;\n      bufc[bufidx]++;\n\n      chld = ak_sid_chldh(ctx, idnode, chld);\n    }\n  }\n\n  bufi[bufidx] = bufi[!bufidx] = 0;\n\n  /* breadth-first search */\n  while (bufc[bufidx] > 0) {\n    it = buf[bufidx][bufi[bufidx]];\n\n    if (it->flags & AK_HEAP_NODE_FLAGS_SID) {\n      AkSIDNode *snode;\n      \n      snode = ak_heap_ext_get(it, AK_HEAP_NODE_FLAGS_SID);\n      if (snode->sid && strcmp(snode->sid, sid_it) == 0) {\n        char *tok;\n        \n        sidnode = it;\n        \n        /* go for next */\n        tok = strtok_r(NULL, \"/ \\t\", &saveptr);\n        if (!tok)\n          goto ret;\n        \n        sid_it = tok;\n      }\n      \n      /* check attrs */\n      if (snode->sids) {\n        char  *p, *end;\n        size_t c;\n        \n        c   = *(size_t *)snode->sids;\n        p   = (char *)snode->sids + sizeof(size_t);\n        end = p + c * (sizeof(char **) + sizeof(uint16_t));\n        \n        while (p != end) {\n          p  += sizeof(uint16_t);\n          \n          /* found sid in attr */\n          if (strcmp(*(char **)p, sid_it) == 0) {\n            char *tok;\n            \n            sidnode = it;\n            \n            /* there is no way to go down */\n            tok = strtok_r(NULL, \"/ \\t\", &saveptr);\n            if (tok)\n              goto err;\n            \n            goto ret;\n          }\n          \n          p += sizeof(char **) + sizeof(uint16_t);\n        }\n      }\n    }\n    \n    if (it->flags & AK_HEAP_NODE_FLAGS_SID_CHLD) {\n      /* keep all children */\n      AkHeapNode *it2;\n\n      it2 = ak_sid_chldh(ctx, it, NULL);\n      while (it2) {\n        if (bufi[!bufidx] == bufl[!bufidx]) {\n          bufl[!bufidx] += 16;\n          buf[!bufidx]   = realloc(buf[!bufidx],\n                                   sizeof(*buf[!bufidx]) * bufl[!bufidx]);\n        }\n\n        if (it2->flags & (AK_HEAP_NODE_FLAGS_SID_CHLD | AK_HEAP_NODE_FLAGS_SID)) {\n          buf[!bufidx][bufi[!bufidx]] = it2;\n          bufi[!bufidx]++;\n          bufc[!bufidx]++;\n        }\n\n        it2 = ak_sid_chldh(ctx, it, it2);\n      }\n    }\n\n    if (++bufi[bufidx] == bufc[bufidx]) {\n      bufc[bufidx] = bufi[bufidx] = bufi[!bufidx] = 0;\n      bufidx       = !bufidx;\n    }\n  }\n\n  if (isdot) {\n    /* pick next parent*/\n    (void)ak_sidElement(ctx, target, &elm, &isdot);\n    if (!elm)\n      goto err;\n\n    free(siddup);\n    goto again;\n  }\n\nret:\n  if (sidnode) {\n    found = ak__alignas(sidnode);\n    if (attribString && attrLoc)\n      *attribString = ak_strdup(NULL, attrLoc + 1);\n  }\n\nerr:\n  free(buf[0]);\n  free(buf[1]);\n  free(siddup);\n\n  return found;\n}\n\nAK_EXPORT\nvoid *\nak_sid_resolve_from(AkContext   * __restrict ctx,\n                    const char  * __restrict id,\n                    const char  * __restrict sid,\n                    const char ** __restrict attribString) {\n  char target[1024];\n  char *pTarget;\n\n  target[0] = '\\0';\n  strcat(target, id);\n  strcat(target, \"/\");\n  strcat(target, sid);\n\n  pTarget = target[0] == '#' ? &target[1] : target;\n\n  return ak_sid_resolve(ctx, pTarget, attribString);\n}\n\nAK_EXPORT\nuint32_t\nak_sid_attr_offset(const char *attr) {\n  const char *attrs[] = {\"X\", \"Y\", \"Z\", \"R\", \"G\", \"B\", \"A\", \"ANGLE\", \"S\", \"T\", \"U\", \"V\", \"W\", \"P\", \"Q\"};\n  uint32_t    offs[]  = { 0,   1,   2,   0,   1,   2,   3,     3,     0,   1,   0,   1,   3,   2,   3 };\n  uint32_t    i, len;\n\n  if (attr) {\n    len = sizeof(offs) / sizeof(offs[0]);\n\n    for (i = 0; i < len; i++) {\n      /* TODO: use strcmp after cache uppercased attr */\n      if (strcasecmp(attr, attrs[i]) == 0) {\n        return offs[i];\n      }\n    }\n\n    return UINT32_MAX;\n  }\n\n  return 0;\n}\n\nAK_EXPORT\nvoid*\nak_sid_resolve_val(AkContext  * __restrict ctx,\n                   const char * __restrict target) {\n  const char *attr;\n  void       *r;\n\n  attr = NULL;\n  r    = ak_sid_resolve(ctx, target, &attr);\n\n  /* currently only transform elements */\n  if (attr && ak_typeid(r) == AKT_OBJECT) {\n    AkObject *obj;\n    int       off;\n\n    obj = r;\n    off = ak_sid_attr_offset(attr);\n\n    switch (obj->type) {\n      case AKT_ROTATE: {\n        AkRotate *t;\n        t = ak_objGet(obj);\n        return &t->val[off];\n      }\n      case AKT_TRANSLATE: {\n        AkTranslate *t;\n        t = ak_objGet(obj);\n        return &t->val[off];\n        break;\n      }\n      case AKT_SCALE: {\n        AkScale *t;\n        t = ak_objGet(obj);\n        return &t->val[off];\n        break;\n      }\n      default:\n        break;\n    }\n    return (char *)obj->pData + off;\n  }\n\n  return r;\n}\n\nAK_HIDE\nvoid\nak_sid_init(void) {\n  ak_heap_init(sidconst_heap,\n               NULL,\n               ak_cmp_i32,\n               NULL);\n  ak_sidInitConstr();\n}\n\nAK_HIDE\nvoid\nak_sid_deinit(void) {\n  ak_heap_destroy(sidconst_heap);\n}\n"
  },
  {
    "path": "src/sid.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_src_sid_h\n#define ak_src_sid_h\n\n#include \"common.h\"\n\ntypedef struct AkSidConstrItem {\n  AkTypeId                constr;\n  uint32_t                chldCound;\n  AkTypeId               *constrChld;\n  struct AkSidConstrItem *next;\n} AkSidConstrItem;\n\ntypedef struct AkSidConstr {\n  AkTypeId         typeId;\n  AkEnum           method; /* 0: block-scope */\n  AkSidConstrItem *item;\n} AkSidConstr;\n\nAK_HIDE void\nak_sid_init(void);\n\nAK_HIDE void\nak_sid_deinit(void);\n\nvoid\nak_sidInitConstr(void);\n\nAkSidConstr*\nak_sidConstraintsOf(AkTypeId typeId);\n\nvoid\nak_sidConstraintTo(AkTypeId         typeId,\n                   AkSidConstrItem *constrs,\n                   AkEnum           method);\n\nAK_HIDE\nAkHeapNode*\nak_sid_profile(AkContext  * __restrict ctx,\n               AkHeapNode * __restrict parent,\n               AkHeapNode * __restrict after);\n\nAK_HIDE\nAkHeapNode*\nak_sid_technique(AkContext  * __restrict ctx,\n                 AkHeapNode * __restrict chld);\n\nAK_HIDE\nAkHeapNode*\nak_sid_chldh(AkContext  * __restrict ctx,\n             AkHeapNode * __restrict parent,\n             AkHeapNode * __restrict after);\n\nAK_HIDE\nptrdiff_t\nak_sidElement(AkContext  * __restrict ctx,\n              const char * __restrict target,\n              void      ** __restrict idnode,\n              bool       * __restrict isdot);\n\n#endif /* ak_src_sid_h */\n"
  },
  {
    "path": "src/sid_constr.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n#include \"sid.h\"\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\nextern AkHeap ak__sidconst_heap;\n\n#define sidconst_heap &ak__sidconst_heap\n\nAkTypeId ak__sidConstrChldParam[] = {\n  AKT_NEWPARAM,\n  AKT_PARAM,\n  AKT_SETPARAM\n};\n\nAkSidConstrItem ak__sidConstrEffect = {\n    .constr     = AKT_TECHNIQUE_FX,\n    .chldCound  = 3,\n    .constrChld = ak__sidConstrChldParam,\n  .next = &(AkSidConstrItem){\n    .constr     = AKT_PROFILE,\n    .chldCound  = 3,\n    .constrChld = ak__sidConstrChldParam,\n  .next = &(AkSidConstrItem){\n    .constr     = AKT_EFFECT,\n    .chldCound  = 3,\n    .constrChld = ak__sidConstrChldParam,\n    }\n  }\n};\n\nAkSidConstr*\nak_sidConstraintsOf(AkTypeId typeId) {\n  void     *found;\n  AkResult  ret;\n\n  ret = ak_heap_getMemById(sidconst_heap,\n                           &typeId,\n                           &found);\n  if (ret == AK_OK)\n    return found;\n\n  return NULL;\n}\n\nvoid\nak_sidConstraintTo(AkTypeId         typeId,\n                   AkSidConstrItem *constrs,\n                   AkEnum           method) {\n  AkHeap      *heap;\n  AkSidConstr *constr;\n  void        *found;\n  AkResult     ret;\n\n  heap = sidconst_heap;\n  ret  = ak_heap_getMemById(heap,\n                            &typeId,\n                            &found);\n  if (ret == AK_OK)\n    ak_free(found);\n\n  if (!constrs)\n    return;\n\n  constr = ak_heap_alloc(heap, NULL, sizeof(*constr));\n  constr->method = method;\n  constr->item   = constrs;\n  constr->typeId = typeId;\n\n  ak_heap_setId(heap,\n                ak__alignof(constr),\n                &constr[0]);\n}\n\nvoid\nak_sidInitConstr(void) {\n  ak_sidConstraintTo(AKT_TEXTURE_NAME, &ak__sidConstrEffect, 0);\n  ak_sidConstraintTo(AKT_TEXCOORD,     &ak__sidConstrEffect, 0);\n  ak_sidConstraintTo(AKT_TEXTURE,      &ak__sidConstrEffect, 0);\n}\n"
  },
  {
    "path": "src/simd/arm.h",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0.\n */\n\n#ifndef assetkit_simd_arm_h\n#define assetkit_simd_arm_h\n\n#if defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM)\n#  if defined(_MSC_VER) && (defined(_M_ARM64) || defined(_M_ARM64EC))\n#    include <arm64_neon.h>\n#  else\n#    include <arm_neon.h>\n#  endif\n#  define AK_SIMD_ARM 1\n#  if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)\n#    define AK_SIMD_ARM64 1\n#  endif\n#endif\n\n#endif /* assetkit_simd_arm_h */\n"
  },
  {
    "path": "src/simd/base64.h",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0.\n *\n * Portions of the ARM64 base64 decode mapping follow the technique used by\n * libbase64:\n * Copyright (c) 2005-2007 Nick Galbreath\n * Copyright (c) 2015-2018 Wojciech Mula\n * Copyright (c) 2016-2017 Matthieu Darbois\n * Copyright (c) 2013-2022 Alfred Klomp\n * BSD 2-Clause license.\n */\n\n#ifndef assetkit_simd_base64_h\n#define assetkit_simd_base64_h\n\n#include \"intrin.h\"\n\n#include <stddef.h>\n#include <stdint.h>\n\n#if defined(AK_SIMD_ARM64)\nAK_INLINE\nint\nak_simd_base64_decode_arm64(const uint8_t * __restrict src,\n                            size_t                     len,\n                            uint8_t       * __restrict dst,\n                            size_t        * __restrict consumed,\n                            size_t        * __restrict written) {\n  static const uint8_t dec_lut1[] = {\n    255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U,\n    255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U,\n    255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U,\n    255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U,\n    255U, 255U, 255U, 255U, 255U, 255U, 255U, 255U,\n    255U, 255U, 255U,  62U, 255U, 255U, 255U,  63U,\n     52U,  53U,  54U,  55U,  56U,  57U,  58U,  59U,\n     60U,  61U, 255U, 255U, 255U, 255U, 255U, 255U,\n  };\n  static const uint8_t dec_lut2[] = {\n      0U, 255U,   0U,   1U,   2U,   3U,   4U,   5U,\n      6U,   7U,   8U,   9U,  10U,  11U,  12U,  13U,\n     14U,  15U,  16U,  17U,  18U,  19U,  20U,  21U,\n     22U,  23U,  24U,  25U, 255U, 255U, 255U, 255U,\n    255U, 255U,  26U,  27U,  28U,  29U,  30U,  31U,\n     32U,  33U,  34U,  35U,  36U,  37U,  38U,  39U,\n     40U,  41U,  42U,  43U,  44U,  45U,  46U,  47U,\n     48U,  49U,  50U,  51U, 255U, 255U, 255U, 255U,\n  };\n  const uint8x16x4_t tbl_dec1 = vld1q_u8_x4(dec_lut1);\n  const uint8x16x4_t tbl_dec2 = vld1q_u8_x4(dec_lut2);\n  const uint8x16_t offset = vdupq_n_u8(63U);\n  const uint8x16_t max_valid = vdupq_n_u8(63U);\n  size_t i, rounds;\n\n  if (len < 68) {\n    *consumed = 0;\n    *written = 0;\n    return 1;\n  }\n\n  rounds = (len - 4) / 64;\n  for (i = 0; i < rounds; i++) {\n    uint8x16x4_t str, dec1, dec2;\n    uint8x16x3_t dec;\n    uint8x16_t classified;\n\n    str = vld4q_u8(src);\n\n    dec2.val[0] = vqsubq_u8(str.val[0], offset);\n    dec2.val[1] = vqsubq_u8(str.val[1], offset);\n    dec2.val[2] = vqsubq_u8(str.val[2], offset);\n    dec2.val[3] = vqsubq_u8(str.val[3], offset);\n\n    dec1.val[0] = vqtbl4q_u8(tbl_dec1, str.val[0]);\n    dec1.val[1] = vqtbl4q_u8(tbl_dec1, str.val[1]);\n    dec1.val[2] = vqtbl4q_u8(tbl_dec1, str.val[2]);\n    dec1.val[3] = vqtbl4q_u8(tbl_dec1, str.val[3]);\n\n    dec2.val[0] = vqtbx4q_u8(dec2.val[0], tbl_dec2, dec2.val[0]);\n    dec2.val[1] = vqtbx4q_u8(dec2.val[1], tbl_dec2, dec2.val[1]);\n    dec2.val[2] = vqtbx4q_u8(dec2.val[2], tbl_dec2, dec2.val[2]);\n    dec2.val[3] = vqtbx4q_u8(dec2.val[3], tbl_dec2, dec2.val[3]);\n\n    str.val[0] = vorrq_u8(dec1.val[0], dec2.val[0]);\n    str.val[1] = vorrq_u8(dec1.val[1], dec2.val[1]);\n    str.val[2] = vorrq_u8(dec1.val[2], dec2.val[2]);\n    str.val[3] = vorrq_u8(dec1.val[3], dec2.val[3]);\n\n    classified = vorrq_u8(vorrq_u8(vcgtq_u8(str.val[0], max_valid),\n                                   vcgtq_u8(str.val[1], max_valid)),\n                          vorrq_u8(vcgtq_u8(str.val[2], max_valid),\n                                   vcgtq_u8(str.val[3], max_valid)));\n    if (vmaxvq_u8(classified) != 0U)\n      return 0;\n\n    dec.val[0] = vorrq_u8(vshlq_n_u8(str.val[0], 2),\n                          vshrq_n_u8(str.val[1], 4));\n    dec.val[1] = vorrq_u8(vshlq_n_u8(str.val[1], 4),\n                          vshrq_n_u8(str.val[2], 2));\n    dec.val[2] = vorrq_u8(vshlq_n_u8(str.val[2], 6), str.val[3]);\n\n    vst3q_u8(dst, dec);\n    src += 64;\n    dst += 48;\n  }\n\n  *consumed = rounds * 64;\n  *written = rounds * 48;\n  return 1;\n}\n#endif\n\nAK_INLINE\nint\nak_simd_base64_decode(const uint8_t * __restrict src,\n                      size_t                     len,\n                      uint8_t       * __restrict dst,\n                      size_t        * __restrict consumed,\n                      size_t        * __restrict written) {\n  *consumed = 0;\n  *written = 0;\n\n#if defined(AK_SIMD_ARM64)\n  return ak_simd_base64_decode_arm64(src, len, dst, consumed, written);\n#else\n  (void)src;\n  (void)len;\n  (void)dst;\n  return 1;\n#endif\n}\n\n#endif /* assetkit_simd_base64_h */\n"
  },
  {
    "path": "src/simd/intrin.h",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0.\n */\n\n#ifndef assetkit_simd_intrin_h\n#define assetkit_simd_intrin_h\n\n#if defined(__AVX2__) || defined(__AVX__) || defined(__SSSE3__) || defined(__SSE2__) || defined(_M_X64) || defined(_M_IX86)\n#  include \"x86.h\"\n#endif\n\n#if defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM)\n#  include \"arm.h\"\n#endif\n\n#if defined(__wasm__) && defined(__wasm_simd128__)\n#  include \"wasm.h\"\n#endif\n\n#if defined(AK_SIMD_X86) || defined(AK_SIMD_ARM) || defined(AK_SIMD_WASM)\n#  define AK_SIMD 1\n#endif\n\n#endif /* assetkit_simd_intrin_h */\n"
  },
  {
    "path": "src/simd/wasm.h",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0.\n */\n\n#ifndef assetkit_simd_wasm_h\n#define assetkit_simd_wasm_h\n\n#if defined(__wasm__) && defined(__wasm_simd128__)\n#  include <wasm_simd128.h>\n#  define AK_SIMD_WASM 1\n#endif\n\n#endif /* assetkit_simd_wasm_h */\n"
  },
  {
    "path": "src/simd/x86.h",
    "content": "/*\n * Copyright (C) 2026 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0.\n */\n\n#ifndef assetkit_simd_x86_h\n#define assetkit_simd_x86_h\n\n#if defined(__AVX2__)\n#  include <immintrin.h>\n#  define AK_SIMD_X86 1\n#  define AK_SIMD_AVX2 1\n#elif defined(__SSSE3__)\n#  include <tmmintrin.h>\n#  define AK_SIMD_X86 1\n#  define AK_SIMD_SSSE3 1\n#elif defined(__SSE2__) || defined(_M_X64) || defined(_M_IX86)\n#  include <emmintrin.h>\n#  define AK_SIMD_X86 1\n#  define AK_SIMD_SSE2 1\n#endif\n\n#endif /* assetkit_simd_x86_h */\n"
  },
  {
    "path": "src/skin/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/skin/fix.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"fix.h\"\n\nstatic\nAkBoneWeights*\nak_skinFixWeightsForPrimitive(AkSkin          * __restrict skin,\n                              AkMeshPrimitive * __restrict prim,\n                              uint32_t                     primIdx) {\n  AkMeshPrimitive *it;\n  uint32_t         idx;\n\n  if (!skin || !skin->weights || skin->nPrims == 0)\n    return NULL;\n\n  if (prim && prim->mesh) {\n    idx = 0;\n    for (it = prim->mesh->primitive; it; it = it->next, idx++) {\n      if (it != prim)\n        continue;\n\n      if (idx < skin->nPrims && skin->weights[idx])\n        return skin->weights[idx];\n\n      break;\n    }\n  }\n\n  if (primIdx < skin->nPrims)\n    return skin->weights[primIdx];\n\n  return NULL;\n}\n\nAK_HIDE\nvoid\nak_skinFixWeights(AkMesh * __restrict mesh) {\n  AkHeap          *heap;\n  AkDoc           *doc;\n  AkMeshPrimitive *prim;\n  AkSkin          *skin;\n  FListItem       *skinItem;\n  AkBoneWeights   *wl;\n  AkBoneWeight    *w, *iw, *old, *oiw;\n  AkDuplicator    *dupl;\n  AkUIntArray     *dupc, *dupcsum;\n  AkAccessor      *acci;\n  size_t          *pOldIndex, *wi;\n  size_t           vc, d, s, pno, poo, nwsum, newidx, next, tmp, count;\n  size_t           oldVertex;\n  uint32_t        *nj, i, j, k, vcount, primIndex;\n\n  if (!(skinItem = mesh->skins))\n    return;\n\n  heap      = ak_heap_getheap(mesh->geom);\n  doc       = ak_heap_data(heap);\n  pOldIndex = NULL;\n\n  /* fix every skin that attached to the mesh */\n  do {\n    skin      = skinItem->data;\n    prim      = mesh->primitive;\n    primIndex = 0;\n\n    while (prim) {\n      if (!(dupl = rb_find(doc->reserved, prim))\n          || dupl->dupCount < 1\n          || !dupl->range\n          || !prim->pos\n          || !(acci = prim->pos->accessor))\n        goto nxt_prim;\n\n      wl          = ak_skinFixWeightsForPrimitive(skin, prim, primIndex);\n      if (!wl || !wl->counts || !wl->indexes || !wl->weights)\n        goto nxt_prim;\n\n      old         = wl->weights;\n      pOldIndex   = wl->indexes;\n      oldVertex   = wl->nVertex;\n      vc          = acci->count;\n      dupc        = dupl->range->dupc;\n      dupcsum     = dupl->range->dupcsum;\n      if (!dupc || !dupcsum)\n        goto nxt_prim;\n\n      if (dupc->count < vc)\n        vc = dupc->count;\n\n      nwsum       = 0;\n\n      wl->nVertex = count = dupl->bufCount + dupl->dupCount;\n      nj          = ak_heap_alloc(heap, wl, count * sizeof(uint32_t));\n      wi          = ak_heap_alloc(heap, wl, count * sizeof(size_t));\n\n      /* copy to new location and duplicate if needed */\n      for (i = 0; i < vc; i++) {\n        if ((poo = dupc->items[3 * i + 2]) == 0)\n          continue;\n        if (poo > oldVertex)\n          continue;\n\n        pno    = dupc->items[3 * i];\n        d      = dupc->items[3 * i + 1];\n        if (pno >= dupcsum->count)\n          continue;\n\n        s      = dupcsum->items[pno];\n        vcount = wl->counts[poo - 1];\n\n        for (j = 0; j <= d; j++) {\n          newidx     = pno + j + s;\n          if (newidx >= count)\n            continue;\n          wi[newidx] = vcount; /* weight index     */\n          nj[newidx] = vcount; /* number of joints */\n          nwsum     += vcount;\n        }\n      }\n\n      /* prepare weight index */\n      for (next = j = 0; j < wl->nVertex; j++) {\n        tmp   = wi[j];\n        wi[j] = next;\n        next  = tmp + next;\n      }\n\n      /* now we know the size of arrays: weights, pCount, pIndex */\n      w     = ak_heap_alloc(heap, wl, sizeof(*w) * nwsum);\n      nwsum = 0;\n\n      for (i = 0; i < vc; i++) {\n        if ((poo = dupc->items[3 * i + 2]) == 0)\n          continue;\n        if (poo > oldVertex)\n          continue;\n\n        pno    = dupc->items[3 * i];\n        d      = dupc->items[3 * i + 1];\n        if (pno >= dupcsum->count)\n          continue;\n\n        s      = dupcsum->items[pno];\n        vcount = wl->counts[poo - 1];\n\n        for (j = 0; j <= d; j++) {\n          tmp = pno + j + s;\n          if (tmp >= count)\n            continue;\n\n          newidx = wi[tmp];\n\n          for (k = 0; k < vcount; k++) {\n            iw         = &w[newidx + k];\n            oiw        = &old[pOldIndex[poo - 1] + k];\n            iw->joint  = oiw->joint;\n            iw->weight = oiw->weight;\n          }\n\n          nwsum += vcount;\n        }\n      }\n\n      if (pOldIndex)\n        ak_free(pOldIndex);\n\n      if (old)\n        ak_free(old);\n\n      if (wl->counts)\n        ak_free(wl->counts);\n\n      wl->counts  = nj;\n      wl->indexes  = wi;\n      wl->weights = w;\n\n    nxt_prim:\n      primIndex++;\n      prim = prim->next;\n    }\n\n    skinItem = skinItem->next;\n  } while (skinItem);\n}\n"
  },
  {
    "path": "src/skin/fix.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_src_skin_index_h\n#define ak_src_skin_index_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\nak_skinFixWeights(AkMesh * __restrict mesh);\n\n#endif /* ak_src_skin_index_h */\n"
  },
  {
    "path": "src/skin/skin.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include <stdint.h>\n#include <string.h>\n\ntypedef struct AkSkinAccessorPair {\n  AkAccessor *jointAcc;\n  AkAccessor *weightAcc;\n  AkBuffer   *jointBuf;\n  AkBuffer   *weightBuf;\n  uint32_t    jStride;\n  uint32_t    wStride;\n  uint32_t    slotCount;\n} AkSkinAccessorPair;\n\ntypedef struct AkSkinInspectPrimitive {\n  struct AkSkinInspectPrimitive *next;\n  AkMeshPrimitive               *prim;\n  AkSkinAccessorPair            *pairs;\n  size_t                         vertexCount;\n  uint32_t                       pairCount;\n} AkSkinInspectPrimitive;\n\ntypedef struct AkSkinInspectView {\n  AkSkinInspectPrimitive *primitive;\n} AkSkinInspectView;\n\nAK_INLINE\nsize_t\nak_skinPrimitiveVertexCount(AkMeshPrimitive * __restrict prim) {\n  AkInput *inp;\n\n  if (!prim) return 0;\n\n  for (inp = prim->input; inp; inp = inp->next) {\n    if (inp->semantic == AK_INPUT_POSITION && inp->accessor)\n      return inp->accessor->count;\n  }\n\n  return 0;\n}\n\nAK_INLINE\nAkBoneWeights*\nak_skinWeightsForPrimitive(AkSkin          * __restrict skin,\n                           AkMeshPrimitive * __restrict prim,\n                           uint32_t                     primIdx) {\n  AkMeshPrimitive *it;\n  uint32_t         idx;\n\n  if (!skin || !skin->weights || skin->nPrims == 0)\n    return NULL;\n\n  if (prim && prim->mesh) {\n    idx = 0;\n    for (it = prim->mesh->primitive; it; it = it->next, idx++) {\n      if (it != prim)\n        continue;\n\n      if (idx < skin->nPrims && skin->weights[idx])\n        return skin->weights[idx];\n\n      break;\n    }\n  }\n\n  if (primIdx < skin->nPrims)\n    return skin->weights[primIdx];\n\n  return NULL;\n}\n\nAK_INLINE\nuint32_t\nak_skinAccessorPairCapacity(AkMeshPrimitive * __restrict prim) {\n  AkInput *inp;\n  uint32_t count;\n\n  count = 0;\n  for (inp = prim->input; inp; inp = inp->next) {\n    if (inp->semantic == AK_INPUT_JOINT)\n      count++;\n  }\n\n  return count;\n}\n\nAK_INLINE\nuint32_t\nak_skinCollectAccessorPairs(AkMeshPrimitive    * __restrict prim,\n                            AkSkinAccessorPair * __restrict pairs,\n                            uint32_t                        pairCap,\n                            size_t            * __restrict vCount) {\n  AkSkinAccessorPair *pair;\n  AkAccessor         *jointAcc, *weightAcc;\n  AkBuffer           *jointBuf, *weightBuf;\n  AkInput            *jointInp, *weightInp, *scan;\n  uint32_t            pairCount;\n  size_t              count;\n\n  if (!prim || !pairs || pairCap == 0 || !vCount)\n    return 0;\n\n  count     = ak_skinPrimitiveVertexCount(prim);\n  pairCount = 0;\n\n  for (jointInp = prim->input; jointInp; jointInp = jointInp->next) {\n    if (jointInp->semantic != AK_INPUT_JOINT)\n      continue;\n\n    weightInp = NULL;\n    for (scan = prim->input; scan; scan = scan->next) {\n      if (scan->semantic == AK_INPUT_WEIGHT && scan->set == jointInp->set) {\n        weightInp = scan;\n        break;\n      }\n    }\n    if (!weightInp)\n      continue;\n\n    jointAcc  = jointInp->accessor;\n    weightAcc = weightInp->accessor;\n    if (!jointAcc || !weightAcc)\n      continue;\n\n    jointBuf  = jointAcc->buffer;\n    weightBuf = weightAcc->buffer;\n    if (!jointBuf || !jointBuf->data || !weightBuf || !weightBuf->data)\n      continue;\n\n    if (pairCount >= pairCap)\n      break;\n\n    pair            = &pairs[pairCount];\n    pair->jointAcc  = jointAcc;\n    pair->weightAcc = weightAcc;\n    pair->jointBuf  = jointBuf;\n    pair->weightBuf = weightBuf;\n    pair->jStride   = (uint32_t)jointAcc->byteStride;\n    pair->wStride   = (uint32_t)weightAcc->byteStride;\n    if (!pair->jStride) pair->jStride = (uint32_t)jointAcc->fillByteSize;\n    if (!pair->wStride) pair->wStride = (uint32_t)weightAcc->fillByteSize;\n\n    pair->slotCount = jointAcc->componentCount;\n    if (weightAcc->componentCount < pair->slotCount)\n      pair->slotCount = weightAcc->componentCount;\n    if (pair->slotCount == 0)\n      continue;\n\n    if (count == 0 || jointAcc->count < count)\n      count = jointAcc->count;\n    if (weightAcc->count < count)\n      count = weightAcc->count;\n\n    pairCount++;\n  }\n\n  *vCount = count;\n  return pairCount;\n}\n\nstatic\nAkSkinInspectPrimitive*\nak_skinInspectPrimitive(AkSkin          * __restrict skin,\n                        AkMeshPrimitive * __restrict prim) {\n  AkSkinInspectView      *view;\n  AkSkinInspectPrimitive *insp;\n  AkSkinAccessorPair     *pairs;\n  AkHeap                 *heap;\n  uint32_t                pairCap, pairCount;\n  size_t                  vCount;\n\n  if (!skin || !prim)\n    return NULL;\n\n  view = skin->inspectResult;\n  if (view) {\n    for (insp = view->primitive; insp; insp = insp->next) {\n      if (insp->prim == prim)\n        return insp;\n    }\n  } else {\n    heap = ak_heap_getheap(skin);\n    view = ak_heap_calloc(heap, skin, sizeof(*view));\n    if (!view)\n      return NULL;\n    skin->inspectResult = view;\n  }\n\n  pairCap = ak_skinAccessorPairCapacity(prim);\n  if (pairCap == 0)\n    return NULL;\n\n  heap = ak_heap_getheap(view);\n  pairs = ak_heap_calloc(heap, view, sizeof(*pairs) * pairCap);\n  if (!pairs)\n    return NULL;\n\n  pairCount = ak_skinCollectAccessorPairs(prim, pairs, pairCap, &vCount);\n  if (pairCount == 0 || vCount == 0)\n    return NULL;\n\n  insp              = ak_heap_calloc(heap, view, sizeof(*insp));\n  insp->prim        = prim;\n  insp->pairs       = pairs;\n  insp->pairCount   = pairCount;\n  insp->vertexCount = vCount;\n  insp->next        = view->primitive;\n  view->primitive   = insp;\n\n  return insp;\n}\n\nAK_INLINE\nbool\nak_skinJointToU16(uint32_t joint, uint16_t *dest) {\n  if (joint > UINT16_MAX)\n    return false;\n\n  *dest = (uint16_t)joint;\n  return true;\n}\n\nAK_INLINE\nbool\nak_skinReadJoint(const char *src,\n                 AkTypeId    componentType,\n                 uint32_t    k,\n                 uint16_t   *dest) {\n  switch (componentType) {\n    case AKT_UBYTE:\n      *dest = ((const uint8_t *)src)[k];\n      return true;\n    case AKT_USHORT:\n      *dest = ((const uint16_t *)src)[k];\n      return true;\n    case AKT_UINT:\n      return ak_skinJointToU16(((const uint32_t *)src)[k], dest);\n    default:\n      return false;\n  }\n}\n\nAK_INLINE\nfloat\nak_skinReadWeight(const char *src, AkTypeId componentType, uint32_t k) {\n  switch (componentType) {\n    case AKT_FLOAT:\n      return ((const float *)src)[k];\n    case AKT_UBYTE:\n      return (float)((const uint8_t  *)src)[k] / 255.0f;\n    case AKT_USHORT:\n      return (float)((const uint16_t *)src)[k] / 65535.0f;\n    default:\n      return 0.0f;\n  }\n}\n\nAK_INLINE\nvoid\nak_skinNormalizeWeights(float * __restrict weights, uint32_t maxJoint) {\n  float    sum;\n  float    inv;\n  uint32_t k;\n\n  sum = 0.0f;\n  for (k = 0; k < maxJoint; k++)\n    sum += weights[k];\n\n  if (sum > 0.0f && isfinite(sum)) {\n    inv = 1.0f / sum;\n    for (k = 0; k < maxJoint; k++)\n      weights[k] *= inv;\n  }\n}\n\nAK_INLINE\nvoid\nak_skinWriteInterleavedRow(char           * __restrict row,\n                           const uint16_t * __restrict joints,\n                           const float    * __restrict weights,\n                           uint32_t                    maxJoint) {\n  size_t jointBytes;\n\n  jointBytes = sizeof(uint16_t) * maxJoint;\n  memcpy(row, joints, jointBytes);\n  memcpy(row + jointBytes, weights, sizeof(float) * maxJoint);\n}\n\nAK_INLINE\nvoid\nak_skinKeepInfluence(uint16_t * __restrict joints,\n                     float    * __restrict weights,\n                     uint32_t              maxJoint,\n                     uint16_t              joint,\n                     float                 weight) {\n  uint32_t k, minIdx;\n  float    minW;\n\n  if (!(weight > 0.0f) || !isfinite(weight)) return;\n\n  for (k = 0; k < maxJoint; k++) {\n    if (weights[k] > 0.0f && joints[k] == joint) {\n      weights[k] += weight;\n      return;\n    }\n  }\n\n  for (k = 0; k < maxJoint; k++) {\n    if (weights[k] <= 0.0f) {\n      joints[k]  = joint;\n      weights[k] = weight;\n      return;\n    }\n  }\n\n  minIdx = 0;\n  minW   = weights[0];\n  for (k = 1; k < maxJoint; k++) {\n    if (weights[k] < minW) {\n      minW   = weights[k];\n      minIdx = k;\n    }\n  }\n\n  if (weight > minW) {\n    joints[minIdx]  = joint;\n    weights[minIdx] = weight;\n  }\n}\n\nAK_EXPORT\nsize_t\nak_skinInterleave(AkSkin          * __restrict skin,\n                  AkMeshPrimitive * __restrict prim,\n                  uint32_t                     primIdx,\n                  uint32_t                     maxJoint,\n                  void           ** __restrict buff) {\n  AkBoneWeights      *bw;\n  AkBoneWeight       *src;\n  AkSkinInspectPrimitive *insp;\n  AkSkinAccessorPair *pairs;\n  AkSkinAccessorPair *pair;\n  uint16_t           *idxScratch;\n  uint16_t            joint;\n  float              *wgtScratch;\n  char               *out;\n  const char         *jSrc, *wSrc;\n  size_t              vCount, outBytes, v, posCount;\n  size_t              rowBytes;\n  uint32_t            k, pairCount, pairIdx, slotCount;\n\n  if (!skin || !prim || !buff || maxJoint == 0)\n    return 0;\n\n  bw        = ak_skinWeightsForPrimitive(skin, prim, primIdx);\n  pairs     = NULL;\n  pairCount = 0;\n\n  if (bw) {\n    vCount   = bw->nVertex;\n    posCount = ak_skinPrimitiveVertexCount(prim);\n    if (posCount > 0 && posCount < vCount)\n      vCount = posCount;\n  } else {\n    if (!(insp = ak_skinInspectPrimitive(skin, prim)))\n      return 0;\n\n    pairs     = insp->pairs;\n    pairCount = insp->pairCount;\n    vCount    = insp->vertexCount;\n  }\n\n  if (vCount == 0)\n    return 0;\n\n  rowBytes = maxJoint * (sizeof(uint16_t) + sizeof(float));\n  outBytes = vCount * rowBytes;\n  if (!(out = *buff))\n    out = *buff = ak_calloc(NULL, outBytes);\n  if (!out)\n    return 0;\n\n  idxScratch = alloca(sizeof(uint16_t) * maxJoint);\n  wgtScratch = alloca(sizeof(float)    * maxJoint);\n\n  for (v = 0; v < vCount; v++) {\n    memset(idxScratch, 0, sizeof(uint16_t) * maxJoint);\n    memset(wgtScratch, 0, sizeof(float)    * maxJoint);\n\n    if (bw) {\n      slotCount = bw->counts[v];\n      for (k = 0; k < slotCount; k++) {\n        src = &bw->weights[bw->indexes[v] + k];\n        if (ak_skinJointToU16(src->joint, &joint))\n          ak_skinKeepInfluence(idxScratch, wgtScratch,\n                               maxJoint, joint, src->weight);\n      }\n    } else {\n      for (pairIdx = 0; pairIdx < pairCount; pairIdx++) {\n        pair = &pairs[pairIdx];\n        jSrc = (const char *)pair->jointBuf->data\n               + pair->jointAcc->byteOffset\n               + (size_t)v * pair->jStride;\n        wSrc = (const char *)pair->weightBuf->data\n               + pair->weightAcc->byteOffset\n               + (size_t)v * pair->wStride;\n\n        slotCount = pair->slotCount;\n        for (k = 0; k < slotCount; k++) {\n          if (ak_skinReadJoint(jSrc,\n                               pair->jointAcc->componentType,\n                               (uint32_t)k,\n                               &joint)) {\n            ak_skinKeepInfluence(idxScratch, wgtScratch,\n                                 maxJoint, joint,\n                                 ak_skinReadWeight(wSrc,\n                                                   pair->weightAcc->componentType,\n                                                   (uint32_t)k));\n          }\n        }\n      }\n    }\n\n    ak_skinNormalizeWeights(wgtScratch, maxJoint);\n    ak_skinWriteInterleavedRow(out + v * rowBytes,\n                               idxScratch,\n                               wgtScratch,\n                               maxJoint);\n  }\n\n  return outBytes;\n}\n\nAK_EXPORT\nsize_t\nak_skinFillWeights(AkSkin          * __restrict skin,\n                   AkMeshPrimitive * __restrict prim,\n                   uint32_t                     primIdx,\n                   uint32_t                     maxJoint,\n                   uint16_t        * __restrict outIndices,\n                   float           * __restrict outWeights) {\n  AkBoneWeights      *bw;\n  AkBoneWeight       *src;\n  AkSkinInspectPrimitive *insp;\n  AkSkinAccessorPair *pairs, *pair;\n  const char         *jSrc, *wSrc;\n  size_t              vCount, v, k, posCount;\n  size_t              outRow;\n  uint32_t            pairCount, pairIdx;\n  uint32_t            slotCount;\n  uint16_t            joint;\n  float               weight;\n\n  if (!skin || !prim || !outIndices || !outWeights || maxJoint == 0)\n    return 0;\n\n  /*------------------------------------------------------------------*/\n  /* DAE path: per-primitive CSR layout in skin->weights[primIdx].    */\n  /* Variable joint count per vertex; pick top-N by weight, zero-pad  */\n  /* missing slots, normalize so sum==1 (graceful degradation when N  */\n  /* < authored joint count).                                         */\n  /*------------------------------------------------------------------*/\n  if ((bw = ak_skinWeightsForPrimitive(skin, prim, primIdx))) {\n    vCount = bw->nVertex;\n    posCount = ak_skinPrimitiveVertexCount(prim);\n    if (posCount > 0 && posCount < vCount)\n      vCount = posCount;\n\n    memset(outIndices, 0, vCount * maxJoint * sizeof(uint16_t));\n    memset(outWeights, 0, vCount * maxJoint * sizeof(float));\n\n    for (v = 0; v < vCount; v++) {\n      slotCount = bw->counts[v];\n      outRow    = (size_t)v * maxJoint;\n\n      for (k = 0; k < slotCount; k++) {\n        src = &bw->weights[bw->indexes[v] + k];\n        if (ak_skinJointToU16(src->joint, &joint)) {\n          ak_skinKeepInfluence(&outIndices[outRow],\n                               &outWeights[outRow],\n                               maxJoint,\n                               joint,\n                               src->weight);\n        }\n      }\n\n      ak_skinNormalizeWeights(&outWeights[outRow], maxJoint);\n    }\n    return vCount;\n  }\n\n  /*------------------------------------------------------------------*/\n  /* Raw accessor path: collect every JOINTS_n / WEIGHTS_n pair, then */\n  /* keep the top maxJoint influences per vertex and normalize.       */\n  /*------------------------------------------------------------------*/\n  if (!(insp = ak_skinInspectPrimitive(skin, prim))) {\n    return 0;\n  }\n\n  pairs     = insp->pairs;\n  pairCount = insp->pairCount;\n  vCount    = insp->vertexCount;\n\n  memset(outIndices, 0, vCount * maxJoint * sizeof(uint16_t));\n  memset(outWeights, 0, vCount * maxJoint * sizeof(float));\n\n  for (v = 0; v < vCount; v++) {\n    outRow = (size_t)v * maxJoint;\n\n    for (pairIdx = 0; pairIdx < pairCount; pairIdx++) {\n      pair = &pairs[pairIdx];\n\n      jSrc = (const char *)pair->jointBuf->data\n             + pair->jointAcc->byteOffset\n             + (size_t)v * pair->jStride;\n      wSrc = (const char *)pair->weightBuf->data\n             + pair->weightAcc->byteOffset\n             + (size_t)v * pair->wStride;\n\n      slotCount = pair->slotCount;\n      for (k = 0; k < slotCount; k++) {\n        if (!ak_skinReadJoint(jSrc,\n                              pair->jointAcc->componentType,\n                              (uint32_t)k,\n                              &joint))\n          continue;\n\n        weight = ak_skinReadWeight(wSrc,\n                                   pair->weightAcc->componentType,\n                                   (uint32_t)k);\n        ak_skinKeepInfluence(&outIndices[outRow],\n                             &outWeights[outRow],\n                             maxJoint,\n                             joint,\n                             weight);\n      }\n    }\n\n    ak_skinNormalizeWeights(&outWeights[outRow], maxJoint);\n  }\n\n  return vCount;\n}\n\nAK_EXPORT\nsize_t\nak_skinVerticesForJoint(AkSkin          * __restrict skin,\n                        AkMeshPrimitive * __restrict prim,\n                        uint32_t                     primIdx,\n                        uint32_t                     jointIdx,\n                        uint32_t        * __restrict outVertices,\n                        size_t                       capacity) {\n  AkBoneWeights      *bw;\n  AkBoneWeight       *src;\n  AkSkinInspectPrimitive *insp;\n  AkSkinAccessorPair *pairs, *pair;\n  const char         *jSrc, *wSrc;\n  size_t              vCount, posCount, v, k;\n  size_t              found, written;\n  uint32_t            pairCount, pairIdx;\n  uint32_t            slotCount;\n  uint16_t            joint;\n  float               weight;\n  bool                matched;\n\n  if (!skin || !prim)\n    return 0;\n\n  found   = 0;\n  written = 0;\n\n  if ((bw = ak_skinWeightsForPrimitive(skin, prim, primIdx))) {\n    vCount = bw->nVertex;\n    posCount = ak_skinPrimitiveVertexCount(prim);\n    if (posCount > 0 && posCount < vCount)\n      vCount = posCount;\n\n    for (v = 0; v < vCount; v++) {\n      matched   = false;\n      slotCount = bw->counts[v];\n\n      for (k = 0; k < slotCount; k++) {\n        src = &bw->weights[bw->indexes[v] + k];\n        if (src->joint == jointIdx\n            && src->weight > 0.0f\n            && isfinite(src->weight)) {\n          matched = true;\n          break;\n        }\n      }\n\n      if (!matched)\n        continue;\n\n      if (outVertices && written < capacity)\n        outVertices[written++] = (uint32_t)v;\n      found++;\n    }\n\n    return found;\n  }\n\n  if (jointIdx > UINT16_MAX)\n    return 0;\n\n  if (!(insp = ak_skinInspectPrimitive(skin, prim)))\n    return 0;\n\n  pairs     = insp->pairs;\n  pairCount = insp->pairCount;\n  vCount    = insp->vertexCount;\n\n  for (v = 0; v < vCount; v++) {\n    matched = false;\n\n    for (pairIdx = 0; pairIdx < pairCount && !matched; pairIdx++) {\n      pair = &pairs[pairIdx];\n\n      jSrc = (const char *)pair->jointBuf->data\n             + pair->jointAcc->byteOffset\n             + (size_t)v * pair->jStride;\n      wSrc = (const char *)pair->weightBuf->data\n             + pair->weightAcc->byteOffset\n             + (size_t)v * pair->wStride;\n\n      slotCount = pair->slotCount;\n      for (k = 0; k < slotCount; k++) {\n        if (!ak_skinReadJoint(jSrc,\n                              pair->jointAcc->componentType,\n                              (uint32_t)k,\n                              &joint))\n          continue;\n\n        weight = ak_skinReadWeight(wSrc,\n                                   pair->weightAcc->componentType,\n                                   (uint32_t)k);\n        if ((uint32_t)joint == jointIdx\n            && weight > 0.0f\n            && isfinite(weight)) {\n          matched = true;\n          break;\n        }\n      }\n    }\n\n    if (!matched)\n      continue;\n\n    if (outVertices && written < capacity)\n      outVertices[written++] = (uint32_t)v;\n    found++;\n  }\n\n  return found;\n}\n"
  },
  {
    "path": "src/string.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n\n#include \"../include/ak/assetkit.h\"\n#include \"common.h\"\n\nAK_EXPORT\nconst char*\nak_strltrim_fast(const char * __restrict str) {\n  const char *ptr;\n  size_t      len, i;\n  char        c;\n\n  len = strlen(str);\n  ptr = str;\n\n  if (len == 0)\n    return ptr;\n\n  for (i = 0; i < len; i++) {\n    c = str[i];\n    if (AK_ARRAY_SEP_CHECK) {\n      ptr++;\n      continue;\n    } else {\n      return ptr;\n    }\n  }\n\n  return ptr;\n}\n\nAK_EXPORT\nint\nak_strtok_count(char * __restrict buff,\n                char * __restrict sep,\n                size_t           *len) {\n  int i, count, itemc, buflen, found_sep;\n\n  buflen = (int)strlen(buff);\n  if (buflen == 0)\n    return 0;\n\n  count = itemc = 0;\n\n  /* because of buff[j + 1] */\n  if (buflen == 1)\n    return 1;\n\n  found_sep = false;\n  for (i = 0; i < buflen; i++) {\n    if (strchr(sep, buff[i])){\n      if (!found_sep) {\n        itemc++;\n        found_sep = true;\n      }\n      continue;\n    }\n\n    found_sep = false;\n    count++;\n  }\n\n  if (len)\n    *len = buflen - count;\n\n  /* left trim */\n  if (strchr(sep, buff[0]))\n    itemc--;\n\n  /* right trim */\n  if (strchr(sep, buff[buflen - 1]))\n    itemc--;\n\n  return itemc + 1;\n}\n\nAK_EXPORT\nint\nak_strtok_count_fast(char * __restrict buff,\n                     size_t            srclen,\n                     size_t           *len) {\n  int  i, count, itemc, buflen, found_sep;\n  char c;\n\n  if (srclen != 0)\n    buflen = (int)srclen;\n  else\n    buflen = (int)strlen(buff);\n\n  if (buflen == 0)\n    return 0;\n\n  count = itemc = 0;\n\n  /* because of buff[j + 1] */\n  if (buflen == 1)\n    return 1;\n\n  found_sep = false;\n  for (i = 0; i < buflen; i++) {\n    c = buff[i];\n    if (AK_ARRAY_SEP_CHECK) {\n      if (!found_sep) {\n        itemc++;\n        found_sep = true;\n      }\n      continue;\n    }\n\n    found_sep = false;\n    count++;\n  }\n\n  /* left trim */\n  c = buff[0];\n  if (AK_ARRAY_SEP_CHECK)\n    itemc--;\n\n  /* right trim */\n  c = buff[buflen - 1];\n  if (AK_ARRAY_SEP_CHECK)\n    itemc--;\n\n  if (len)\n    *len = buflen - count;\n  \n  return itemc + 1;\n}\n\nAK_EXPORT\nunsigned long\nak_strtof(char    * __restrict src,\n          size_t               srclen,\n          unsigned long        n,\n          AkFloat * __restrict dest) {\n  char *tok, *tok_end, *end;\n  char  c;\n\n  if (n == 0)\n    return 0;\n\n  dest = dest + n - 1ul;\n  tok = src;\n\n  if (srclen != 0) {\n    end = src + srclen;\n    \n    do {\n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n      \n      *(dest - --n) = strtof(tok, &tok_end);\n      tok = tok_end;\n      \n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n    } while (n > 0ul && tok < end);\n  } else {\n    do {\n      while (((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n      \n      *(dest - --n) = strtof(tok, &tok_end);\n      tok = tok_end;\n      \n      while (((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n    } while (n > 0ul && *tok != '\\0');\n  }\n\n  return n;\n}\n\nAK_EXPORT\nunsigned long\nak_strtof_line(char    * __restrict src,\n               size_t               srclen,\n               unsigned long        n,\n               AkFloat * __restrict dest) {\n  char *tok, *tok_end, *end;\n  char  c;\n\n  if (n == 0)\n    return 0;\n\n  dest = dest + n - 1ul;\n  tok = src;\n\n  if (srclen != 0) {\n    end = src + srclen;\n    \n    do {\n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK))\n        tok++;\n      \n      *(dest - --n) = strtof(tok, &tok_end);\n      tok = tok_end;\n      \n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK))\n        tok++;\n    } while (n > 0ul && tok < end);\n  } else {\n    do {\n      while (((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK))\n        tok++;\n      \n      *(dest - --n) = strtof(tok, &tok_end);\n      tok = tok_end;\n      \n      while (((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK))\n        tok++;\n    } while (n > 0ul && *tok != '\\0');\n  }\n\n  return n;\n}\n\nAK_EXPORT\nunsigned long\nak_strtod(char     * __restrict src,\n          size_t                srclen,\n          unsigned long         n,\n          AkDouble * __restrict dest) {\n  char *tok, *tok_end, *end;\n  char  c;\n\n  if (n == 0)\n    return 0;\n\n  dest = dest + n - 1ul;\n  tok = src;\n\n  if (srclen != 0) {\n    end = src + srclen;\n    \n    do {\n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n      \n      *(dest - --n) = strtod(tok, &tok_end);\n      tok = tok_end;\n      \n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n    } while (n > 0ul && tok < end);\n  } else {\n    do {\n      while (((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n      \n      *(dest - --n) = strtod(tok, &tok_end);\n      tok = tok_end;\n      \n      while (((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n    } while (n > 0ul && *tok != '\\0');\n  }\n\n  return n;\n}\n\nAK_EXPORT\nunsigned long\nak_strtoui(char    * __restrict src,\n           size_t               srclen,\n           unsigned long        n,\n           AkUInt  * __restrict dest) {\n  char    *tok, *tok_end, *end;\n  char     c;\n  AkUInt64 val;\n\n  if (n == 0)\n    return 0;\n\n  dest = dest + n - 1ul;\n  tok = src;\n\n  if (srclen != 0) {\n    end = src + srclen;\n    \n    do {\n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n      \n      val = strtoul(tok, &tok_end, 10);\n      tok = tok_end;\n\n      /* BUGFIX: some indices may come as -1 as BUG, fix this. */\n      if (val < UINT32_MAX) {\n        *(dest - --n) = (AkUInt)val;\n      } else {\n        *(dest - --n) = 0;\n      }\n\n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n    } while (n > 0ul && tok < end);\n  } else {\n    do {\n      while (((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n      \n      val = strtoul(tok, &tok_end, 10);\n      tok = tok_end;\n\n      /* BUGFIX: some indices may come as -1 as BUG, fix this. */\n      if (val < UINT32_MAX) {\n        *(dest - --n) = (AkUInt)val;\n      } else {\n        *(dest - --n) = 0;\n      }\n\n      while (((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n    } while (n > 0ul && *tok != '\\0');\n  }\n\n  return n;\n}\n\nAK_EXPORT\nunsigned long\nak_strtoi(char    * __restrict src,\n          size_t               srclen,\n          unsigned long        n,\n          AkInt   * __restrict dest) {\n  char *tok, *tok_end, *end;\n  char  c;\n  \n  if (n == 0)\n    return 0;\n  \n  dest = dest + n - 1ul;\n  tok = src;\n  \n  if (srclen != 0) {\n    end = src + srclen;\n    \n    do {\n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n      \n      *(dest - --n) = (AkUInt)strtol(tok, &tok_end, 10);\n      tok = tok_end;\n      \n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n    } while (n > 0ul && tok < end);\n  } else {\n    do {\n      while (((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n      \n      *(dest - --n) = (AkUInt)strtol(tok, &tok_end, 10);\n      tok = tok_end;\n      \n      while (((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n    } while (n > 0ul && *tok != '\\0');\n  }\n  \n  return n;\n}\n\nAK_EXPORT\nunsigned long\nak_strtoi_line(char    * __restrict src,\n               size_t               srclen,\n               unsigned long        n,\n               AkInt   * __restrict dest) {\n  char *tok, *tok_end, *end;\n  char  c;\n  \n  if (n == 0)\n    return 0;\n  \n  dest = dest + n - 1ul;\n  tok = src;\n  \n  if (srclen != 0) {\n    end = src + srclen;\n    \n    do {\n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK))\n        tok++;\n      \n      *(dest - --n) = (AkUInt)strtol(tok, &tok_end, 10);\n      tok = tok_end;\n      \n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK))\n        tok++;\n    } while (n > 0ul && tok < end);\n  } else {\n    do {\n      while (((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK))\n        tok++;\n      \n      *(dest - --n) = (AkUInt)strtol(tok, &tok_end, 10);\n      tok = tok_end;\n      \n      while (((void)(c = *tok), AK_ARRAY_SEPLINE_CHECK))\n        tok++;\n    } while (n > 0ul && *tok != '\\0');\n  }\n  \n  return n;\n}\n\nAK_EXPORT\nunsigned long\nak_strtob(char    * __restrict src,\n          size_t               srclen,\n          unsigned long        n,\n          AkBool  * __restrict dest) {\n  char *tok, *end;\n  char  c;\n\n  if (n == 0)\n    return 0;\n  \n  dest = dest + n - 1ul;\n  tok = src;\n  \n  if (srclen != 0) {\n    end = src + srclen;\n    \n    do {\n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n      \n      *(dest - --n) = tok[0] == 't' || tok[0] == 'T';\n      tok++;\n      \n      while (tok < end && ((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n    } while (n > 0ul && tok < end);\n  } else {\n    do {\n      while (((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n      \n      *(dest - --n) = tok[0] == 't' || tok[0] == 'T';\n      tok++;\n      \n      while (((void)(c = *tok), AK_ARRAY_SEP_CHECK))\n        tok++;\n    } while (n > 0ul && *tok != '\\0');\n  }\n  \n  return n;\n}\n\nAK_EXPORT\nchar*\nak_tolower(char *str) {\n  char *p;\n  for (p = str; *p; ++p) *p = tolower(*p);\n  return str;\n}\n\nAK_EXPORT\nchar*\nak_toupper(char *str) {\n  char *p;\n  for (p = str; *p; ++p) *p = toupper(*p);\n  return str;\n}\n"
  },
  {
    "path": "src/strpool.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _AK_STRPOOL_\n#  define _AK_STRPOOL_\n#endif\n\n#include \"strpool.h\"\n#include <string.h>\n\nconst char _s_ak_pool_0[] =\n\"POSITION\\0\"\n\"NORMAL\\0\"\n\"TEXCOORD\\0\"\n\"COLOR\\0\"\n;\n\n#undef _AK_STRPOOL_\n"
  },
  {
    "path": "src/strpool.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_strpool_h\n#  define ak_strpool_h\n\n#ifndef AK_STRPOOL_\n#  define _AK_EXTERN extern\n#else\n#  define _AK_EXTERN\n#endif\n\n_AK_EXTERN const char _s_ak_pool_0[];\n\n#define _s_ak_0(x) (_s_ak_pool_0 + x)\n\n/* _s_ak_pool_0 */\n#define _s_POSITION _s_ak_0(0)\n#define _s_NORMAL _s_ak_0(9)\n#define _s_TEXCOORD _s_ak_0(16)\n#define _s_COLOR _s_ak_0(25)\n\n#endif /* ak_strpool_h */\n"
  },
  {
    "path": "src/strpool.json",
    "content": "{\n  \"POSITION\": \"POSITION\",\n  \"NORMAL\": \"NORMAL\",\n  \"TEXCOORD\": \"TEXCOORD\",\n  \"COLOR\": \"COLOR\"\n}\n"
  },
  {
    "path": "src/strpool.py",
    "content": "#!/usr/bin/python3\n#\n# Copyright (C) 2020 Recep Aslantas\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nimport json\nfrom collections import OrderedDict\nfrom os.path     import realpath\nfrom os.path     import dirname\n\nheaderContents = [ ]\ndestdir        = dirname(realpath(__file__))\nspidx          = 0\npos            = 0\n\nfspoolJson = open(destdir + \"/strpool.json\")\nspool      = json.loads(fspoolJson.read(),\n                        object_pairs_hook=OrderedDict)\nfspoolJson.close()\n\nfspool_h = open(destdir + \"/strpool.h\", \"wb\");\nfspool_c = open(destdir + \"/strpool.c\", \"wb\");\n\ncopyright_str = \"\"\"\\\n/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\"\"\"\n\nfspool_h.write(copyright_str.encode())\nfspool_c.write(copyright_str.encode())\n\nfspool_h.write(\"\"\"\n#ifndef ak_strpool_h\n#  define ak_strpool_h\n\n#ifndef AK_STRPOOL_\n#  define _AK_EXTERN extern\n#else\n#  define _AK_EXTERN\n#endif\n\"\"\".encode())\n\nfspool_c.write(\"\"\"\n#ifndef _AK_STRPOOL_\n#  define _AK_STRPOOL_\n#endif\n\n#include \"strpool.h\"\n#include <string.h>\n\nconst char _s_ak_pool_0[] =\n\"\"\".encode())\n\nheaderContents.append(\"\\n/* _s_ak_pool_0 */\\n\")\n\nfor name, val in spool.items():\n  valLen = len(val) + 1\n\n  # string literal size: 2048\n  if pos + valLen > 2048:\n    pos    = 0\n    spidx += 1\n\n    fspool_c.write(\";\\n\\nconst char _s_ak_pool_{0}[] =\\n\"\n                     .format(str(spidx)).encode())\n\n    headerContents.append(\"\\n/* _s_ak_pool_{0} */\\n\"\n                            .format(spidx))\n\n  fspool_c.write(\"\\\"{0}\\\\0\\\"\\n\".format(val).encode())\n\n  headerContents.append(\"#define _s_{0} _s_ak_{1}({2})\\n\"\n                          .format(name, str(spidx), str(pos)))\n\n  pos += valLen\n\n# source file, then close it\nfspool_c.write(\";\\n\\n#undef _AK_STRPOOL_\\n\".encode())\nfspool_c.close()\n\n# header file\nfor idx in range(spidx + 1):\n  fspool_h.write(\"\\n_AK_EXTERN const char _s_ak_pool_{0}[];\"\n                   .format(str(idx)).encode())\n\nfspool_h.write(\"\\n\\n\".encode())\n\nfor idx in range(spidx + 1):\n  fspool_h.write(\"#define _s_ak_{0}(x) (_s_ak_pool_{0} + x)\\n\"\n                   .format(str(idx)).encode())\n\n# write header contents, then close it\nfspool_h.writelines(map(lambda x: x.encode(), headerContents))\nfspool_h.write(\"\\n#endif /* ak_strpool_h */\\n\".encode())\nfspool_h.close()\n\n# try free array\ndel headerContents[:]\n\n"
  },
  {
    "path": "src/topo/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME}\n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/topo/topo.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"topo.h\"\n\n/* create indices to fix topology,\n   an alternative way could be work with each input,\n   this can be provided by an option maybe in the future. */\nAK_HIDE\nvoid\ntopofix_noind(AkHeap          * __restrict heap,\n              AkMeshPrimitive * __restrict prim,\n              uint8_t                      trig_fan,\n              uint8_t                      trig_strip,\n              uint8_t                      line_loop,\n              uint8_t                      line_strip) {\n  /* TODO: no indices, handle inputs... */\n  AkInput     *input;\n  AkAccessor  *acc;\n  AkBuffer    *buff;\n  AkUIntArray *indices;\n  AkUInt      *it, nVertices, i, j;\n\n  if (!(input = prim->pos)\n      || !(acc  = input->accessor)\n      || !(buff = acc->buffer)) {\n    return;\n  }\n\n  nVertices = acc->count;\n\n  switch (prim->type) {\n    case AK_PRIMITIVE_TRIANGLES: {\n      if (trig_fan || trig_strip) {\n        AkTriangles *trig;\n        AkUInt       ntrigs;\n\n        trig = (AkTriangles *)prim;\n\n        switch (trig->mode) {\n          case AK_TRIANGLE_FAN:\n          case AK_TRIANGLE_STRIP: break;\n          default:                return;\n        }\n\n        ntrigs         = nVertices - 2;\n        indices        = ak_heap_calloc(heap, prim, sizeof(*indices) + sizeof(AkUInt) * ntrigs * 3);\n        indices->count = ntrigs * 3;\n        it             = indices->items;\n\n        switch (trig->mode) {\n          case AK_TRIANGLE_FAN:\n            if (trig_fan) {\n              for (i = 0, j = 0; i < ntrigs; ++i) {\n                it[j++] = 0; /* center vertex */\n                it[j++] = i + 1;\n                it[j++] = i + 2;\n              }\n            }\n            break;\n          case AK_TRIANGLE_STRIP:\n            if (trig_strip) {\n              for (i = 0, j = 0; i < ntrigs; ++i) {\n                if (i % 2 == 0) {\n                  it[j++] = i;\n                  it[j++] = i + 1;\n                  it[j++] = i + 2;\n                } else {\n                  it[j++] = i + 2;\n                  it[j++] = i + 1;\n                  it[j++] = i;\n                }\n              }\n            }\n            break;\n          default: break;\n        }\n\n        trig->mode        = AK_TRIANGLES;\n        prim->indices     = indices;\n        prim->indexStride = 1;\n      }\n      break;\n    }\n    case AK_PRIMITIVE_LINES:\n      if (line_loop || line_strip) {\n        AkLines *lines;\n        AkUInt   nlines;\n\n        lines = (AkLines *)prim;\n\n        switch (lines->mode) {\n          case AK_LINE_LOOP:\n            if (line_loop) {\n              nlines         = nVertices;\n              indices        = ak_heap_calloc(heap, prim, sizeof(*indices) + sizeof(AkUInt) * nlines * 2);\n              indices->count = nlines * 2;\n              it             = indices->items;\n\n              for (i = 0; i < nVertices - 1; ++i) {\n                it[i * 2]     = i;\n                it[i * 2 + 1] = i + 1;\n              }\n\n              /* close the loop */\n              it[(nVertices - 1) * 2] = nVertices - 1;\n              it[(nVertices - 1) * 2 + 1] = 0;\n\n              lines->mode       = AK_LINES;\n              prim->indices     = indices;\n              prim->indexStride = 1;\n            }\n            break;\n          case AK_LINE_STRIP:\n            if (line_strip) {\n              nlines         = nVertices - 1;\n              indices        = ak_heap_calloc(heap, prim, sizeof(*indices) + sizeof(AkUInt) * nlines * 2);\n              indices->count = nlines * 2;\n              it             = indices->items;\n\n              for (i = 0; i < nlines; ++i) {\n                it[i * 2]     = i;\n                it[i * 2 + 1] = i + 1;\n              }\n\n              lines->mode       = AK_LINES;\n              prim->indices     = indices;\n              prim->indexStride = 1;\n            }\n            break;\n          default: break;\n        }\n      }\n      break;\n    default: break;\n  }\n}\n\nAK_HIDE\nvoid\ntopofix_ind(AkHeap          * __restrict heap,\n            AkMeshPrimitive * __restrict prim,\n            uint8_t                      trig_fan,\n            uint8_t                      trig_strip,\n            uint8_t                      line_loop,\n            uint8_t                      line_strip) {\n  AkUInt      *it, *newIt, nIndices, i, j;\n  AkUIntArray *newIndices;\n\n  it       = prim->indices->items;\n  nIndices = (AkUInt)prim->indices->count;\n\n  switch (prim->type) {\n    case AK_PRIMITIVE_TRIANGLES: {\n      if (trig_fan || trig_strip) {\n        AkTriangles *trig;\n        AkUInt       nTriangles;\n\n        trig = (AkTriangles *)prim;\n\n        switch (trig->mode) {\n          case AK_TRIANGLE_FAN:\n          case AK_TRIANGLE_STRIP: break;\n          default:                return;\n        }\n\n        nTriangles        = nIndices - 2;\n        newIndices        = ak_heap_calloc(heap,\n                                           prim,\n                                           sizeof(*newIndices)\n                                           + sizeof(AkUInt) * nTriangles * 3);\n        newIndices->count = nTriangles * 3;\n        newIt             = newIndices->items;\n\n        switch (trig->mode) {\n          case AK_TRIANGLE_FAN:\n            if (trig_fan) {\n              for (i = 0, j = 0; i < nTriangles; ++i) {\n                newIt[j++] = it[0];\n                newIt[j++] = it[i + 1];\n                newIt[j++] = it[i + 2];\n              }\n\n              trig->mode = AK_TRIANGLES;\n            }\n            break;\n          case AK_TRIANGLE_STRIP:\n            if (trig_strip) {\n              for (i = 0, j = 0; i < nTriangles; ++i) {\n                newIt[j++] = it[i];\n\n                if (i % 2 == 0) {\n                  newIt[j++] = it[i + 1];\n                  newIt[j++] = it[i + 2];\n                } else {\n                  newIt[j++] = it[i + 2];\n                  newIt[j++] = it[i + 1];\n                }\n              }\n\n              trig->mode = AK_TRIANGLES;\n            }\n            break;\n          default: break;\n        }\n\n        ak_free(prim->indices);\n        prim->indices = newIndices;\n      }\n      break;\n    }\n    case AK_PRIMITIVE_LINES:\n      if (line_loop || line_strip) {\n        AkLines *lines;\n        AkUInt   nLines;\n\n        lines = (AkLines *)prim;\n\n        switch (lines->mode) {\n          case AK_LINE_LOOP:\n            if (line_loop) {\n              nLines            = nIndices;\n              newIndices        = ak_heap_calloc(heap,\n                                                 prim,\n                                                 sizeof(*newIndices)\n                                                 + sizeof(AkUInt) * nLines * 2);\n              newIndices->count = nLines * 2;\n              newIt             = newIndices->items;\n\n              for (i = 0; i < nIndices - 1; ++i) {\n                newIt[i * 2]     = it[i];\n                newIt[i * 2 + 1] = it[i + 1];\n              }\n\n              /* close the loop */\n              newIt[(nIndices - 1) * 2]     = it[nIndices - 1];\n              newIt[(nIndices - 1) * 2 + 1] = it[0];\n\n              ak_free(prim->indices);\n              prim->indices = newIndices;\n\n              lines->mode = AK_LINES;\n            }\n            break;\n          case AK_LINE_STRIP:\n            if (line_strip) {\n              nLines            = nIndices - 1;\n              newIndices        = ak_heap_calloc(heap,\n                                                 prim,\n                                                 sizeof(*newIndices)\n                                                 + sizeof(AkUInt) * nLines * 2);\n              newIndices->count = nLines * 2;\n              newIt             = newIndices->items;\n\n              for (i = 0; i < nLines; ++i) {\n                newIt[i * 2]     = it[i];\n                newIt[i * 2 + 1] = it[i + 1];\n              }\n\n              ak_free(prim->indices);\n              prim->indices = newIndices;\n\n              lines->mode = AK_LINES;\n            }\n            break;\n          default: break;\n        }\n      }\n      break;\n    default: break;\n  }\n}\n\nAK_HIDE\nvoid\ntopofix(AkMesh * mesh) {\n  AkHeap          *heap;\n  AkMeshPrimitive *prim;\n  uint8_t          trig_fan, trig_strip, line_loop, line_strip;\n\n  trig_fan   = (int)ak_opt_get(AK_OPT_CVT_TRIANGLEFAN);\n  trig_strip = (int)ak_opt_get(AK_OPT_CVT_TRIANGLESTRIP);\n  line_loop  = (int)ak_opt_get(AK_OPT_CVT_LINELOOP);\n  line_strip = (int)ak_opt_get(AK_OPT_CVT_LINESTRIP);\n\n  if (!trig_fan && !trig_strip && !line_loop && !line_strip)\n    return;\n\n  heap       = ak_heap_getheap(mesh->geom);\n  prim       = mesh->primitive;\n\n  while (prim) {\n    if (prim->indices) {\n      topofix_ind(heap, prim, trig_fan, trig_strip, line_loop, line_strip);\n    } else {\n      topofix_noind(heap, prim, trig_fan, trig_strip, line_loop, line_strip);\n    }\n\n    prim = prim->next;\n  }\n}\n"
  },
  {
    "path": "src/topo/topo.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef src_topo_h\n#define src_topo_h\n\n#include \"../common.h\"\n\nAK_HIDE\nvoid\ntopofix(AkMesh * mesh);\n\n#endif /* src_topo_h */\n"
  },
  {
    "path": "src/transform/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/transform/dup.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include <cglm/cglm.h>\n\nAK_EXPORT\nvoid\nak_transformDup(AkNode * __restrict srcNode,\n                AkNode * __restrict destNode) {\n  AkHeap      *heap;\n  AkTransform *trans;\n  AkObject    *transItem, *newTransItem, *lastTransItem;\n\n  if (!srcNode || !srcNode->transform || !destNode)\n    return;\n\n  heap  = ak_heap_getheap(srcNode);\n  trans = ak_heap_calloc(heap, destNode, sizeof(*trans));\n\n  transItem     = srcNode->transform->item;\n  lastTransItem = NULL;\n\n  while (transItem) {\n    switch (transItem->type) {\n      case AKT_MATRIX: {\n        AkMatrix *matrix, *newMatrix;\n\n        matrix = ak_objGet(transItem);\n        newTransItem = ak_objAlloc(heap,\n                                   destNode,\n                                   sizeof(*matrix),\n                                   AKT_MATRIX,\n                                   true);\n        newMatrix = ak_objGet(newTransItem);\n        ak_sid_dup(newTransItem, transItem);\n        glm_mat4_ucopy(matrix->val, newMatrix->val);\n        break;\n      }\n      case AKT_LOOKAT: {\n        AkLookAt *lookAt, *newLookAt;\n\n        lookAt = ak_objGet(transItem);\n        newTransItem = ak_objAlloc(heap,\n                                   destNode,\n                                   sizeof(*lookAt),\n                                   AKT_LOOKAT,\n                                   true);\n        newLookAt = ak_objGet(newTransItem);\n        ak_sid_dup(newTransItem, transItem);\n\n        glm_vec3_copy(lookAt->val[0], newLookAt->val[0]);\n        glm_vec3_copy(lookAt->val[1], newLookAt->val[1]);\n        glm_vec3_copy(lookAt->val[2], newLookAt->val[2]);\n        break;\n      }\n      case AKT_ROTATE: {\n        AkRotate *rotate, *newRotate;\n\n        rotate = ak_objGet(transItem);\n        newTransItem = ak_objAlloc(heap,\n                                   destNode,\n                                   sizeof(*rotate),\n                                   AKT_ROTATE,\n                                   true);\n        newRotate = ak_objGet(newTransItem);\n        ak_sid_dup(newTransItem, transItem);\n        glm_vec4_copy(rotate->val, newRotate->val);\n        break;\n      }\n      case AKT_QUATERNION: {\n        AkQuaternion *quat, *newQuat;\n\n        quat = ak_objGet(transItem);\n        newTransItem = ak_objAlloc(heap,\n                                   destNode,\n                                   sizeof(*quat),\n                                   AKT_QUATERNION,\n                                   true);\n        newQuat = ak_objGet(newTransItem);\n        ak_sid_dup(newTransItem, transItem);\n        glm_vec4_copy(quat->val, newQuat->val);\n        break;\n      }\n      case AKT_SCALE: {\n        AkScale *scale, *newScale;\n\n        scale = ak_objGet(transItem);\n        newTransItem = ak_objAlloc(heap,\n                                   destNode,\n                                   sizeof(*scale),\n                                   AKT_SCALE,\n                                   true);\n        newScale = ak_objGet(newTransItem);\n        ak_sid_dup(newTransItem, transItem);\n        glm_vec3_copy(scale->val, newScale->val);\n        break;\n      }\n      case AKT_TRANSLATE: {\n        AkTranslate *translate, *newTranslate;\n\n        translate = ak_objGet(transItem);\n        newTransItem = ak_objAlloc(heap,\n                                   destNode,\n                                   sizeof(*translate),\n                                   AKT_TRANSLATE,\n                                   true);\n        newTranslate = ak_objGet(newTransItem);\n        ak_sid_dup(newTransItem, transItem);\n        glm_vec3_copy(translate->val, newTranslate->val);\n        break;\n      }\n      case AKT_SKEW: {\n        AkSkew *skew, *newSkew;\n\n        skew = ak_objGet(transItem);\n        newTransItem = ak_objAlloc(heap,\n                                   destNode,\n                                   sizeof(*skew),\n                                   AKT_SKEW,\n                                   true);\n        newSkew = ak_objGet(newTransItem);\n        ak_sid_dup(newTransItem, transItem);\n\n        newSkew->angle = skew->angle;\n        glm_vec3_copy(skew->aroundAxis, newSkew->aroundAxis);\n        glm_vec3_copy(skew->rotateAxis, newSkew->rotateAxis);\n        break;\n      }\n      default:\n        transItem = transItem->next;\n        continue;\n    }\n\n    if (lastTransItem)\n      lastTransItem->next = newTransItem;\n    else\n      trans->item = newTransItem;\n    lastTransItem = newTransItem;\n\n    transItem = transItem->next;\n  }\n\n  destNode->transform = trans;\n}\n"
  },
  {
    "path": "src/transform/trans.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include <cglm/cglm.h>\n\n/*\n Note from OpenCOLLADA repo:\n Important assumptions on skew and shears:\n\n COLLADA uses the RenderMan standard:\n [ 1+s*dx*ex   s*dx*ey   s*dx*ez  0 ]\n [   s*dy*ex 1+s*dy*ey   s*dy*ez  0 ]\n [   s*dz*ex   s*dz*ey 1+s*dz*ez  0 ]\n [         0         0         0  1 ]\n where s = tan(skewAngle), if the axises are normalized\n */\nAK_EXPORT\nvoid\nak_transformSkewMatrix(AkSkew * __restrict skew,\n                       float  * matrix) {\n  mat4 mat;\n  float s;\n\n  s = tanf(skew->angle);\n\n  glm_vec3_scale(skew->aroundAxis, skew->rotateAxis[0] * s, mat[0]);\n  glm_vec3_scale(skew->aroundAxis, skew->rotateAxis[1] * s, mat[1]);\n  glm_vec3_scale(skew->aroundAxis, skew->rotateAxis[2] * s, mat[2]);\n\n  mat[0][0] += 1.0f;\n  mat[1][1] += 1.0f;\n  mat[2][2] += 1.0f;\n\n  mat[0][3] = mat[1][3] = mat[2][3] =\n  mat[3][0] = mat[3][1] = mat[3][2] = 0.0f;\n\n  mat[3][3] = 1.0f;\n\n  glm_mat4_copy(mat, (vec4 *)matrix);\n}\n\nAK_EXPORT\nvoid\nak_transformCombine(AkTransform * __restrict transform,\n                    float       * __restrict matrix) {\n  AkObject *transformItem, *transformGroup;\n  mat4      mat = GLM_MAT4_IDENTITY_INIT;\n  mat4      tmp;\n\n  if (!transform || !matrix)\n    goto ret;\n\n  transformGroup = transform->base;\n\nagain:\n  transformItem = transformGroup;\n  while (transformItem) {\n    switch (transformItem->type) {\n      case AKT_MATRIX: {\n        AkMatrix *matrix;\n        matrix = ak_objGet(transformItem);\n\n        glm_mat4_mul(mat, matrix->val, mat);\n        break;\n      }\n      case AKT_LOOKAT: {\n        AkLookAt *lookAt;\n        lookAt = ak_objGet(transformItem);\n\n        glm_lookat(lookAt->val[0],\n                   lookAt->val[1],\n                   lookAt->val[2],\n                   tmp);\n\n        /* because this is view matrix */\n        glm_inv_tr(tmp);\n        glm_mat4_mul(mat, tmp, mat);\n        break;\n      }\n      case AKT_ROTATE: {\n        AkRotate *rotate;\n\n        rotate = ak_objGet(transformItem);\n        glm_rotate_make(tmp, rotate->val[3], rotate->val);\n        glm_mat4_mul(mat, tmp, mat);\n        break;\n      }\n      case AKT_QUATERNION: {\n        AkQuaternion *quat;\n\n        quat = ak_objGet(transformItem);\n        glm_quat_mat4(quat->val, tmp);\n        glm_mat4_mul(mat, tmp, mat);\n        break;\n      }\n      case AKT_SCALE: {\n        AkScale *scale;\n        scale = ak_objGet(transformItem);\n\n        glm_scale_make(tmp, scale->val);\n        glm_mat4_mul(mat, tmp, mat);\n        break;\n      }\n      case AKT_TRANSLATE: {\n        AkTranslate *translate;\n        translate = ak_objGet(transformItem);\n\n        glm_translate_make(tmp, translate->val);\n        glm_mat4_mul(mat, tmp, mat);\n        break;\n      }\n      case AKT_SKEW: {\n        AkSkew *skew;\n        skew = ak_objGet(transformItem);\n\n        ak_transformSkewMatrix(skew, tmp[0]);\n        glm_mat4_mul(mat, tmp, mat);\n        break;\n      }\n    }\n\n    transformItem = transformItem->next;\n  }\n\n  if (transformGroup != transform->item) {\n    transformGroup = transform->item;\n    goto again;\n  }\n\nret:\n  glm_mat4_copy(mat, (vec4 *)matrix);\n}\n\nAK_EXPORT\nvoid\nak_transformFreeBase(AkTransform * __restrict transform) {\n  AkObject *transItem, *tofree;\n\n  transItem = transform->base;\n  while (transItem) {\n    tofree = transItem;\n    transItem = transItem->next;\n    ak_free(tofree);\n  }\n\n  transform->base = NULL;\n}\n\nAK_EXPORT\nAkObject*\nak_getTransformTRS(AkNode *node, AkTypeId transformType) {\n  AkHeap       *heap;\n  AkObject     *obj, *it, *prev;\n\n  heap = ak_heap_getheap(node);\n\n  if (node->transform) {\n    obj = node->transform->item;\n\n    if (obj) {\n      do {\n        if (obj->type == transformType)\n          return obj;\n      } while ((obj = obj->next));\n    }\n  } else {\n    node->transform = ak_heap_calloc(heap, node, sizeof(*node->transform));\n  }\n\n  switch (transformType) {\n    case AKT_QUATERNION: {\n      AkQuaternion *rot;\n      obj = ak_objAlloc(heap, node, sizeof(*rot), AKT_QUATERNION, true);\n      rot = ak_objGet(obj);\n      glm_quat_identity(rot->val);\n      break;\n    }\n    case AKT_TRANSLATE: {\n      obj = ak_objAlloc(heap, node, sizeof(AkTranslate), AKT_TRANSLATE, true);\n      break;\n    }\n    case AKT_SCALE: {\n      AkScale *scale;\n      obj   = ak_objAlloc(heap, node, sizeof(*scale), AKT_SCALE, true);\n      scale = ak_objGet(obj);\n      glm_vec3_one(scale->val);\n      break;\n    }\n    default:\n      return NULL;\n  }\n\n  it = node->transform->item;\n\n  if (!it) {\n    node->transform->item = obj;\n    return obj;\n  }\n\n  prev = NULL;\n\n  while (it) {\n    if (transformType < it->type)\n      break;\n    prev = it;\n    it   = it->next;\n  }\n\n  if (prev)\n    prev->next = obj;\n  else\n    node->transform->item = obj;\n  obj->next = it;\n\n  return obj;\n}\n"
  },
  {
    "path": "src/transform/traverse.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../common.h\"\n#include <cglm/cglm.h>\n\nAK_EXPORT\nvoid\nak_transformCombineWorld(AkNode * __restrict node,\n                         float  * matrix) {\n  AkNode *parentNode;\n  mat4    mat;\n  mat4    ptrans;\n\n  ak_transformCombine(node->transform, mat[0]);\n\n  parentNode = node->parent;\n  while (parentNode) {\n    ak_transformCombine(parentNode->transform, ptrans[0]);\n\n    glm_mat4_mul(ptrans, mat, mat);\n    parentNode = parentNode->parent;\n  }\n\n  glm_mat4_copy(mat, (vec4 *)matrix);\n}\n"
  },
  {
    "path": "src/trash.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"trash.h\"\n\n#include \"../include/ak/assetkit.h\"\n#include \"../include/ak/trash.h\"\n#include \"mem/common.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\ntypedef struct AkTrashItem {\n  void               *tofree;\n  struct AkTrashItem *next;\n} AkTrashItem;\n\nstatic AkHeap ak__trash_heap = {\n  .flags = 0\n};\n\n#define trash_heap &ak__trash_heap\n\nAkTrashItem *ak__trash = NULL;\n\nstatic\nint\nak__trash_cmp(void *key1, void *key2) {\n  if (key1 > key2)\n    return 1;\n  else if (key1 < key2)\n    return -1;\n\n  return 0;\n}\n\nAK_EXPORT\nvoid\nak_trash_add(void *mem) {\n  AkTrashItem *item;\n  AkHeapNode  *found;\n  AkResult     ret;\n\n  ret = ak_heap_getNodeById(trash_heap, mem, &found);\n  if (ret == AK_OK)\n    return;\n\n  item = ak_heap_alloc(trash_heap, NULL, sizeof(*item));\n  ak_heap_setId(trash_heap,\n                ak__alignof(item),\n                mem);\n\n  item->next   = ak__trash;\n  item->tofree = mem;\n  ak__trash    = item;\n}\n\nAK_EXPORT\nvoid\nak_trash_empty(void) {\n  while (ak__trash) {\n    AkTrashItem *tofree;\n    tofree    = ak__trash;\n    ak__trash = ak__trash->next;\n\n    ak_free(tofree->tofree);\n    ak_free(tofree);\n  }\n}\n\nAK_HIDE\nvoid\nak_trash_init(void) {\n  ak_heap_init(trash_heap, NULL, ak__trash_cmp, NULL);\n}\n\nAK_HIDE\nvoid\nak_trash_deinit(void) {\n  ak_heap_destroy(trash_heap);\n}\n"
  },
  {
    "path": "src/trash.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_src_trash_h\n#define ak_src_trash_h\n\n#include \"common.h\"\n\nAK_HIDE\nvoid\nak_trash_init(void);\n\nAK_HIDE\nvoid\nak_trash_deinit(void);\n\n#endif /* ak_src_trash_h */\n"
  },
  {
    "path": "src/tree.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"tree.h\"\n#include \"common.h\"\n#include \"utils.h\"\n\n#include \"xml.h\"\n\nAK_HIDE\nAkTreeNode*\ntree_fromxml(AkHeap * __restrict heap,\n             void   * __restrict memParent,\n             xml_t  * __restrict xml) {\n  AkTreeNode     *tree, *node, *inode/*, *pa*/;\n  AkTreeNodeAttr *att;\n  xml_t          *root, *xpa;\n  xml_attr_t     *xatt;\n  size_t          namelen;\n\n  tree = ak_heap_calloc(heap, memParent, sizeof(*tree));\n\n  root = xml;\n  xml  = xml->val;\n  node = tree;\n  /* pa   = NULL; */\n\n  while (xml) {\n    switch (xml->type) {\n      case XML_ELEMENT: {\n        inode = ak_heap_calloc(heap, node, sizeof(*inode));\n        namelen = xml->tagsize;\n\n        if (xml->prefix)\n          namelen += xml->prefixsize;\n        \n        inode->name = ak_heap_alloc(heap, inode, namelen + 1);\n\n        if (xml->prefix) {\n          memcpy((void *)inode->name, xml->prefix, xml->prefixsize);\n          memcpy((void *)(inode->name + xml->prefixsize), xml->tag, xml->tagsize);\n        } else {\n          memcpy((void *)inode->name, xml->tag, xml->tagsize);\n        }\n\n        memset((void *)(inode->name + namelen), '\\0', 1);\n        \n        if ((xatt = xml->attr)) {\n          do {\n            att       = ak_heap_calloc(heap, inode, sizeof(*att));\n            att->name = ak_heap_strndup(heap, att, xatt->name, xatt->namesize);\n            att->val  = ak_heap_strndup(heap, att, xatt->val,  xatt->valsize);\n\n            att->next      = inode->attribs;\n            inode->attribs = att;\n\n            inode->attrc++;\n          } while ((xatt = xatt->next));\n        }\n\n        if (node->chld)\n          node->chld->prev = inode;\n\n        inode->next   = node->chld;\n        node->chld    = inode;\n        inode->parent = node;\n        node->chldc++;\n        \n        node = inode;\n        \n        if (xml->val) {\n          xml = xml->val;\n          continue;\n        }\n        break;\n      }\n      case XML_STRING:\n        node->val = xml_strdup(xml, heap, node);\n        break;\n      default:\n        break;\n    }\n\n    if (xml->next) {\n      xml = xml->next;\n    } else if ((xpa = xml->parent) != root) {\n      do {\n        node = node->parent;\n        xml  = xpa->next;\n        xpa  = xpa->parent;\n        \n        if (xpa == root || xml == root)\n          goto end;\n      } while (!xml && xpa);\n    } else {\n      break;\n    }\n  } /* while (xml)  */\n\nend:\n  return tree;\n}\n"
  },
  {
    "path": "src/tree.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_src_tree_h\n#define ak_src_tree_h\n\n#include \"../include/ak/assetkit.h\"\n#include <xml/xml.h>\n/**\n * @brief load a tree from xml\n */\nAK_HIDE\nAkTreeNode*\ntree_fromxml(AkHeap * __restrict heap,\n             void   * __restrict memParent,\n             xml_t  * __restrict xml);\n\n#endif /* ak_src_tree_h */\n"
  },
  {
    "path": "src/type.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n#include \"../include/ak/type.h\"\n#include \"type.h\"\n#include \"default/type.h\"\n\n#include <ds/rb.h>\n\nRBTree *ak__typetree        = NULL;\nRBTree *ak__typetree_byname = NULL;\n\nAK_EXPORT\nAkTypeId\nak_typeidh(AkHeapNode * __restrict hnode) {\n  return hnode->typeid;\n}\n\nAK_EXPORT\nAkTypeId\nak_typeid(void * __restrict mem) {\n  AkHeapNode *hnode;\n\n  hnode = ak__alignof(mem);\n  return hnode->typeid;\n}\n\nAK_EXPORT\nvoid\nak_setypeid(void * __restrict mem,\n            AkTypeId tid) {\n  AkHeapNode *hnode;\n\n  hnode = ak__alignof(mem);\n  hnode->typeid = tid;\n}\n\nAK_EXPORT\nbool\nak_isKindOf(void * __restrict mem,\n            void * __restrict other) {\n  AkHeapNode *hnode1, *hnode2;\n\n  if (!mem || !other)\n    return false;\n\n  hnode1 = ak__alignof(mem);\n  hnode2 = ak__alignof(other);\n\n  return hnode1->typeid == hnode2->typeid;\n}\n\nAK_EXPORT\nAkTypeDesc*\nak_typeDesc(AkTypeId typeId) {\n  return rb_find(ak__typetree, (void *)typeId);\n}\n\nAK_EXPORT\nAkTypeDesc*\nak_typeDescByName(const char * __restrict name) {\n  return rb_find(ak__typetree_byname, (void *)name);\n}\n\nAK_EXPORT\nvoid\nak_registerType(AkTypeId typeId, AkTypeDesc *desc) {\n  rb_insert(ak__typetree, (void *)typeId, desc);\n\n  if (desc->typeName)\n    rb_insert(ak__typetree_byname, (void *)desc->typeName, desc);\n}\n\nAK_HIDE\nvoid\nak_type_init(void) {\n  AkTypeDesc *it;\n\n  ak__typetree        = rb_newtree(NULL, ds_cmp_i32p, NULL);\n  ak__typetree_byname = rb_newtree(NULL, ds_cmp_str,  NULL);\n\n  /* register predefined type descs */\n  it = (AkTypeDesc *)ak_def_typedesc();\n  if (it) {\n    do {\n      ak_registerType(it->typeId, it);\n      it++;\n    } while (it->size > 0);\n  }\n}\n\nAK_HIDE\nvoid\nak_type_deinit(void) {\n  rb_destroy(ak__typetree);\n  rb_destroy(ak__typetree_byname);\n}\n"
  },
  {
    "path": "src/type.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_src_type_h\n#define ak_src_type_h\n\nAK_HIDE\nvoid\nak_type_init(void);\n\nAK_HIDE\nvoid\nak_type_deinit(void);\n\n#endif /* ak_src_type_h */\n"
  },
  {
    "path": "src/utils.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"utils.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <sys/stat.h>\n#include <assert.h>\n#include <math.h>\n#include <errno.h>\n#include \"../include/ak/options.h\"\n\n#include \"mem/common.h\"\n\nchar *\nstrptime(const char * __restrict buf,\n         const char * __restrict fmt,\n         struct tm * __restrict tm);\n\nAkResult\nak_readfile(const char * __restrict file,\n            void       * __restrict parent,\n            void      ** __restrict dest,\n            size_t     * __restrict size) {\n  FILE      * infile;\n  size_t      blksize;\n  size_t      fsize;\n  size_t      fcontents_size;\n  size_t      total_read;\n  size_t      nread;\n  struct stat infile_st;\n  int         infile_no;\n  \n  infile = fopen(file, \"rb\");\n  if (!infile) {\n    fprintf(stderr, \"errno: %d: %s\", errno, strerror(errno));\n    goto err;\n  }\n\n  infile_no = fileno(infile);\n\n  if (fstat(infile_no, &infile_st) != 0)\n    goto err;\n\n  fsize = infile_st.st_size;\n\n  if (ak_opt_get(AK_OPT_USE_MMAP)\n      && (*dest = ak_mmap_rdonly(infile_no, fsize))) {\n    *size = fsize;\n    if (parent)\n      ak_mmap_attach(parent, *dest, fsize);\n    return AK_OK;\n  }\n\n#ifndef AK_WINAPI\n  blksize = infile_st.st_blksize;\n#else\n  blksize = 512;\n#endif\n\n  fcontents_size = sizeof(char) * fsize;\n  *size          = fcontents_size;\n\n  *dest = malloc(fcontents_size + 1);\n  assert(*dest && \"malloc failed\");\n\n  memset(*(char **)dest + fcontents_size, '\\0', 1);\n\n  total_read = 0;\n\n  do {\n    if ((fcontents_size - total_read) < blksize)\n      blksize = fcontents_size - total_read;\n\n    nread = fread(*(char **)dest + total_read,\n                  sizeof(char),\n                  blksize,\n                  infile);\n\n    total_read += nread;\n  } while (nread > 0 && total_read < fsize);\n\n  fclose(infile);\n\n  return AK_OK;\nerr:\n  *dest = NULL;\n  *size = 0;\n  return AK_ERR;\n}\n\ntime_t\nak_parse_date(const char * __restrict input,\n              const char ** __restrict ret) {\n  struct tm    _tm;\n  const char * cp;\n\n  memset(&_tm, '\\0', sizeof(_tm));\n\n  cp = strptime(input,\n                \"%Y-%m-%dT%T%Z\",\n                &_tm);\n\n  if (ret)\n    *ret = cp;\n\n  return mktime(&_tm);\n}\n\nAK_EXPORT\nint\nak_digitsize(size_t number) {\n  if (number == 0)\n    return 1;\n  \n  return (int)floor(log10((double)number)) + 1;\n}\n\nAK_HIDE\nvoid\nak_releasefile(void *file, size_t size) {\n  if (ak_opt_get(AK_OPT_USE_MMAP)) {\n    ak_unmap(file, size);\n    return;\n  }\n\n  free(file);\n}\n"
  },
  {
    "path": "src/utils.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_src_utils_h\n#define ak_src_utils_h\n\n#include \"../include/ak/assetkit.h\"\n#include \"../include/ak/util.h\"\n\nAkResult\nak_readfile(const char * __restrict file,\n            void       * __restrict parent,\n            void      ** __restrict dest,\n            size_t     * __restrict size);\ntime_t\nak_parse_date(const char * __restrict input,\n              const char ** __restrict ret);\nAK_HIDE\nvoid\nak_releasefile(void *file, size_t size);\n\n#endif /* ak_src_utils_h */\n"
  },
  {
    "path": "src/win32/CMakeLists.txt",
    "content": "FILE(GLOB CSources *.h *.c)\ntarget_sources(${PROJECT_NAME} \n  PRIVATE\n  ${CSources}\n)\n"
  },
  {
    "path": "src/win32/dllmain.c",
    "content": "/*\r\n * Copyright (C) 2020 Recep Aslantas\r\n *\r\n * Licensed under the Apache License, Version 2.0 (the \"License\");\r\n * you may not use this file except in compliance with the License.\r\n * You may obtain a copy of the License at\r\n *\r\n *      http://www.apache.org/licenses/LICENSE-2.0\r\n *\r\n * Unless required by applicable law or agreed to in writing, software\r\n * distributed under the License is distributed on an \"AS IS\" BASIS,\r\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n * See the License for the specific language governing permissions and\r\n * limitations under the License.\r\n */\r\n\r\n#include \"../config.h\"\r\n#include \"../mem/common.h\"\r\n\r\nvoid\r\nAK_CONSTRUCTOR\r\nak__init();\r\n\r\nvoid\r\nAK_DESTRUCTOR\r\nak__cleanup();\r\n\r\nBOOL APIENTRY DllMain(HMODULE hModule,\r\n                      DWORD   ul_reason_for_call,\r\n                      LPVOID  lpReserved);\r\n\r\nBOOL APIENTRY DllMain(HMODULE hModule,\r\n                      DWORD   ul_reason_for_call,\r\n                      LPVOID  lpReserved)\r\n{\r\n  (void)hModule;\r\n  (void)lpReserved;\r\n\r\n  switch (ul_reason_for_call) {\r\n  case DLL_PROCESS_ATTACH:\r\n    ak__init();\r\n    break;\r\n  case DLL_THREAD_ATTACH:\r\n    break;\r\n  case DLL_THREAD_DETACH:\r\n    break;\r\n  case DLL_PROCESS_DETACH:\r\n    ak__cleanup();\r\n    break;\r\n  }\r\n  return TRUE;\r\n}\r\n\r\n"
  },
  {
    "path": "src/win32/strptime.c",
    "content": "/*\n * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc.\n * All rights reserved.\n *\n * This code was contributed to The NetBSD Foundation by Klaus Klein.\n * Heavily optimised by David Laight\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS\n * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS\n * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <ctype.h>\n#include <string.h>\n#include <time.h>\n\ntypedef\tunsigned char\tu_char;\n\n/*\n * We do not implement alternate representations. However, we always\n * check whether a given modifier is allowed for a certain conversion.\n */\n#define ALT_E\t\t\t0x01\n#define ALT_O\t\t\t0x02\n#define LEGAL_ALT(x)\t\t{ if (alt_format & ~(x)) return NULL; }\n\nstatic char gmt[] = { \"GMT\" };\n#ifdef TM_ZONE\nstatic char utc[] = { \"UTC\" };\n#endif\n\n/* RFC-822/RFC-2822 */\nstatic const char * const nast[5] = {\n  \"EST\", \"CST\", \"MST\", \"PST\", \"\\0\\0\\0\"\n};\nstatic const char * const nadt[5] = {\n  \"EDT\", \"CDT\", \"MDT\", \"PDT\", \"\\0\\0\\0\"\n};\n\n#define TM_YEAR_BASE\t\t1900\n\nstatic const char *day[7] = {\n  \"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\",\n  \"Thursday\", \"Friday\", \"Saturday\"\n};\nstatic const char *abday[7] = {\n  \"Sun\",\"Mon\",\"Tue\",\"Wed\",\"Thu\",\"Fri\",\"Sat\"\n};\nstatic const char *mon[12] = {\n  \"January\", \"February\", \"March\", \"April\", \"May\", \"June\", \"July\",\n  \"August\", \"September\", \"October\", \"November\", \"December\"\n};\nstatic const char *abmon[12] = {\n  \"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\",\n  \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\"\n};\nstatic const char *am_pm[2] = {\n  \"AM\", \"PM\"\n};\n\nstatic const u_char *conv_num (const unsigned char *, int *,\n                               unsigned int, unsigned int);\nstatic const u_char *find_string (const u_char *, int *, const char * const *,\n                                  const char * const *, int);\n\nconst char *\nstrptime (const char *buf, const char *fmt, struct tm *tm);\n\nconst char *\nstrptime (const char *buf, const char *fmt, struct tm *tm)\n{\n  unsigned char c;\n  const unsigned char *bp, *ep;\n  int alt_format, i, split_year = 0, neg = 0, offs;\n  const char *new_fmt;\n\n  bp = (const u_char *)buf;\n\n  while (bp != NULL && (c = *fmt++) != '\\0') {\n    /* Clear 'alternate' modifier prior to new conversion. */\n    alt_format = 0;\n    i = 0;\n\n    /* Eat up white-space. */\n    if (isspace(c)) {\n      while (isspace(*bp))\n        bp++;\n      continue;\n    }\n\n    if (c != '%')\n      goto literal;\n\n\nagain:\n    switch (c = *fmt++) {\n    case '%':  /* \"%%\" is converted to \"%\". */\nliteral:\n      if (c != *bp++)\n        return NULL;\n      LEGAL_ALT(0);\n      continue;\n\n    /*\n     * \"Alternative\" modifiers. Just set the appropriate flag\n     * and start over again.\n     */\n    case 'E':  /* \"%E?\" alternative conversion modifier. */\n      LEGAL_ALT(0);\n      alt_format |= ALT_E;\n      goto again;\n\n    case 'O':  /* \"%O?\" alternative conversion modifier. */\n      LEGAL_ALT(0);\n      alt_format |= ALT_O;\n      goto again;\n\n    /*\n     * \"Complex\" conversion rules, implemented through recursion.\n     */\n    case 'c':  /* Date and time, using the locale's format. */\n      new_fmt = \"%x %X\";\n      goto recurse;\n\n    case 'D':  /* The date as \"%m/%d/%y\". */\n      new_fmt = \"%m/%d/%y\";\n      LEGAL_ALT(0);\n      goto recurse;\n\n    case 'F':  /* The date as \"%Y-%m-%d\". */\n      new_fmt = \"%Y-%m-%d\";\n      LEGAL_ALT(0);\n      goto recurse;\n\n    case 'R':  /* The time as \"%H:%M\". */\n      new_fmt = \"%H:%M\";\n      LEGAL_ALT(0);\n      goto recurse;\n\n    case 'r':  /* The time in 12-hour clock representation. */\n      new_fmt = \"%I:%M:%S %p\";\n      LEGAL_ALT(0);\n      goto recurse;\n\n    case 'T':  /* The time as \"%H:%M:%S\". */\n      new_fmt = \"%H:%M:%S\";\n      LEGAL_ALT(0);\n      goto recurse;\n\n    case 'X':  /* The time, using the locale's format. */\n      new_fmt = \"%H:%M:%S\";\n      goto recurse;\n\n    case 'x':  /* The date, using the locale's format. */\n      new_fmt = \"%m/%d/%y\";\n      recurse:\n      bp = (const u_char *)strptime((const char *)bp, new_fmt, tm);\n      LEGAL_ALT(ALT_E);\n      continue;\n\n    /*\n     * \"Elementary\" conversion rules.\n     */\n    case 'A':  /* The day of week, using the locale's form. */\n    case 'a':\n      bp = find_string(bp, &tm->tm_wday, day, abday, 7);\n      LEGAL_ALT(0);\n      continue;\n\n    case 'B':  /* The month, using the locale's form. */\n    case 'b':\n    case 'h':\n      bp = find_string(bp, &tm->tm_mon, mon, abmon, 12);\n      LEGAL_ALT(0);\n      continue;\n\n    case 'C':  /* The century number. */\n      i = 20;\n      bp = conv_num(bp, &i, 0, 99);\n\n      i = i * 100 - TM_YEAR_BASE;\n      if (split_year)\n        i += tm->tm_year % 100;\n      split_year = 1;\n      tm->tm_year = i;\n      LEGAL_ALT(ALT_E);\n      continue;\n\n    case 'd':  /* The day of month. */\n    case 'e':\n      bp = conv_num(bp, &tm->tm_mday, 1, 31);\n      LEGAL_ALT(ALT_O);\n      continue;\n\n    case 'k':  /* The hour (24-hour clock representation). */\n      LEGAL_ALT(0);\n      /* FALLTHROUGH */\n    case 'H':\n      bp = conv_num(bp, &tm->tm_hour, 0, 23);\n      LEGAL_ALT(ALT_O);\n      continue;\n\n    case 'l':  /* The hour (12-hour clock representation). */\n      LEGAL_ALT(0);\n      /* FALLTHROUGH */\n    case 'I':\n      bp = conv_num(bp, &tm->tm_hour, 1, 12);\n      if (tm->tm_hour == 12)\n        tm->tm_hour = 0;\n      LEGAL_ALT(ALT_O);\n      continue;\n\n    case 'j':  /* The day of year. */\n      i = 1;\n      bp = conv_num(bp, &i, 1, 366);\n      tm->tm_yday = i - 1;\n      LEGAL_ALT(0);\n      continue;\n\n    case 'M':  /* The minute. */\n      bp = conv_num(bp, &tm->tm_min, 0, 59);\n      LEGAL_ALT(ALT_O);\n      continue;\n\n    case 'm':  /* The month. */\n      i = 1;\n      bp = conv_num(bp, &i, 1, 12);\n      tm->tm_mon = i - 1;\n      LEGAL_ALT(ALT_O);\n      continue;\n\n    case 'p':  /* The locale's equivalent of AM/PM. */\n      bp = find_string(bp, &i, am_pm, NULL, 2);\n      if (tm->tm_hour > 11)\n        return NULL;\n      tm->tm_hour += i * 12;\n      LEGAL_ALT(0);\n      continue;\n\n    case 'S':  /* The seconds. */\n      bp = conv_num(bp, &tm->tm_sec, 0, 61);\n      LEGAL_ALT(ALT_O);\n      continue;\n\n    case 'U':  /* The week of year, beginning on sunday. */\n    case 'W':  /* The week of year, beginning on monday. */\n      /*\n       * XXX This is bogus, as we can not assume any valid\n       * information present in the tm structure at this\n       * point to calculate a real value, so just check the\n       * range for now.\n       */\n       bp = conv_num(bp, &i, 0, 53);\n       LEGAL_ALT(ALT_O);\n       continue;\n\n    case 'w':  /* The day of week, beginning on sunday. */\n      bp = conv_num(bp, &tm->tm_wday, 0, 6);\n      LEGAL_ALT(ALT_O);\n      continue;\n\n    case 'u':  /* The day of week, monday = 1. */\n      bp = conv_num(bp, &i, 1, 7);\n      tm->tm_wday = i % 7;\n      LEGAL_ALT(ALT_O);\n      continue;\n\n    case 'g':\n      /* The year corresponding to the ISO week\n       * number but without the century.\n       */\n      bp = conv_num(bp, &i, 0, 99);\n      continue;\n\n    case 'G':\n      /* The year corresponding to the ISO week\n       * number with century.\n       */\n      do\n        bp++;\n      while (isdigit(*bp));\n      continue;\n\n    case 'V':  /* The ISO 8601:1988 week number as decimal */\n      bp = conv_num(bp, &i, 0, 53);\n      continue;\n\n    case 'Y':  /* The year. */\n      i = TM_YEAR_BASE;\t/* just for data sanity... */\n      bp = conv_num(bp, &i, 0, 9999);\n      tm->tm_year = i - TM_YEAR_BASE;\n      LEGAL_ALT(ALT_E);\n      continue;\n\n    case 'y':  /* The year within 100 years of the epoch. */\n      /* LEGAL_ALT(ALT_E | ALT_O); */\n      bp = conv_num(bp, &i, 0, 99);\n\n      if (split_year)\n        /* preserve century */\n        i += (tm->tm_year / 100) * 100;\n      else {\n        split_year = 1;\n        if (i <= 68)\n          i = i + 2000 - TM_YEAR_BASE;\n        else\n          i = i + 1900 - TM_YEAR_BASE;\n      }\n      tm->tm_year = i;\n      continue;\n\n    case 'Z':\n      if (strncmp((const char *)bp, gmt, 3) == 0) {\n        tm->tm_isdst = 0;\n#ifdef TM_GMTOFF\n        tm->TM_GMTOFF = 0;\n#endif\n#ifdef TM_ZONE\n        tm->TM_ZONE = gmt;\n#endif\n        bp += 3;\n      } else {\n#if defined(_TM_DEFINED) && !defined(_WIN32_WCE)\n        _tzset();\n        ep = find_string(bp, &i,\n                         (const char * const *)tzname,\n                         NULL, 2);\n        if (ep != NULL) {\n          tm->tm_isdst = i;\n#ifdef TM_GMTOFF\n          tm->TM_GMTOFF = -(timezone);\n#endif\n#ifdef TM_ZONE\n          tm->TM_ZONE = tzname[i];\n#endif\n        }\n        bp = ep;\n#endif\n      }\n      continue;\n\n    case 'z':\n      /*\n       * We recognize all ISO 8601 formats:\n       * Z = Zulu time/UTC\n       * [+-]hhmm\n       * [+-]hh:mm\n       * [+-]hh\n       * We recognize all RFC-822/RFC-2822 formats:\n       * UT|GMT\n       *    North American : UTC offsets\n       * E[DS]T = Eastern : -4 | -5\n       * C[DS]T = Central : -5 | -6\n       * M[DS]T = Mountain: -6 | -7\n       * P[DS]T = Pacific : -7 | -8\n       *    Military\n       * [A-IL-M] = -1 ... -9 (J not used)\n       * [N-Y]  = +1 ... +12\n       */\n      while (isspace(*bp))\n        bp++;\n\n      switch (*bp++) {\n      case 'G':\n        if (*bp++ != 'M')\n          return NULL;\n        /*FALLTHROUGH*/\n      case 'U':\n        if (*bp++ != 'T')\n          return NULL;\n        /*FALLTHROUGH*/\n      case 'Z':\n        tm->tm_isdst = 0;\n#ifdef TM_GMTOFF\n        tm->TM_GMTOFF = 0;\n#endif\n#ifdef TM_ZONE\n        tm->TM_ZONE = utc;\n#endif\n        continue;\n      case '+':\n        neg = 0;\n        break;\n      case '-':\n        neg = 1;\n        break;\n      default:\n        --bp;\n        ep = find_string(bp, &i, nast, NULL, 4);\n        if (ep != NULL) {\n#ifdef TM_GMTOFF\n          tm->TM_GMTOFF = -5 - i;\n#endif\n#ifdef TM_ZONE\n          tm->TM_ZONE = __UNCONST(nast[i]);\n#endif\n          bp = ep;\n          continue;\n        }\n        ep = find_string(bp, &i, nadt, NULL, 4);\n        if (ep != NULL) {\n          tm->tm_isdst = 1;\n#ifdef TM_GMTOFF\n          tm->TM_GMTOFF = -4 - i;\n#endif\n#ifdef TM_ZONE\n          tm->TM_ZONE = __UNCONST(nadt[i]);\n#endif\n          bp = ep;\n          continue;\n        }\n\n        if ((*bp >= 'A' && *bp <= 'I') ||\n          (*bp >= 'L' && *bp <= 'Y')) {\n#ifdef TM_GMTOFF\n          /* Argh! No 'J'! */\n          if (*bp >= 'A' && *bp <= 'I')\n            tm->TM_GMTOFF =\n              ('A' - 1) - (int)*bp;\n          else if (*bp >= 'L' && *bp <= 'M')\n            tm->TM_GMTOFF = 'A' - (int)*bp;\n          else if (*bp >= 'N' && *bp <= 'Y')\n            tm->TM_GMTOFF = (int)*bp - 'M';\n#endif\n#ifdef TM_ZONE\n          tm->TM_ZONE = NULL; /* XXX */\n#endif\n          bp++;\n          continue;\n        }\n        return NULL;\n      }\n      offs = 0;\n      for (i = 0; i < 4; ) {\n        if (isdigit(*bp)) {\n          offs = offs * 10 + (*bp++ - '0');\n          i++;\n          continue;\n        }\n        if (i == 2 && *bp == ':') {\n          bp++;\n          continue;\n        }\n        break;\n      }\n      switch (i) {\n      case 2:\n        offs *= 100;\n        break;\n      case 4:\n        i = offs % 100;\n        if (i >= 60)\n          return NULL;\n        /* Convert minutes into decimal */\n        offs = (offs / 100) * 100 + (i * 50) / 30;\n        break;\n      default:\n        return NULL;\n      }\n      if (neg)\n        offs = -offs;\n      tm->tm_isdst = 0;  /* XXX */\n#ifdef TM_GMTOFF\n      tm->TM_GMTOFF = offs;\n#endif\n#ifdef TM_ZONE\n      tm->TM_ZONE = NULL;  /* XXX */\n#endif\n      continue;\n\n    /*\n     * Miscellaneous conversions.\n     */\n    case 'n':  /* Any kind of white-space. */\n    case 't':\n      while (isspace(*bp))\n        bp++;\n      LEGAL_ALT(0);\n      continue;\n\n\n    default:  /* Unknown/unsupported conversion. */\n      return NULL;\n    }\n  }\n\n  return ((const char *)bp);\n}\n\n\nstatic const u_char *\nconv_num (const unsigned char *buf, int *dest,\n          unsigned int llim, unsigned int ulim)\n{\n  unsigned int result = 0;\n  unsigned char ch;\n\n  /* The limit also determines the number of valid digits. */\n  unsigned int rulim = ulim;\n\n  ch = *buf;\n  if (ch < '0' || ch > '9')\n    return NULL;\n\n  do {\n    result *= 10;\n    result += ch - '0';\n    rulim /= 10;\n    ch = *++buf;\n  } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9');\n\n  if (result < llim || result > ulim)\n    return NULL;\n\n  *dest = result;\n  return buf;\n}\n\nstatic int\nstrncasecmp (const char *s1, const char *s2, size_t n)\n{\n  if (n == 0) return 0;\n\n  while (n-- != 0 && tolower(*s1) == tolower(*s2)) {\n    if (n == 0 || *s1 == '\\0' || *s2 == '\\0')\n      break;\n    s1++;\n    s2++;\n  }\n \n  return tolower(*(const unsigned char *) s1)\n    - tolower(*(const unsigned char *) s2);\n}\n\nstatic const u_char *\nfind_string (const u_char *bp, int *tgt, const char * const *n1,\n             const char * const *n2, int c)\n{\n  int i;\n  size_t len;\n\n  /* check full name - then abbreviated ones */\n  for (; n1 != NULL; n1 = n2, n2 = NULL) {\n    for (i = 0; i < c; i++, n1++) {\n      len = strlen(*n1);\n      if (strncasecmp(*n1, (const char *)bp, len) == 0) {\n        *tgt = i;\n        return bp + len;\n      }\n    }\n  }\n\n  /* Nothing matched */\n  return NULL;\n}\n\n"
  },
  {
    "path": "src/xml.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"common.h\"\n#include \"xml.h\"\n\nAK_EXPORT\nchar *\nxml_strdup(const xml_t * __restrict xobj,\n           AkHeap      * __restrict heap,\n           void        * __restrict parent) {\n  char        *s, *p;\n  const xml_t *v;\n  size_t       len;\n\n  if ((len = xmls_sumlen(xobj)) < 1)\n    return NULL;\n\n  s = p = ak_heap_alloc(heap, parent, len);\n  if (xobj->type!= XML_STRING) {\n    v = xmls(xobj); /* because len > 0 */\n    \n    do {\n      memcpy(p, v->val, v->valsize);\n      p += v->valsize;\n    } while ((v = xmls_next(v)));\n  } else {\n    memcpy(p, xobj->val, xobj->valsize);\n  }\n\n  s[len - 1] = '\\0';\n  \n  return s;\n}\n\nAK_EXPORT\nunsigned long\nxml_strtof_fast(const xml_t * __restrict xobj,\n                AkFloat     * __restrict dest,\n                unsigned long            n) {\n  const xml_t  *v;\n  unsigned long rem;\n  \n  /* this step must be done before calling this func. */\n  /*\n  if (!(v = xmls(xobj)))\n    return 0;\n  */\n  \n  if (!(v = xobj)\n      || !v->val\n      || (rem = n) < 1)\n    return 0;\n  \n  while ((rem = ak_strtof(v->val,\n                          v->valsize,\n                          rem,\n                          dest + n - rem))\n         && (v = xmls_next(v)));\n  \n  return rem;\n}\n\nAK_EXPORT\nunsigned long\nxml_strtoui_fast(const xml_t  * __restrict xobj,\n                 AkUInt * __restrict dest,\n                 unsigned long       n) {\n  const xml_t  *v;\n  unsigned long rem;\n  \n  /* this step must be done before calling this func. */\n  /*\n  if (!(v = xmls(xobj)))\n    return 0;\n  */\n  \n  if (!(v = xobj)\n      || !v->val\n      || (rem = n) < 1)\n    return 0;\n  \n  while ((rem = ak_strtoui(v->val,\n                           v->valsize,\n                           rem,\n                           dest + n - rem))\n         && (v = xmls_next(v)));\n  \n\n\n  return rem;\n}\n\nAK_EXPORT\nunsigned long\nxml_strtoi_fast(const xml_t * __restrict xobj,\n                AkInt * __restrict dest,\n                unsigned long      n) {\n  const xml_t  *v;\n  unsigned long rem;\n  \n  /* this step must be done before calling this func. */\n  /*\n  if (!(v = xmls(xobj)))\n    return 0;\n  */\n  \n  if (!(v = xobj)\n      || !v->val\n      || (rem = n) < 1)\n    return 0;\n\n  while ((rem = ak_strtoi(v->val,\n                          v->valsize,\n                          rem,\n                          dest + n - rem))\n         && (v = xmls_next(v)));\n\n  return rem;\n}\n\nAK_EXPORT\nunsigned long\nxml_strtob_fast(const xml_t  * __restrict xobj,\n                AkBool * __restrict dest,\n                unsigned long       n) {\n  const xml_t  *v;\n  unsigned long rem;\n  \n  /* this step must be done before calling this func. */\n  /*\n  if (!(v = xmls(xobj)))\n    return 0;\n  */\n  \n  if (!(v = xobj)\n      || !v->val\n      || (rem = n) < 1)\n    return 0;\n\n  \n  while ((rem = ak_strtob(v->val,\n                          v->valsize,\n                          rem,\n                          dest + n - rem))\n         && (v = xmls_next(v)));\n\n  return rem;\n}\n\nAK_EXPORT\nsize_t\nxml_strtok_count_fast(const xml_t  * __restrict xobj,\n                      size_t       * __restrict len) {\n  const xml_t *v, *p;\n  size_t       count, len_total, l;\n  \n//  if (!(v = xmls(xobj)))\n//    return 0;\n  \n  v = xobj;\n\n  len_total = 0;\n  count     = 0;\n  p         = v;\n\n  do {\n    count     += ak_strtok_count_fast(p->val, p->valsize, &l);\n    len_total += l;\n  } while ((p = xmls_next(p)));\n\n  if (len)\n    *len = len_total;\n\n  return count;\n}\n\nAK_EXPORT\nAkResult\nxml_strtof_arrayL(AkHeap         * __restrict heap,\n                  void           * __restrict memp,\n                  const xml_t    * __restrict xobj,\n                  AkFloatArrayL ** __restrict array) {\n  AkFloatArrayL *arr;\n  unsigned long  count;\n\n  if ((count = (unsigned long)xml_strtok_count_fast(xobj, NULL)) == 0)\n    return AK_ERR;\n\n  arr = ak_heap_alloc(heap, memp, sizeof(*arr) + sizeof(AkFloat) * count);\n  xml_strtof_fast(xobj, arr->items, count);\n\n  arr->count = count;\n  arr->next  = NULL;\n\n  *array = arr;\n\n  return AK_OK;\n}\n\nAK_EXPORT\nAkResult\nxml_strtoui_array(AkHeap       * __restrict heap,\n                  void         * __restrict memp,\n                  const xml_t  * __restrict xobj,\n                  AkUIntArray ** __restrict array) {\n  AkUIntArray  *arr;\n  unsigned long count;\n\n  if ((count = (unsigned long)xml_strtok_count_fast(xobj, NULL)) == 0)\n    return AK_ERR;\n\n  arr = ak_heap_alloc(heap, memp, sizeof(*arr) + sizeof(AkUInt) * count);\n  xml_strtoui_fast(xobj, arr->items, count);\n\n  arr->count = count;\n\n  *array = arr;\n\n  return AK_OK;\n}\n\nAK_EXPORT\nchar *\nxmla_strdup(const xml_attr_t * __restrict attr,\n            AkHeap           * __restrict heap,\n            void             * __restrict parent) {\n  const char *s;\n  \n  if (!attr || !(s = attr->val))\n    return NULL;\n\n  return ak_heap_strndup(heap, parent, s, attr->valsize);\n}\n\nAK_EXPORT\nchar *\nxmla_strdup_by(const xml_t * __restrict xobject,\n               AkHeap      * __restrict heap,\n               const char  * __restrict name,\n               void        * __restrict parent) {\n  xml_attr_t *att;\n  \n  if ((att = xmla(xobject, name)))\n    return ak_heap_strndup(heap, parent, att->val, att->valsize);\n\n  return NULL;\n}\n\nAK_EXPORT\nvoid\nxmla_setid(const xml_t * __restrict xobject,\n           AkHeap      * __restrict heap,\n           void        * __restrict memptr) {\n  xml_attr_t *att;\n  \n  if ((att = xmla(xobject, \"id\")))\n    ak_setId(memptr, ak_heap_strndup(heap, memptr, att->val, att->valsize));\n}\n"
  },
  {
    "path": "src/xml.h",
    "content": "/*\n* Copyright (c), Recep Aslantas.\n*\n* MIT License (MIT), http://opensource.org/licenses/MIT\n* Full license can be found in the LICENSE file\n*/\n\n#ifndef ak_src_xml_h\n#define ak_src_xml_h\n\n#include \"common.h\"\n#include <xml/xml.h>\n\nAK_EXPORT\nchar *\nxml_strdup(const xml_t * __restrict xobj,\n           AkHeap      * __restrict heap,\n           void        * __restrict parent);\n\nAK_EXPORT\nunsigned long\nxml_strtof_fast(const xml_t * __restrict xobj,\n                AkFloat     * __restrict dest,\n                unsigned long            n);\n\nAK_EXPORT\nunsigned long\nxml_strtoui_fast(const xml_t * __restrict xobj,\n                 AkUInt      * __restrict dest,\n                 unsigned long            n);\n\nAK_EXPORT\nunsigned long\nxml_strtoi_fast(const xml_t * __restrict xobj,\n                AkInt       * __restrict dest,\n                unsigned long            n);\n\nAK_EXPORT\nunsigned long\nxml_strtob_fast(const xml_t * __restrict xobj,\n                AkBool      * __restrict dest,\n                unsigned long            n);\n\nAK_EXPORT\nsize_t\nxml_strtok_count_fast(const xml_t * __restrict xobj,\n                      size_t      * __restrict len);\n\nAK_EXPORT\nAkResult\nxml_strtof_arrayL(AkHeap         * __restrict heap,\n                  void           * __restrict memp,\n                  const xml_t    * __restrict xobj,\n                  AkFloatArrayL ** __restrict array);\n\nAK_EXPORT\nAkResult\nxml_strtoui_array(AkHeap       * __restrict heap,\n                  void         * __restrict memp,\n                  const xml_t  * __restrict xobj,\n                  AkUIntArray ** __restrict array);\n\nAK_EXPORT\nchar *\nxmla_strdup(const xml_attr_t * __restrict attr,\n            AkHeap           * __restrict heap,\n            void             * __restrict parent);\n\nAK_EXPORT\nchar *\nxmla_strdup_by(const xml_t * __restrict xobject,\n               AkHeap      * __restrict heap,\n               const char  * __restrict name,\n               void        * __restrict parent);\n\nAK_EXPORT\nvoid\nxmla_setid(const xml_t * __restrict xobject,\n           AkHeap      * __restrict heap,\n           void        * __restrict memptr);\n\n#endif /* ak_src_xml_h */\n"
  },
  {
    "path": "test/include/common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef tests_common_h\n#define tests_common_h\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n\ntypedef struct test_status_t {\n  const char *msg;\n  int         status;\n} test_status_t;\n\ntypedef test_status_t (*fntest)(void);\n\ntypedef struct test_entry_t {\n  char  *name;\n  fntest entry;\n  int    ret;\n  int    show_output;\n} test_entry_t;\n\n#define RESET       \"\\033[0m\"\n#define BLACK       \"\\033[30m\"             /* Black */\n#define RED         \"\\033[31m\"             /* Red */\n#define GREEN       \"\\033[32m\"             /* Green */\n#define YELLOW      \"\\033[33m\"             /* Yellow */\n#define BLUE        \"\\033[34m\"             /* Blue */\n#define MAGENTA     \"\\033[35m\"             /* Magenta */\n#define CYAN        \"\\033[36m\"             /* Cyan */\n#define WHITE       \"\\033[37m\"             /* White */\n#define BOLDBLACK   \"\\033[1m\\033[30m\"      /* Bold Black */\n#define BOLDRED     \"\\033[1m\\033[31m\"      /* Bold Red */\n#define BOLDGREEN   \"\\033[1m\\033[32m\"      /* Bold Green */\n#define BOLDYELLOW  \"\\033[1m\\033[33m\"      /* Bold Yellow */\n#define BOLDBLUE    \"\\033[1m\\033[34m\"      /* Bold Blue */\n#define BOLDMAGENTA \"\\033[1m\\033[35m\"      /* Bold Magenta */\n#define BOLDCYAN    \"\\033[1m\\033[36m\"      /* Bold Cyan */\n#define BOLDWHITE   \"\\033[1m\\033[37m\"      /* Bold White */\n\n#define TEST_DECLARE(FUN) test_status_t test_ ## FUN(void);\n#define TEST_ENTRY(FUN)   { #FUN, test_ ## FUN, 0, 0 },\n#define TEST_LIST         static test_entry_t tests[] = \n\n/* __VA_ARGS__ workaround for MSVC: https://stackoverflow.com/a/5134656 */\n#define EXPAND(x) x\n\n#define TEST_OK 1\n#define TEST_SUCCESS  return (test_status_t){NULL, TEST_OK};\n\n#define TEST_IMPL_ARG1(FUN) \\\n  test_status_t test_ ## FUN (void);                                          \\\n  test_status_t test_ ## FUN()\n\n#define TEST_IMPL_ARG2(PREFIX, FUN) TEST_IMPL_ARG1(PREFIX ## FUN)\n#define TEST_IMPL_ARG3(arg1, arg2, arg3, ...) arg3\n\n#define TEST_IMPL_CHOOSER(...)                                                \\\n  EXPAND(TEST_IMPL_ARG3(__VA_ARGS__, TEST_IMPL_ARG2, TEST_IMPL_ARG1))\n\n#define TEST_IMPL(...) EXPAND(TEST_IMPL_CHOOSER(__VA_ARGS__)(__VA_ARGS__))\n\n#define ASSERT_EXT(expr, msg)                                                 \\\n  if (!(expr)) {                                                              \\\n    fprintf(stderr,                                                           \\\n            RED \"  assert fail\" RESET                                         \\\n            \" in \" BOLDCYAN \"%s \" RESET                                       \\\n            \"on \" BOLDMAGENTA \"line %d\" RESET                                 \\\n            \" : \" BOLDWHITE \" ASSERT(%s)\\n\" RESET,                            \\\n            __FILE__,                                                         \\\n            __LINE__,                                                         \\\n            #expr);                                                           \\\n    return (test_status_t){msg, 0};                                           \\\n  }\n\n#define ASSERT_ARG1(expr)                  ASSERT_EXT(expr, NULL)\n#define ASSERT_ARG2(expr, msg)             ASSERT_EXT(expr, msg)\n#define ASSERT_ARG3(arg1, arg2, arg3, ...) arg3\n\n#define ASSERT_CHOOSER(...) ASSERT_ARG3(__VA_ARGS__, ASSERT_ARG2, ASSERT_ARG1)\n#define ASSERT(...) do { ASSERT_CHOOSER(__VA_ARGS__)(__VA_ARGS__) } while(0);\n#define ASSERTIFY(expr) do {                                                  \\\n    test_status_t ts; \\\n    ts = expr; \\\n    if (ts.status != TEST_OK) {                                               \\\n      fprintf(stderr,                                                         \\\n              RED \"  assert fail\" RESET                                       \\\n              \" in \" BOLDCYAN \"%s \" RESET                                     \\\n              \"on \" BOLDMAGENTA \"line %d\" RESET                               \\\n              \" : \" BOLDWHITE \" ASSERTIFY(%s)\\n\" RESET,                       \\\n              __FILE__,                                                       \\\n              __LINE__,                                                       \\\n              #expr);                                                         \\\n      return (test_status_t){ts.msg, 0};                                      \\\n    } \\\n  } while(0);\n\n#if defined(_WIN32)\n# define drand48()  ((float)(rand() / (RAND_MAX + 1.0)))\n# define OK_TEXT    \"ok:\"\n# define FAIL_TEXT  \"fail:\"\n# define FINAL_TEXT \"^_^\"\n#else\n# define OK_TEXT    \"✔︎\"\n# define FAIL_TEXT  \"𐄂\"\n# define FINAL_TEXT \"🎉\"\n#endif\n\n#endif /* common_h */\n"
  },
  {
    "path": "test/runner.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"include/common.h\"\n#include \"tests.h\"\n\n#include <stdlib.h>\n#include <time.h>\n#include <string.h>\n\n#define TARGET_NAME \"AssetKit\"\n\nint\nmain(int argc, const char * argv[]) {\n  test_entry_t *entry;\n  test_status_t st;\n  int32_t       i, count, passed, failed, maxlen;\n  double        start, end, elapsed, total;\n\n  passed = failed = maxlen  = 0;\n  total  = 0.0;\n  count  = sizeof(tests) / sizeof(tests[0]);\n\n  fprintf(stderr, CYAN \"\\nWelcome to %s tests\\n\\n\" RESET, TARGET_NAME);\n\n  for (i = 0; i < count; i++) {\n    int32_t len;\n\n    entry = tests + i;\n    len   = (int32_t)strlen(entry->name);\n\n    if (len > maxlen)\n      maxlen = len;\n  }\n\n  maxlen += 5;\n\n  fprintf(stderr,\n          BOLDWHITE  \"  %-*s    %-*s\\n\",\n          maxlen, \"Test Name\", maxlen, \"Elapsed Time\");\n\n  for (i = 0; i < count; i++) {\n    entry   = tests + i;\n    start   = clock();\n    st      = entry->entry();\n    end     = clock();\n    elapsed = (end - start) / CLOCKS_PER_SEC;\n    total  += elapsed;\n\n    if (!st.status) {\n      fprintf(stderr,\n              BOLDRED  \"  \" FAIL_TEXT BOLDWHITE \" %s \" RESET, entry->name);\n      if (st.msg) {\n        fprintf(stderr,\n                YELLOW \"- %s\" RESET,\n                st.msg);\n      }\n\n      fprintf(stderr, \"\\n\");\n\n      failed++;\n    } else {\n      fprintf(stderr, GREEN  \"  \" OK_TEXT RESET \" %-*s  \", maxlen, entry->name);\n\n      if (elapsed > 0.01)\n        fprintf(stderr, YELLOW \"%.2fs\", elapsed);\n      else\n        fprintf(stderr, \"0\");\n\n      fprintf(stderr, \"\\n\" RESET);\n      passed++;\n    }\n  }\n\n  if (failed == 0) {\n    fprintf(stderr,\n            BOLDGREEN \"\\n  All tests are passed \" FINAL_TEXT \"\\n\" RESET);\n  }\n\n  fprintf(stderr,\n          CYAN \"\\n%s test results (%0.2fs):\\n\" RESET\n          \"--------------------------\\n\"\n\n          MAGENTA \"%d\" RESET \" tests are runned, \"\n          GREEN   \"%d\" RESET \" %s passed, \"\n          RED     \"%d\" RESET \" %s failed\\n\\n\" RESET,\n          TARGET_NAME,\n          total,\n          count,\n          passed,\n          passed > 1 ? \"are\" : \"is\",\n          failed,\n          failed > 1 ? \"are\" : \"is\");\n\n  return failed;\n}\n"
  },
  {
    "path": "test/src/collada/test_dae_load.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../test_common.h\"\n\nconst char *dae_dir = \"./test/sample-models/collada/files\";\n\nTEST_IMPL(dae_load_folder) {\n  DIR           *dir;\n  struct dirent *ent;\n  AkDoc         *doc;\n\n  chdir(dae_dir);\n\n  if ((dir = opendir (\"./\")) != NULL) {\n    while ((ent = readdir (dir)) != NULL) {\n\t  size_t namelen;\n\n\t  namelen = strlen(ent->d_name);\n\n      if (*ent->d_name == '.') {\n        if (namelen == 1\n            || (namelen == 2 && ent->d_name[1] == '.')\n            || strcmp(ent->d_name, \".DS_Store\") == 0)\n          continue;\n      }\n\n      ASSERT(ak_load(&doc, ent->d_name, NULL) == AK_OK);\n      ak_free(doc);\n    }\n    \n    closedir(dir);\n  }\n\n  TEST_SUCCESS\n}\n"
  },
  {
    "path": "test/src/test_common.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ak_test_common_h\n#define ak_test_common_h\n\n#include \"../include/common.h\"\n\n#include <stdlib.h>\n#include <stdio.h>\n#include <dirent.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <string.h>\n#include <stdarg.h>\n#include <stdint.h>\n#include <stddef.h>\n#include <setjmp.h>\n\n#include <ak/assetkit.h>\n\n#endif /* ak_test_common_h */\n"
  },
  {
    "path": "test/src/test_memory.c",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"test_common.h\"\n#include \"../../src/mem_common.h\"\n#include \"../../src/mem_lt.h\"\n\nextern AkHeapAllocator ak__allocator;\n\nTEST_IMPL(heap) {\n  AkHeap  *heap, *other, staticHeap;\n  uint32_t heapid, data;\n\n  heap = ak_heap_new(NULL, NULL, NULL);\n  ASSERT(heap->allocator == &ak__allocator);\n  ASSERT(ak_heap_allocator(heap) == &ak__allocator);\n\n  heapid = heap->heapid;\n  ASSERT(heapid > 0);\n  ASSERT(ak_heap_lt_find(heap->heapid) == heap);\n\n  other = ak_heap_new(NULL, NULL, NULL);\n\n  ak_heap_attach(heap, other);\n  ASSERT(heap->chld == other);\n\n  ak_heap_dettach(heap, other);\n  ASSERT(heap->chld == NULL);\n\n  ak_heap_attach(heap, other);\n  ASSERT(heap->chld == other);\n\n  ak_heap_setdata(heap, &data);\n  ASSERT(ak_heap_data(heap) == &data);\n\n  ak_heap_destroy(heap);\n  ASSERT(ak_heap_lt_find(heapid) == NULL);\n\n  ak_heap_init(&staticHeap, NULL, NULL, NULL);\n  ASSERT(staticHeap.heapid > 0);\n\n  ak_heap_lt_remove(staticHeap.heapid);\n  ASSERT(ak_heap_lt_find(staticHeap.heapid) == NULL);\n\n  TEST_SUCCESS\n}\n\nTEST_IMPL(heap_multiple) {\n  AkHeap  *heap, *root;\n  uint32_t i;\n\n  root = ak_heap_new(NULL, NULL, NULL);\n\n  /* multiple alloc, leak */\n  for (i = 0; i < 1000; i++)\n    heap = ak_heap_new(NULL, NULL, NULL);\n\n  /* multiple alloc 2, leak */\n  for (i = 0; i < 1000; i++)\n    heap = ak_heap_new(NULL, NULL, NULL);\n\n  /* multiple alloc-free 1 */\n  for (i = 0; i < 1000; i++) {\n    heap = ak_heap_new(NULL, NULL, NULL);\n    ak_heap_destroy(heap);\n  }\n\n  /* multiple alloc-free 2 */\n  for (i = 0; i < 1000; i++) {\n    heap = ak_heap_new(NULL, NULL, NULL);\n    ak_heap_destroy(heap);\n  }\n\n  /* multiple alloc, attach to parent */\n  for (i = 0; i < 1000; i++) {\n    heap = ak_heap_new(NULL, NULL, NULL);\n    ak_heap_attach(root, heap);\n  }\n\n  /* multiple alloc, attach to parent */\n  for (i = 0; i < 1000; i++) {\n    heap = ak_heap_new(NULL, NULL, NULL);\n    ak_heap_attach(root, heap);\n  }\n\n  ak_heap_destroy(root);\n\n  root = ak_heap_new(NULL, NULL, NULL);\n\n  /* multiple alloc, attach-detach to parent */\n  for (i = 0; i < 1000; i++) {\n    heap = ak_heap_new(NULL, NULL, NULL);\n    ak_heap_attach(root, heap);\n    ak_heap_dettach(root, heap);\n  }\n\n  TEST_SUCCESS\n}\n"
  },
  {
    "path": "test/tests.h",
    "content": "/*\n * Copyright (C) 2020 Recep Aslantas\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef tests_h\n#define tests_h\n\n#include \"include/common.h\"\n\n/*\n * To register a test:\n *   1. use TEST_DECLARE() to forward declare test\n *   2. use TEST_ENTRY() to add test to list\n */\n\nTEST_DECLARE(heap)\nTEST_DECLARE(heap_multiple)\nTEST_DECLARE(dae_load_folder)\n\n/*****************************************************************************/\n\nTEST_LIST {\n  TEST_ENTRY(heap)\n  TEST_ENTRY(heap_multiple)\n  TEST_ENTRY(dae_load_folder)\n};\n\n#endif /* tests_h */\n"
  }
]