Merge "Set focus to web engine." into devel/master
authorJIYUN YANG <ji.yang@samsung.com>
Wed, 6 Jan 2021 03:42:32 +0000 (03:42 +0000)
committerGerrit Code Review <gerrit@review>
Wed, 6 Jan 2021 03:42:32 +0000 (03:42 +0000)
267 files changed:
README.md
automated-tests/patch-coverage.pl
automated-tests/resources/2CylinderEngine.gltf [new file with mode: 0644]
automated-tests/resources/2CylinderEngine0.bin [new file with mode: 0644]
automated-tests/resources/AnimatedMorphCube.bin [new file with mode: 0644]
automated-tests/resources/AnimatedMorphCube.gltf [new file with mode: 0644]
automated-tests/resources/AnimatedMorphSphere.bin [new file with mode: 0644]
automated-tests/resources/AnimatedMorphSphere.gltf [new file with mode: 0644]
automated-tests/resources/AnimatedTriangle.gltf [new file with mode: 0644]
automated-tests/resources/BoxAnimated.gltf [new file with mode: 0644]
automated-tests/resources/BoxAnimated0.bin [new file with mode: 0644]
automated-tests/resources/CesiumMan.gltf [new file with mode: 0644]
automated-tests/resources/CesiumMan_data.bin [new file with mode: 0644]
automated-tests/resources/CesiumMilkTruck.gltf [new file with mode: 0644]
automated-tests/resources/CesiumMilkTruck_data.bin [new file with mode: 0644]
automated-tests/resources/EnvironmentTest.gltf [new file with mode: 0644]
automated-tests/resources/EnvironmentTest_binary.bin [new file with mode: 0644]
automated-tests/resources/MetalRoughSpheres.gltf [new file with mode: 0644]
automated-tests/resources/MetalRoughSpheres0.bin [new file with mode: 0644]
automated-tests/resources/MorphPrimitivesTest.bin [new file with mode: 0644]
automated-tests/resources/MorphPrimitivesTest.gltf [new file with mode: 0644]
automated-tests/resources/RGB16F.ktx [new file with mode: 0644]
automated-tests/resources/RGB32F.ktx [new file with mode: 0644]
automated-tests/resources/RGBA8888.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_10x10.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_10x5.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_10x6.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_12x10.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_12x12.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_4x4.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_5x4.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_5x5.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_6x5.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_6x6.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_8x5.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_8x6.ktx [new file with mode: 0644]
automated-tests/resources/RGBA_ASTC_8x8.ktx [new file with mode: 0644]
automated-tests/resources/SimpleSparseAccessor.bin [new file with mode: 0644]
automated-tests/resources/SimpleSparseAccessor.gltf [new file with mode: 0644]
automated-tests/resources/Studio/Irradiance.ktx [new file with mode: 0644]
automated-tests/resources/Studio/Radiance.ktx [new file with mode: 0644]
automated-tests/resources/animation.bin [new file with mode: 0644]
automated-tests/resources/arc.dli [new file with mode: 0644]
automated-tests/resources/dli/animation-failed-to-open.dli [new file with mode: 0644]
automated-tests/resources/dli/constraints.dli [new file with mode: 0644]
automated-tests/resources/dli/extras.dli [new file with mode: 0644]
automated-tests/resources/dli/material-environment-out-of-bounds.dli [new file with mode: 0644]
automated-tests/resources/dli/mesh-indices-read-fail.dli [new file with mode: 0644]
automated-tests/resources/dli/mesh-positions-read-fail.dli [new file with mode: 0644]
automated-tests/resources/dli/mesh-uri-missing.dli [new file with mode: 0644]
automated-tests/resources/dli/node-animated-image-mesh-missing.dli [new file with mode: 0644]
automated-tests/resources/dli/node-arc-mesh-missing.dli [new file with mode: 0644]
automated-tests/resources/dli/node-child-invalid-type.dli [new file with mode: 0644]
automated-tests/resources/dli/node-model-mesh-missing.dli [new file with mode: 0644]
automated-tests/resources/dli/node-name-already-used.dli [new file with mode: 0644]
automated-tests/resources/dli/node-processor.dli [new file with mode: 0644]
automated-tests/resources/dli/node-renderable-mesh-invalid-type.dli [new file with mode: 0644]
automated-tests/resources/dli/node-renderable-mesh-out-of-bounds.dli [new file with mode: 0644]
automated-tests/resources/dli/nodes-array-empty.dli [new file with mode: 0644]
automated-tests/resources/dli/nodes-invalid-type.dli [new file with mode: 0644]
automated-tests/resources/dli/nodes-missing.dli [new file with mode: 0644]
automated-tests/resources/dli/root-id-invalid.dli [new file with mode: 0644]
automated-tests/resources/dli/root-id-out-of-bounds.dli [new file with mode: 0644]
automated-tests/resources/dli/root-node-invalid-type.dli [new file with mode: 0644]
automated-tests/resources/dli/scene-out-of-bounds.dli [new file with mode: 0644]
automated-tests/resources/dli/scenes-missing.dli [new file with mode: 0644]
automated-tests/resources/dli/scenes-nodes-missing.dli [new file with mode: 0644]
automated-tests/resources/dli/shader-fragment-missing.dli [new file with mode: 0644]
automated-tests/resources/dli/shader-uniforms.dli [new file with mode: 0644]
automated-tests/resources/dli/shader-vertex-missing.dli [new file with mode: 0644]
automated-tests/resources/dli/skeleton-node-missing.dli [new file with mode: 0644]
automated-tests/resources/dli/skeleton-root-not-found.dli [new file with mode: 0644]
automated-tests/resources/dli_arc.fsh [new file with mode: 0644]
automated-tests/resources/dli_arc.vsh [new file with mode: 0644]
automated-tests/resources/dli_images.fsh [new file with mode: 0644]
automated-tests/resources/dli_images.vsh [new file with mode: 0644]
automated-tests/resources/dli_pbr.fsh [new file with mode: 0644]
automated-tests/resources/dli_pbr.vsh [new file with mode: 0644]
automated-tests/resources/exercise.dli [new file with mode: 0644]
automated-tests/resources/exercise/Icons/Icon_Idle.png [new file with mode: 0644]
automated-tests/resources/exercise/Icons/Icon_JJ.png [new file with mode: 0644]
automated-tests/resources/exercise/Icons/Icon_Lunge.png [new file with mode: 0644]
automated-tests/resources/exercise/Icons/Icon_Squat.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/BG_AlbedoMetallic.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/BG_NormalRoughness.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/Body_AlbedoMetallic.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/Body_Female_Asian_Adult_SubsurfaceColor.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/Body_NormalRoughness.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/Eye_AlbedoMetallic.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/Eye_NormalRoughness.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/FitBot_AlbedoMetallic.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/FitBot_NormalRoughness.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/FitTop_AlbedoMetallic.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/FitTop_NormalRoughness.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/Hair_AlbedoMetallic.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/Hair_NormalRoughness.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/Head_AlbedoMetallic.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/Head_Female_SubsurfaceColor.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/Head_NormalRoughness.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/Shoe_AlbedoMetallic.png [new file with mode: 0644]
automated-tests/resources/exercise/Textures/Shoe_NormalRoughness.png [new file with mode: 0644]
automated-tests/resources/exercise/idle-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/idle-to-jumping-jack-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/idle-to-lunge-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/idle-to-squat-animation-0.ani [new file with mode: 0644]
automated-tests/resources/exercise/idle-to-squat-animation-1.ani [new file with mode: 0644]
automated-tests/resources/exercise/jumping-jack-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/jumping-jack-to-idle-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/jumping-jack-to-lunge-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/jumping-jack-to-squat-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/lunge-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/lunge-to-idle-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/lunge-to-jumping-jack-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/lunge-to-squat-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/model.dae.bin [new file with mode: 0644]
automated-tests/resources/exercise/squat-animation-0.ani [new file with mode: 0644]
automated-tests/resources/exercise/squat-animation-1.ani [new file with mode: 0644]
automated-tests/resources/exercise/squat-to-idle-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/squat-to-jumping-jack-animation.ani [new file with mode: 0644]
automated-tests/resources/exercise/squat-to-lunge-animation.ani [new file with mode: 0644]
automated-tests/resources/forest_irradiance.ktx [new file with mode: 0644]
automated-tests/resources/forest_radiance.ktx [new file with mode: 0644]
automated-tests/resources/invalid.gltf [new file with mode: 0644]
automated-tests/resources/morph.dli [new file with mode: 0644]
automated-tests/resources/morph/HeadTest_002.dae.bin [new file with mode: 0644]
automated-tests/resources/morph/cube.gltf.bin [new file with mode: 0644]
automated-tests/resources/simpleTriangle.bin [new file with mode: 0644]
automated-tests/resources/truncated.ktx [new file with mode: 0644]
automated-tests/src/dali-scene-loader-internal/CMakeLists.txt [new file with mode: 0755]
automated-tests/src/dali-scene-loader-internal/tct-dali-scene-loader-internal-core.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader-internal/utc-Dali-Gltf2Asset.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader-internal/utc-Dali-Hash.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader-internal/utc-Dali-JsonReader.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader-internal/utc-Dali-JsonUtil.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/CMakeLists.txt [new file with mode: 0755]
automated-tests/src/dali-scene-loader/tct-dali-scene-loader-core.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-AlphaFunctionHelper.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-AnimatedProperty.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-AnimationDefinition.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-CameraParameters.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-DliLoader.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-EnvironmentDefinition.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-Gltf2Loader.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-KtxLoader.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-MatrixStack.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-NodeDefinition.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-RendererState.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-ResourceBundle.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-SceneDefinition.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-ShaderDefinition.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-ShaderDefinitionFactory.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-StringCallback.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-Utils.cpp [new file with mode: 0644]
automated-tests/src/dali-scene-loader/utc-Dali-ViewProjection.cpp [new file with mode: 0644]
automated-tests/src/dali-toolkit-internal/addons/test-rendering-addon.cpp
automated-tests/src/dali-toolkit/utc-Dali-AnimatedImageVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-ArcVisual.cpp
automated-tests/src/dali-toolkit/utc-Dali-Visual.cpp
automated-tests/src/dali-toolkit/utc-Dali-VisualFactory.cpp
build/tizen/CMakeLists.txt
build/tizen/dali-scene-loader/CMakeLists.txt [new file with mode: 0644]
build/tizen/dali-scene-loader/build.sh [new file with mode: 0755]
build/tizen/dali-scene-loader/dali2-scene-loader.pc.in [new file with mode: 0644]
build/tizen/docs/dali.doxy.in
dali-scene-loader.manifest [new file with mode: 0644]
dali-scene-loader.manifest-smack [new file with mode: 0644]
dali-scene-loader/README.md [new file with mode: 0644]
dali-scene-loader/internal/file.list [new file with mode: 0644]
dali-scene-loader/internal/gltf2-asset.cpp [new file with mode: 0644]
dali-scene-loader/internal/gltf2-asset.h [new file with mode: 0644]
dali-scene-loader/internal/hash.cpp [new file with mode: 0644]
dali-scene-loader/internal/hash.h [new file with mode: 0644]
dali-scene-loader/internal/json-reader.cpp [new file with mode: 0644]
dali-scene-loader/internal/json-reader.h [new file with mode: 0644]
dali-scene-loader/internal/json-util.cpp [new file with mode: 0644]
dali-scene-loader/internal/json-util.h [new file with mode: 0644]
dali-scene-loader/public-api/alpha-function-helper.cpp [new file with mode: 0644]
dali-scene-loader/public-api/alpha-function-helper.h [new file with mode: 0644]
dali-scene-loader/public-api/animated-property.cpp [new file with mode: 0644]
dali-scene-loader/public-api/animated-property.h [new file with mode: 0644]
dali-scene-loader/public-api/animation-definition.cpp [new file with mode: 0644]
dali-scene-loader/public-api/animation-definition.h [new file with mode: 0644]
dali-scene-loader/public-api/api.h [new file with mode: 0644]
dali-scene-loader/public-api/blend-shape-details.cpp [new file with mode: 0644]
dali-scene-loader/public-api/blend-shape-details.h [new file with mode: 0644]
dali-scene-loader/public-api/camera-parameters.cpp [new file with mode: 0644]
dali-scene-loader/public-api/camera-parameters.h [new file with mode: 0644]
dali-scene-loader/public-api/customization.cpp [new file with mode: 0644]
dali-scene-loader/public-api/customization.h [new file with mode: 0644]
dali-scene-loader/public-api/dli-loader.cpp [new file with mode: 0644]
dali-scene-loader/public-api/dli-loader.h [new file with mode: 0644]
dali-scene-loader/public-api/environment-definition.cpp [new file with mode: 0644]
dali-scene-loader/public-api/environment-definition.h [new file with mode: 0644]
dali-scene-loader/public-api/file.list [new file with mode: 0644]
dali-scene-loader/public-api/gltf2-loader.cpp [new file with mode: 0644]
dali-scene-loader/public-api/gltf2-loader.h [new file with mode: 0644]
dali-scene-loader/public-api/index.h [new file with mode: 0644]
dali-scene-loader/public-api/ktx-loader.cpp [new file with mode: 0644]
dali-scene-loader/public-api/ktx-loader.h [new file with mode: 0644]
dali-scene-loader/public-api/light-parameters.h [new file with mode: 0644]
dali-scene-loader/public-api/load-result.h [new file with mode: 0644]
dali-scene-loader/public-api/material-definition.cpp [new file with mode: 0644]
dali-scene-loader/public-api/material-definition.h [new file with mode: 0644]
dali-scene-loader/public-api/matrix-stack.cpp [new file with mode: 0644]
dali-scene-loader/public-api/matrix-stack.h [new file with mode: 0644]
dali-scene-loader/public-api/mesh-definition.cpp [new file with mode: 0644]
dali-scene-loader/public-api/mesh-definition.h [new file with mode: 0644]
dali-scene-loader/public-api/mesh-geometry.h [new file with mode: 0644]
dali-scene-loader/public-api/node-definition.cpp [new file with mode: 0644]
dali-scene-loader/public-api/node-definition.h [new file with mode: 0644]
dali-scene-loader/public-api/parse-renderer-state.cpp [new file with mode: 0644]
dali-scene-loader/public-api/parse-renderer-state.h [new file with mode: 0644]
dali-scene-loader/public-api/renderer-state.cpp [new file with mode: 0644]
dali-scene-loader/public-api/renderer-state.h [new file with mode: 0644]
dali-scene-loader/public-api/resource-bundle.cpp [new file with mode: 0644]
dali-scene-loader/public-api/resource-bundle.h [new file with mode: 0644]
dali-scene-loader/public-api/scene-definition.cpp [new file with mode: 0644]
dali-scene-loader/public-api/scene-definition.h [new file with mode: 0644]
dali-scene-loader/public-api/shader-definition-factory.cpp [new file with mode: 0644]
dali-scene-loader/public-api/shader-definition-factory.h [new file with mode: 0644]
dali-scene-loader/public-api/shader-definition.cpp [new file with mode: 0644]
dali-scene-loader/public-api/shader-definition.h [new file with mode: 0644]
dali-scene-loader/public-api/skeleton-definition.h [new file with mode: 0644]
dali-scene-loader/public-api/skinning-details.cpp [new file with mode: 0644]
dali-scene-loader/public-api/skinning-details.h [new file with mode: 0644]
dali-scene-loader/public-api/string-callback.cpp [new file with mode: 0644]
dali-scene-loader/public-api/string-callback.h [new file with mode: 0644]
dali-scene-loader/public-api/utils.cpp [new file with mode: 0644]
dali-scene-loader/public-api/utils.h [new file with mode: 0644]
dali-scene-loader/public-api/view-projection.cpp [new file with mode: 0644]
dali-scene-loader/public-api/view-projection.h [new file with mode: 0644]
dali-scene-loader/third-party/json.h [new file with mode: 0644]
dali-toolkit/devel-api/builder/tree-node.cpp
dali-toolkit/devel-api/builder/tree-node.h
dali-toolkit/devel-api/visuals/arc-visual-properties-devel.h
dali-toolkit/devel-api/visuals/image-visual-properties-devel.h
dali-toolkit/internal/builder/tree-node-manipulator.cpp
dali-toolkit/internal/builder/tree-node-manipulator.h
dali-toolkit/internal/controls/control/control-data-impl.cpp
dali-toolkit/internal/controls/text-controls/text-editor-impl.cpp
dali-toolkit/internal/controls/text-controls/text-field-impl.cpp
dali-toolkit/internal/file.list
dali-toolkit/internal/visuals/animated-image/animated-image-visual.cpp
dali-toolkit/internal/visuals/animated-image/fixed-image-cache.cpp
dali-toolkit/internal/visuals/animated-image/fixed-image-cache.h
dali-toolkit/internal/visuals/animated-image/image-cache.h
dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.cpp
dali-toolkit/internal/visuals/animated-image/rolling-animated-image-cache.h
dali-toolkit/internal/visuals/animated-image/rolling-image-cache.cpp
dali-toolkit/internal/visuals/animated-image/rolling-image-cache.h
dali-toolkit/internal/visuals/arc/arc-visual.cpp
dali-toolkit/internal/visuals/arc/arc-visual.h
dali-toolkit/internal/visuals/image/image-visual.cpp
dali-toolkit/internal/visuals/npatch-data.cpp [new file with mode: 0644]
dali-toolkit/internal/visuals/npatch-data.h [new file with mode: 0644]
dali-toolkit/internal/visuals/npatch-loader.cpp
dali-toolkit/internal/visuals/npatch-loader.h
dali-toolkit/internal/visuals/npatch/npatch-visual.cpp
dali-toolkit/internal/visuals/npatch/npatch-visual.h
dali-toolkit/internal/visuals/texture-manager-impl.cpp
dali-toolkit/internal/visuals/texture-manager-impl.h
dali-toolkit/internal/visuals/visual-base-data-impl.cpp
dali-toolkit/internal/visuals/visual-base-data-impl.h
dali-toolkit/internal/visuals/visual-base-impl.cpp
dali-toolkit/public-api/dali-toolkit-version.cpp
dali-toolkit/public-api/visuals/visual-properties.h
packaging/dali-toolkit.spec

index ff51191..dd18745 100644 (file)
--- a/README.md
+++ b/README.md
@@ -14,6 +14,7 @@
       * [3. Building for MS Windows](#3-building-for-ms-windows)
          * Build with the Visual Studio project.
          * Build with CMake.
+   * [DALi Scene Loader](#dali-scene-loader)
 
 # Build Instructions
 
@@ -109,3 +110,7 @@ vcpkg-script folder in the windows-dependencies repository.
     - INSTALL_CMAKE_MODULES    ---> Whether to install the CMake modules (Used by the CMake command find_package() to find previously installed libraries).
     - ENABLE_DEBUG             ---> Whether to build with debug enabled.
     - USE_DEFAULT_RESOURCE_DIR ---> Whether to use the default resource folders. Otherwise set environment variables for DALI_IMAGE_DIR, DALI_SOUND_DIR, DALI_STYLE_DIR, DALI_STYLE_IMAGE_DIR and DALI_DATA_READ_ONLY_DIR
+
+# DALi Scene Loader
+
+For information about the DALi Scene Loader library, refer to dali-scene-loader/README.md.
\ No newline at end of file
index b3cf098..1f836a4 100755 (executable)
@@ -83,7 +83,7 @@ my %options = (
     "output:s"     => { "optvar"=>\$opt_output, "desc"=>"Generate html output"},
     "help"         => { "optvar"=>\$opt_help, "desc"=>""},
     "quiet"        => { "optvar"=>\$opt_quiet, "desc"=>""},
-    "verbose"      => { "optvar"=>\$opt_verbose, "desc"=>"" });
+    "verbose"      => { "optvar"=>\$opt_verbose, "desc"=>"Also output coverage" });
 
 my %longOptions = map { $_ => $options{$_}->{"optvar"} } keys(%options);
 GetOptions( %longOptions ) or pod2usage(2);
@@ -875,15 +875,14 @@ sub info(@)
     }
 }
 
-# NEW STUFF
 
-## Format per file, repeated, no linebreak
+# Format per file, repeated, no linebreak
 # <diffcmd>
 # index c1..c2 c3
 # --- a/<left-hand-side-file>
 # +++ b/<right-hand-side-file>
 # <diff hunks>
-
+#
 # Format of each diff hunk, repeated, no linebreak
 # @@ <ranges> @@ line
 # 3 lines of context
@@ -986,7 +985,7 @@ sub parse_diff
     $files{$file}->{"patch"} = [@checklines];
     $files{$file}->{"b_lines"} = {%b_lines};
 
-    my %filter = map { $_ => $files{$_} } grep {m!^dali(-toolkit)?/!} (keys(%files));;
+    my %filter = map { $_ => $files{$_} } grep {m!^dali(-toolkit|-scene-loader)?/!} (keys(%files));
 
     if($pd_debug)
     {
@@ -1064,6 +1063,13 @@ sub calc_patch_coverage_percentage
         my $abs_filename = File::Spec->rel2abs($file, $root);
         my $sumcountref = $info_data{$abs_filename}->{"sum"};
 
+        if($debug>1)
+        {
+            print("File:  $abs_filename\n");
+            print Dumper($info_data{$abs_filename});
+            print "\n";
+        }
+
         if( $sumcountref )
         {
             for my $patch (@$patchref)
@@ -1312,27 +1318,6 @@ EOH
 ##                                    MAIN                                    ##
 ################################################################################
 
-my $cwd = getcwd(); # expect this to be automated-tests folder
-
-# execute coverage.sh, generating build/tizen/dali.info from lib, and
-# *.dir/dali.info. Don't generate html
-print `./coverage.sh -n`;
-chdir "..";
-$root = getcwd();
-
-our %info_data; # Hash of all data from .info files
-my @info_files = split(/\n/, `find . -name dali.info`);
-my %new_info;
-
-# Read in all specified .info files
-foreach (@info_files)
-{
-    %new_info = %{read_info_file($_)};
-
-    # Combine %new_info with %info_data
-    %info_data = %{combine_info_files(\%info_data, \%new_info)};
-}
-
 
 # Generate git diff command
 my @cmd=('--no-pager','diff','--no-ext-diff','-U0','--no-color');
@@ -1362,7 +1347,7 @@ else
         }
         else
         {
-            die "Both cached & working files - cannot get correct patch from git\n";
+            die "Error: Both cached & working files - cannot get correct patch from git\nRun git add first.";
             # Would have to diff from separate clone.
         }
     }
@@ -1370,6 +1355,31 @@ else
 
 push @cmd, @ARGV;
 
+# Before executing the diff, run the coverage.sh script. This is done here so that the
+# error condition above happens straight away, rather than after spewing out lots of information.
+
+my $cwd = getcwd(); # expect this to be automated-tests folder
+# execute coverage.sh, generating build/tizen/dali.info from lib, and
+# *.dir/dali.info. Don't generate html
+printf("Running coverage.sh\n");
+my $coverage_output=`./coverage.sh -n`;
+chdir "..";
+$root = getcwd();
+
+our %info_data; # Hash of all data from .info files
+my @info_files = split(/\n/, `find . -name dali.info`);
+my %new_info;
+
+# Read in all specified .info files
+foreach (@info_files)
+{
+    %new_info = %{read_info_file($_)};
+
+    # Combine %new_info with %info_data
+    %info_data = %{combine_info_files(\%info_data, \%new_info)};
+}
+
+
 # Execute diff & coverage from root directory
 my $filesref = run_diff(@cmd);
 
@@ -1386,21 +1396,33 @@ foreach my $file (keys(%$filesref))
 }
 if( $filecount == 0 )
 {
-    print "No source files found\n";
+    print "Warning: No source files found\n";
     exit 0;    # Exit with no error.
 }
 
 #print_simplified_info() if $debug;
 #exit 0;
+if($debug > 1)
+{
+    print "Info keys:\n";
+    for my $key (keys(%info_data))
+    {
+        print "$key\n";
+    }
+    print "\n\n";
+}
 
 my $percentref = calc_patch_coverage_percentage($filesref);
 if($percentref->[0] == 0)
 {
-    print "No coverable lines found\n";
+    print "Warning: No coverable lines found\n";
     exit 0;
 }
 my $percent = $percentref->[1];
 
+printf(join("\n", grep { $_ !~ /^Remov/ } split(/\n/,$coverage_output))) if $opt_verbose;
+#printf($coverage_output) if $opt_verbose;
+
 my $color=BOLD RED;
 if($opt_output)
 {
@@ -1417,6 +1439,7 @@ elsif( ! $opt_quiet )
     print RESET;
 }
 
+printf("\n\n=========================\nPatch coverage output:\n=========================\n");
 printf("Line Coverage: %d/%d\n", $percentref->[2], $percentref->[0]);
 printf("Percentage of change covered: %5.2f%\n", $percent);
 
diff --git a/automated-tests/resources/2CylinderEngine.gltf b/automated-tests/resources/2CylinderEngine.gltf
new file mode 100644 (file)
index 0000000..c4caf78
--- /dev/null
@@ -0,0 +1,4390 @@
+{\r
+    "asset": {\r
+        "generator": "COLLADA2GLTF",\r
+        "version": "2.0"\r
+    },\r
+    "scene": 0,\r
+    "scenes": [\r
+        {\r
+            "nodes": [\r
+                81,\r
+                0\r
+            ]\r
+        }\r
+    ],\r
+    "nodes": [\r
+        {\r
+            "children": [\r
+                80,\r
+                79,\r
+                78,\r
+                77,\r
+                76,\r
+                75,\r
+                74,\r
+                73,\r
+                13,\r
+                10,\r
+                7,\r
+                4,\r
+                1\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                3,\r
+                2\r
+            ],\r
+            "matrix": [\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                136.860107421875,\r
+                -64.45372009277344,\r
+                -36.179630279541019,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 0,\r
+            "matrix": [\r
+                -1.0,\r
+                8.979318677493353e-11,\r
+                0.0,\r
+                0.0,\r
+                -8.979318677493353e-11,\r
+                -1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                -294.67718505859377,\r
+                73.97987365722656,\r
+                16.17963218688965,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 1,\r
+            "matrix": [\r
+                0.9995650053024292,\r
+                0.029493184760212896,\r
+                0.0,\r
+                0.0,\r
+                -0.029493184760212896,\r
+                0.9995650053024292,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                -45.315460205078128,\r
+                -24.617263793945317,\r
+                -26.320369720458986,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                6,\r
+                5\r
+            ],\r
+            "matrix": [\r
+                -1.0,\r
+                8.979318677493353e-11,\r
+                0.0,\r
+                0.0,\r
+                -8.979318677493353e-11,\r
+                -1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                -136.860107421875,\r
+                64.45372009277344,\r
+                3.8203670978546144,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 0,\r
+            "matrix": [\r
+                -1.0,\r
+                8.979318677493353e-11,\r
+                0.0,\r
+                0.0,\r
+                -8.979318677493353e-11,\r
+                -1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                -294.67718505859377,\r
+                73.97987365722656,\r
+                16.17963218688965,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 1,\r
+            "matrix": [\r
+                0.9995650053024292,\r
+                0.029493184760212896,\r
+                0.0,\r
+                0.0,\r
+                -0.029493184760212896,\r
+                0.9995650053024292,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                -45.315460205078128,\r
+                -24.617263793945317,\r
+                -26.32036781311035,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                9,\r
+                8\r
+            ],\r
+            "matrix": [\r
+                -0.5,\r
+                -0.8660253882408142,\r
+                0.0,\r
+                0.0,\r
+                0.8660253882408142,\r
+                -0.5,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                18.09818458557129,\r
+                -69.69783782958985,\r
+                -105.559814453125,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 2,\r
+            "matrix": [\r
+                -0.9390941858291626,\r
+                0.3436597883701325,\r
+                0.0,\r
+                0.0,\r
+                -0.3436597883701325,\r
+                -0.9390941858291626,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                -51.311012268066409,\r
+                -50.52240753173828,\r
+                -18.440185546875,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 3,\r
+            "matrix": [\r
+                -0.9390941858291626,\r
+                0.3436597883701325,\r
+                0.0,\r
+                0.0,\r
+                -0.3436597883701325,\r
+                -0.9390941858291626,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                -51.311012268066409,\r
+                -50.52240753173828,\r
+                107.559814453125,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                12,\r
+                11\r
+            ],\r
+            "matrix": [\r
+                0.7071067690849304,\r
+                -0.7071067690849304,\r
+                0.0,\r
+                0.0,\r
+                0.7071067690849304,\r
+                0.7071067690849304,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                47.17867660522461,\r
+                -52.821327209472659,\r
+                -88.94477081298828,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 4,\r
+            "matrix": [\r
+                -0.4234085381031037,\r
+                -0.9059388637542724,\r
+                -7.575183536001616e-11,\r
+                0.0,\r
+                -0.9059388637542724,\r
+                0.4234085381031037,\r
+                -4.821281221478735e-11,\r
+                0.0,\r
+                7.575183536001616e-11,\r
+                4.821281221478735e-11,\r
+                -1.0,\r
+                0.0,\r
+                -90.59386444091796,\r
+                -24.379817962646489,\r
+                -40.05522918701172,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 5,\r
+            "matrix": [\r
+                -1.877404400829619e-7,\r
+                -1.194886607436274e-7,\r
+                1.0,\r
+                0.0,\r
+                -0.905938446521759,\r
+                0.42340943217277529,\r
+                -1.194886607436274e-7,\r
+                0.0,\r
+                -0.42340943217277529,\r
+                -0.905938446521759,\r
+                -1.877404400829619e-7,\r
+                0.0,\r
+                -30.2958984375,\r
+                -52.56131362915039,\r
+                25.05522727966309,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                72,\r
+                71,\r
+                70,\r
+                69,\r
+                68,\r
+                67,\r
+                66,\r
+                65,\r
+                14\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                64,\r
+                63,\r
+                62,\r
+                61,\r
+                60,\r
+                59,\r
+                58,\r
+                57,\r
+                56,\r
+                43,\r
+                29,\r
+                15\r
+            ],\r
+            "matrix": [\r
+                -2.430540746445331e-7,\r
+                0.0000014087579529586949,\r
+                -1.0,\r
+                0.0,\r
+                -1.0,\r
+                -2.430540746445331e-7,\r
+                2.4305373358402006e-7,\r
+                0.0,\r
+                -2.4305373358402006e-7,\r
+                1.0,\r
+                0.0000014087580666455324,\r
+                0.0,\r
+                -48.26182556152344,\r
+                -59.11042404174805,\r
+                34.595985412597659,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                28,\r
+                27,\r
+                16\r
+            ],\r
+            "matrix": [\r
+                -0.4546820223331452,\r
+                0.6541662216186523,\r
+                -0.6044260263442993,\r
+                0.0,\r
+                0.8696397542953491,\r
+                0.4726206660270691,\r
+                -0.1426759660243988,\r
+                0.0,\r
+                0.19233042001724244,\r
+                -0.590505063533783,\r
+                -0.7837810516357422,\r
+                0.0,\r
+                14.898193359375,\r
+                85.82951354980469,\r
+                -48.034645080566409,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                26,\r
+                25,\r
+                24,\r
+                23,\r
+                22,\r
+                21,\r
+                20,\r
+                19,\r
+                18,\r
+                17\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                0.8450329899787903,\r
+                0.005398945417255163,\r
+                -0.5346869230270386,\r
+                0.0,\r
+                -0.4710906744003296,\r
+                0.4805830717086792,\r
+                -0.7396712303161621,\r
+                0.0,\r
+                0.25296804308891299,\r
+                0.8769326210021973,\r
+                0.4086519181728363,\r
+                0.0,\r
+                -74.0894775390625,\r
+                71.41646575927735,\r
+                -157.91323852539066,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                -0.7145451903343201,\r
+                -0.1154177337884903,\r
+                0.6900028586387634,\r
+                0.0,\r
+                -0.6522517800331116,\r
+                0.4665486216545105,\r
+                -0.5974110960960388,\r
+                0.0,\r
+                -0.25296807289123537,\r
+                -0.8769327998161316,\r
+                -0.4086515009403229,\r
+                0.0,\r
+                122.53109741210938,\r
+                86.64814758300781,\r
+                -312.3133850097656,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                0.8450329899787903,\r
+                0.005398945417255163,\r
+                -0.5346869230270386,\r
+                0.0,\r
+                -0.4710906744003296,\r
+                0.4805830717086792,\r
+                -0.7396712303161621,\r
+                0.0,\r
+                0.25296804308891299,\r
+                0.8769326210021973,\r
+                0.4086519181728363,\r
+                0.0,\r
+                -69.3792953491211,\r
+                71.78133392333985,\r
+                -161.61203002929688,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                -0.7145451903343201,\r
+                -0.1154177337884903,\r
+                0.6900028586387634,\r
+                0.0,\r
+                -0.6522517800331116,\r
+                0.4665486216545105,\r
+                -0.5974110960960388,\r
+                0.0,\r
+                -0.25296807289123537,\r
+                -0.8769327998161316,\r
+                -0.4086515009403229,\r
+                0.0,\r
+                127.24127197265624,\r
+                87.01302337646485,\r
+                -316.0121765136719,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                0.8450329899787903,\r
+                0.005398945417255163,\r
+                -0.5346869230270386,\r
+                0.0,\r
+                -0.4710906744003296,\r
+                0.4805830717086792,\r
+                -0.7396712303161621,\r
+                0.0,\r
+                0.25296804308891299,\r
+                0.8769326210021973,\r
+                0.4086519181728363,\r
+                0.0,\r
+                -64.66907501220703,\r
+                72.14624786376953,\r
+                -165.310791015625,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                -0.7145451903343201,\r
+                -0.1154177337884903,\r
+                0.6900028586387634,\r
+                0.0,\r
+                -0.6522517800331116,\r
+                0.4665486216545105,\r
+                -0.5974110960960388,\r
+                0.0,\r
+                -0.25296807289123537,\r
+                -0.8769327998161316,\r
+                -0.4086515009403229,\r
+                0.0,\r
+                131.9515380859375,\r
+                87.37792205810547,\r
+                -319.7109680175781,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                0.8450329899787903,\r
+                0.005398945417255163,\r
+                -0.5346869230270386,\r
+                0.0,\r
+                -0.4710906744003296,\r
+                0.4805830717086792,\r
+                -0.7396712303161621,\r
+                0.0,\r
+                0.25296804308891299,\r
+                0.8769326210021973,\r
+                0.4086519181728363,\r
+                0.0,\r
+                -59.958885192871097,\r
+                72.5111312866211,\r
+                -169.00955200195313,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                -0.7145451903343201,\r
+                -0.1154177337884903,\r
+                0.6900028586387634,\r
+                0.0,\r
+                -0.6522517800331116,\r
+                0.4665486216545105,\r
+                -0.5974110960960388,\r
+                0.0,\r
+                -0.25296807289123537,\r
+                -0.8769327998161316,\r
+                -0.4086515009403229,\r
+                0.0,\r
+                136.66165161132813,\r
+                87.74280548095703,\r
+                -323.4097595214844,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                0.8450329899787903,\r
+                0.005398945417255163,\r
+                -0.5346869230270386,\r
+                0.0,\r
+                -0.4710906744003296,\r
+                0.4805830717086792,\r
+                -0.7396712303161621,\r
+                0.0,\r
+                0.25296804308891299,\r
+                0.8769326210021973,\r
+                0.4086519181728363,\r
+                0.0,\r
+                -55.24869537353516,\r
+                72.87601470947266,\r
+                -172.70831298828126,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                -0.7145451903343201,\r
+                -0.1154177337884903,\r
+                0.6900028586387634,\r
+                0.0,\r
+                -0.6522517800331116,\r
+                0.4665486216545105,\r
+                -0.5974110960960388,\r
+                0.0,\r
+                -0.25296807289123537,\r
+                -0.8769327998161316,\r
+                -0.4086515009403229,\r
+                0.0,\r
+                141.37188720703126,\r
+                88.10767364501953,\r
+                -327.1084899902344,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 7,\r
+            "matrix": [\r
+                0.7850325703620911,\r
+                0.06081420928239823,\r
+                -0.6164620518684387,\r
+                0.0,\r
+                -0.13561886548995973,\r
+                0.9878994822502136,\r
+                -0.07524696737527847,\r
+                0.0,\r
+                0.6044265031814575,\r
+                0.14267520606517793,\r
+                0.7837808728218079,\r
+                0.0,\r
+                -5.746735095977783,\r
+                -250.409912109375,\r
+                -86.68790435791016,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 8,\r
+            "matrix": [\r
+                0.785033106803894,\r
+                0.06081344559788704,\r
+                -0.616461455821991,\r
+                0.0,\r
+                -0.3681585192680359,\r
+                -0.7545340061187744,\r
+                -0.5432658195495606,\r
+                0.0,\r
+                -0.4981790184974671,\r
+                0.6534371972084045,\r
+                -0.569945216178894,\r
+                0.0,\r
+                34.187137603759769,\r
+                252.666015625,\r
+                65.06369018554688,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                42,\r
+                41,\r
+                30\r
+            ],\r
+            "matrix": [\r
+                0.4546822011470794,\r
+                0.6541662216186523,\r
+                0.604425847530365,\r
+                0.0,\r
+                -0.8696396350860596,\r
+                0.4726209044456482,\r
+                0.14267593622207642,\r
+                0.0,\r
+                -0.1923305094242096,\r
+                -0.5905048847198486,\r
+                0.7837811708450317,\r
+                0.0,\r
+                91.87051391601564,\r
+                80.63255310058594,\r
+                166.26089477539066,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                40,\r
+                39,\r
+                38,\r
+                37,\r
+                36,\r
+                35,\r
+                34,\r
+                33,\r
+                32,\r
+                31\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                0.8620702028274536,\r
+                -0.015319700352847577,\r
+                -0.506557285785675,\r
+                0.0,\r
+                -0.4340354502201081,\r
+                0.4936883449554444,\r
+                -0.7535814642906189,\r
+                0.0,\r
+                0.26162606477737429,\r
+                0.8695039749145508,\r
+                0.41894471645355227,\r
+                0.0,\r
+                -77.39921569824219,\r
+                74.49835205078125,\r
+                -159.90199279785157,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                -0.6893031001091003,\r
+                -0.13550062477588654,\r
+                0.7116886973381043,\r
+                0.0,\r
+                -0.6755834221839905,\r
+                0.47497618198394778,\r
+                -0.5639013051986694,\r
+                0.0,\r
+                -0.2616262137889862,\r
+                -0.8695039749145508,\r
+                -0.4189445376396179,\r
+                0.0,\r
+                115.9541015625,\r
+                89.47693634033203,\r
+                -311.7364501953125,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                0.8620702028274536,\r
+                -0.015319700352847577,\r
+                -0.506557285785675,\r
+                0.0,\r
+                -0.4340354502201081,\r
+                0.4936883449554444,\r
+                -0.7535814642906189,\r
+                0.0,\r
+                0.26162606477737429,\r
+                0.8695039749145508,\r
+                0.41894471645355227,\r
+                0.0,\r
+                -71.11894989013672,\r
+                74.98487091064453,\r
+                -164.83367919921876,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                -0.6893031001091003,\r
+                -0.13550062477588654,\r
+                0.7116886973381043,\r
+                0.0,\r
+                -0.6755834221839905,\r
+                0.47497618198394778,\r
+                -0.5639013051986694,\r
+                0.0,\r
+                -0.2616262137889862,\r
+                -0.8695039749145508,\r
+                -0.4189445376396179,\r
+                0.0,\r
+                122.234375,\r
+                89.96346282958985,\r
+                -316.668212890625,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                0.8620702028274536,\r
+                -0.015319700352847577,\r
+                -0.506557285785675,\r
+                0.0,\r
+                -0.4340354502201081,\r
+                0.4936883449554444,\r
+                -0.7535814642906189,\r
+                0.0,\r
+                0.26162606477737429,\r
+                0.8695039749145508,\r
+                0.41894471645355227,\r
+                0.0,\r
+                -64.83870697021485,\r
+                75.47139739990235,\r
+                -169.76536560058598,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                -0.6893031001091003,\r
+                -0.13550062477588654,\r
+                0.7116886973381043,\r
+                0.0,\r
+                -0.6755834221839905,\r
+                0.47497618198394778,\r
+                -0.5639013051986694,\r
+                0.0,\r
+                -0.2616262137889862,\r
+                -0.8695039749145508,\r
+                -0.4189445376396179,\r
+                0.0,\r
+                128.51461791992188,\r
+                90.44998931884766,\r
+                -321.5999145507813,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                0.8620702028274536,\r
+                -0.015319700352847577,\r
+                -0.506557285785675,\r
+                0.0,\r
+                -0.4340354502201081,\r
+                0.4936883449554444,\r
+                -0.7535814642906189,\r
+                0.0,\r
+                0.26162606477737429,\r
+                0.8695039749145508,\r
+                0.41894471645355227,\r
+                0.0,\r
+                -58.558441162109378,\r
+                75.9579086303711,\r
+                -174.6970672607422,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                -0.6893031001091003,\r
+                -0.13550062477588654,\r
+                0.7116886973381043,\r
+                0.0,\r
+                -0.6755834221839905,\r
+                0.47497618198394778,\r
+                -0.5639013051986694,\r
+                0.0,\r
+                -0.2616262137889862,\r
+                -0.8695039749145508,\r
+                -0.4189445376396179,\r
+                0.0,\r
+                134.79489135742188,\r
+                90.9365005493164,\r
+                -326.5315856933594,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                0.8620702028274536,\r
+                -0.015319700352847577,\r
+                -0.506557285785675,\r
+                0.0,\r
+                -0.4340354502201081,\r
+                0.4936883449554444,\r
+                -0.7535814642906189,\r
+                0.0,\r
+                0.26162606477737429,\r
+                0.8695039749145508,\r
+                0.41894471645355227,\r
+                0.0,\r
+                -52.27817535400391,\r
+                76.44441223144531,\r
+                -179.62875366210938,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 6,\r
+            "matrix": [\r
+                -0.6893031001091003,\r
+                -0.13550062477588654,\r
+                0.7116886973381043,\r
+                0.0,\r
+                -0.6755834221839905,\r
+                0.47497618198394778,\r
+                -0.5639013051986694,\r
+                0.0,\r
+                -0.2616262137889862,\r
+                -0.8695039749145508,\r
+                -0.4189445376396179,\r
+                0.0,\r
+                141.07516479492188,\r
+                91.42301940917968,\r
+                -331.4632263183594,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 7,\r
+            "matrix": [\r
+                0.7850325703620911,\r
+                0.06081420928239823,\r
+                -0.6164620518684387,\r
+                0.0,\r
+                -0.13561886548995973,\r
+                0.9878994822502136,\r
+                -0.07524696737527847,\r
+                0.0,\r
+                0.6044265031814575,\r
+                0.14267520606517793,\r
+                0.7837808728218079,\r
+                0.0,\r
+                -1.248520016670227,\r
+                -250.06192016601566,\r
+                -90.22713470458985,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 8,\r
+            "matrix": [\r
+                0.7850322723388672,\r
+                0.06081392988562584,\r
+                -0.6164625287055969,\r
+                0.0,\r
+                -0.3924780488014221,\r
+                -0.7211048007011414,\r
+                -0.5709367990493774,\r
+                0.0,\r
+                -0.4792549908161164,\r
+                0.6901518106460571,\r
+                -0.5422223806381226,\r
+                0.0,\r
+                45.59693908691406,\r
+                243.51312255859376,\r
+                69.38878631591797,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                55,\r
+                54,\r
+                49,\r
+                44\r
+            ],\r
+            "matrix": [\r
+                0.9999974370002748,\r
+                0.0,\r
+                -0.0022710729390382768,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0022710729390382768,\r
+                0.0,\r
+                0.9999974370002748,\r
+                0.0,\r
+                -0.13409000635147096,\r
+                -12.999988555908204,\r
+                0.12365700304508208,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                48,\r
+                47,\r
+                46,\r
+                45\r
+            ],\r
+            "matrix": [\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                -0.000007999999979801942,\r
+                -0.000011000000085914508,\r
+                -99.9995346069336,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 9,\r
+            "matrix": [\r
+                0.9999974370002748,\r
+                0.0,\r
+                0.002270043129101396,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                -0.002270043129101396,\r
+                0.0,\r
+                0.9999974370002748,\r
+                0.0,\r
+                49.7321662902832,\r
+                344.3533630371094,\r
+                79.59915161132813,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 10,\r
+            "matrix": [\r
+                0.9999974370002748,\r
+                1.0191530236923485e-13,\r
+                0.0022699993569403888,\r
+                0.0,\r
+                -1.0191530236923485e-13,\r
+                -1.0,\r
+                8.97931243248884e-11,\r
+                0.0,\r
+                0.0022699993569403888,\r
+                -8.97931243248884e-11,\r
+                -0.9999974370002748,\r
+                0.0,\r
+                49.69811248779297,\r
+                257.12298583984377,\r
+                94.59911346435549,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 11,\r
+            "matrix": [\r
+                0.9986716508865356,\r
+                -4.959933574966158e-10,\r
+                -0.051525991410017017,\r
+                0.0,\r
+                -0.051525991410017017,\r
+                -1.9239376314317274e-8,\r
+                -0.9986716508865356,\r
+                0.0,\r
+                -4.959933574966158e-10,\r
+                1.0,\r
+                -1.9239376314317274e-8,\r
+                0.0,\r
+                67.53964233398438,\r
+                326.7381286621094,\r
+                297.74066162109377,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 11,\r
+            "matrix": [\r
+                0.9986629486083984,\r
+                -1.884843170429917e-9,\r
+                -0.05169397220015526,\r
+                0.0,\r
+                -0.05169397220015526,\r
+                -7.287438563707839e-8,\r
+                -0.9986629486083984,\r
+                0.0,\r
+                -1.884843170429917e-9,\r
+                1.0,\r
+                -7.287438563707839e-8,\r
+                0.0,\r
+                17.574960708618165,\r
+                326.7381286621094,\r
+                297.6291809082031,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                53,\r
+                52,\r
+                51,\r
+                50\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 9,\r
+            "matrix": [\r
+                0.9999974370002748,\r
+                0.0,\r
+                0.002270043129101396,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                -0.002270043129101396,\r
+                0.0,\r
+                0.9999974370002748,\r
+                0.0,\r
+                49.50515365600586,\r
+                344.3533630371094,\r
+                79.59934997558594,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 10,\r
+            "matrix": [\r
+                0.9999974370002748,\r
+                1.0191530236923485e-13,\r
+                0.0022699993569403888,\r
+                0.0,\r
+                -1.0191530236923485e-13,\r
+                -1.0,\r
+                8.97931243248884e-11,\r
+                0.0,\r
+                0.0022699993569403888,\r
+                -8.97931243248884e-11,\r
+                -0.9999974370002748,\r
+                0.0,\r
+                49.47110366821289,\r
+                257.1229553222656,\r
+                94.59931182861328,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 11,\r
+            "matrix": [\r
+                0.9984543323516846,\r
+                -6.428584886641885e-10,\r
+                -0.05557801201939583,\r
+                0.0,\r
+                -0.05557801201939583,\r
+                -2.311567648405344e-8,\r
+                -0.9984543323516846,\r
+                0.0,\r
+                -6.428584886641885e-10,\r
+                1.0,\r
+                -2.311567648405344e-8,\r
+                0.0,\r
+                68.16710662841797,\r
+                326.7381286621094,\r
+                297.7886657714844,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 11,\r
+            "matrix": [\r
+                0.998445212841034,\r
+                5.507552747197052e-10,\r
+                -0.05574197694659233,\r
+                0.0,\r
+                -0.05574197694659233,\r
+                1.97455189976381e-8,\r
+                -0.998445212841034,\r
+                0.0,\r
+                5.507552747197052e-10,\r
+                1.0,\r
+                1.97455189976381e-8,\r
+                0.0,\r
+                18.201759338378908,\r
+                326.7381286621094,\r
+                297.6770324707031,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 12,\r
+            "matrix": [\r
+                -0.9999873638153076,\r
+                -0.004487415309995413,\r
+                -0.0022699744440615179,\r
+                0.0,\r
+                0.004487401805818081,\r
+                -0.999989926815033,\r
+                0.000011093182365584652,\r
+                0.0,\r
+                -0.0022700014524161817,\r
+                9.06754564766743e-7,\r
+                0.9999974370002748,\r
+                0.0,\r
+                -29.98022842407227,\r
+                768.69921875,\r
+                -20.581327438354493,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 12,\r
+            "matrix": [\r
+                -0.9999850392341614,\r
+                -0.004973169881850481,\r
+                -0.002269970485940576,\r
+                0.0,\r
+                0.004973156377673149,\r
+                -0.9999876618385316,\r
+                0.00001164450713986298,\r
+                0.0,\r
+                -0.0022700002882629639,\r
+                3.554153522600245e-7,\r
+                0.9999974370002748,\r
+                0.0,\r
+                48.794708251953128,\r
+                768.6771850585938,\r
+                -20.40250396728516,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 13,\r
+            "matrix": [\r
+                -0.8213930726051331,\r
+                0.3830228745937348,\r
+                0.4226191341876984,\r
+                0.0,\r
+                -0.3830228745937348,\r
+                0.17860689759254456,\r
+                -0.9063073992729188,\r
+                0.0,\r
+                -0.4226191341876984,\r
+                -0.9063073992729188,\r
+                4.807415265872805e-8,\r
+                0.0,\r
+                -141.64085388183598,\r
+                388.6727600097656,\r
+                122.00779724121094,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 13,\r
+            "matrix": [\r
+                -0.8213450312614441,\r
+                -0.38299909234046938,\r
+                0.42273396253585818,\r
+                0.0,\r
+                -0.38312679529190077,\r
+                -0.1786557286977768,\r
+                -0.9062538146972656,\r
+                0.0,\r
+                0.4226182401180268,\r
+                -0.906307816505432,\r
+                7.823305168130901e-7,\r
+                0.0,\r
+                -270.7940673828125,\r
+                145.4340057373047,\r
+                122.04411315917968,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 14,\r
+            "matrix": [\r
+                1.0,\r
+                -0.000060999998822808269,\r
+                -0.000001000000338535756,\r
+                0.0,\r
+                0.000001000000338535756,\r
+                9.44896494381453e-10,\r
+                1.0,\r
+                0.0,\r
+                -0.000060999998822808269,\r
+                -1.0,\r
+                1.0058964772241553e-9,\r
+                0.0,\r
+                51.087928771972659,\r
+                325.7384033203125,\r
+                91.09835815429688,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 15,\r
+            "matrix": [\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                4.4896641959724089e-11,\r
+                -1.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                4.4896641959724089e-11,\r
+                0.0,\r
+                24.52630043029785,\r
+                213.73817443847657,\r
+                283.0735778808594,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 16,\r
+            "matrix": [\r
+                0.9026297926902772,\r
+                -0.4304177761077881,\r
+                0.0,\r
+                0.0,\r
+                0.4304177761077881,\r
+                0.9026297926902772,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                353.9793395996094,\r
+                330.9040222167969,\r
+                -5.389931201934815,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 17,\r
+            "matrix": [\r
+                0.9026297330856324,\r
+                0.4304178953170777,\r
+                0.0,\r
+                0.0,\r
+                -0.4304178953170777,\r
+                0.9026297330856324,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                277.7611083984375,\r
+                580.0806274414063,\r
+                -5.3898539543151859,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 18,\r
+            "matrix": [\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                4.4896641959724089e-11,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                -1.0,\r
+                4.4896641959724089e-11,\r
+                0.0,\r
+                49.58733749389649,\r
+                175.73818969726566,\r
+                89.59835815429688,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 19,\r
+            "matrix": [\r
+                0.9999933242797852,\r
+                -1.649976856532565e-11,\r
+                0.003653998486697674,\r
+                0.0,\r
+                0.003653998486697674,\r
+                9.031045244967118e-9,\r
+                -0.9999933242797852,\r
+                0.0,\r
+                -1.649976856532565e-11,\r
+                1.0,\r
+                9.031045244967118e-9,\r
+                0.0,\r
+                81.19497680664063,\r
+                247.73817443847657,\r
+                220.1460723876953,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 19,\r
+            "matrix": [\r
+                0.9999898672103882,\r
+                -4.353116391658807e-12,\r
+                -0.004497999791055918,\r
+                0.0,\r
+                -0.004497999791055918,\r
+                -1.9355692604250409e-9,\r
+                -0.9999898672103882,\r
+                0.0,\r
+                -4.353116391658807e-12,\r
+                1.0,\r
+                -1.9355692604250409e-9,\r
+                0.0,\r
+                -23.12494468688965,\r
+                247.73817443847657,\r
+                220.35459899902345,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 20,\r
+            "matrix": [\r
+                -2.907086411596538e-7,\r
+                0.7071071863174439,\r
+                -0.7071064114570618,\r
+                0.0,\r
+                7.018321639407077e-7,\r
+                -0.7071064114570618,\r
+                -0.7071071863174439,\r
+                0.0,\r
+                -1.0,\r
+                -7.018321639407077e-7,\r
+                -2.907086411596538e-7,\r
+                0.0,\r
+                65.0,\r
+                128.71702575683598,\r
+                -3.270524024963379,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 21,\r
+            "matrix": [\r
+                4.4896641959724089e-11,\r
+                0.0,\r
+                -1.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                4.4896641959724089e-11,\r
+                0.0,\r
+                223.9999847412109,\r
+                31.98792839050293,\r
+                23.50873374938965,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 22,\r
+            "matrix": [\r
+                4.4896641959724089e-11,\r
+                0.0,\r
+                -1.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                4.4896641959724089e-11,\r
+                0.0,\r
+                223.9999847412109,\r
+                30.487924575805665,\r
+                25.008731842041017,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 23,\r
+            "matrix": [\r
+                1.1084333451094608e-10,\r
+                -0.0009251347510144116,\r
+                -0.9999995827674866,\r
+                0.0,\r
+                2.396268712345773e-7,\r
+                -0.9999995827674866,\r
+                0.0009251347510144116,\r
+                0.0,\r
+                -1.0,\r
+                -2.396268712345773e-7,\r
+                1.1084333451094608e-10,\r
+                0.0,\r
+                -295.0,\r
+                21.498876571655278,\r
+                0.49255698919296267,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 23,\r
+            "matrix": [\r
+                5.062397168131838e-10,\r
+                0.002356505487114191,\r
+                -0.9999971985816956,\r
+                0.0,\r
+                -4.2965237412317949e-7,\r
+                -0.9999971985816956,\r
+                -0.002356505487114191,\r
+                0.0,\r
+                -1.0,\r
+                4.2965237412317949e-7,\r
+                5.062397168131838e-10,\r
+                0.0,\r
+                -295.0,\r
+                21.60527801513672,\r
+                -105.32867431640624,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 24,\r
+            "matrix": [\r
+                -1.0,\r
+                6.349337028366264e-11,\r
+                6.349337028366264e-11,\r
+                0.0,\r
+                -6.349337028366264e-11,\r
+                -2.220446049250313e-16,\r
+                -1.0,\r
+                0.0,\r
+                -6.349337028366264e-11,\r
+                -1.0,\r
+                -2.220446049250313e-16,\r
+                0.0,\r
+                -126.93566131591796,\r
+                0.000003000000106112566,\r
+                36.558841705322269,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 25,\r
+            "matrix": [\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                4.4896641959724089e-11,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                -1.0,\r
+                4.4896641959724089e-11,\r
+                0.0,\r
+                126.93566131591796,\r
+                -0.00005299999975250103,\r
+                -36.5588493347168,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 26\r
+        },\r
+        {\r
+            "mesh": 27,\r
+            "matrix": [\r
+                -0.9861037135124208,\r
+                0.16613073647022248,\r
+                0.0,\r
+                0.0,\r
+                -0.16613073647022248,\r
+                -0.9861037135124208,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                -71.01806640625,\r
+                -22.193012237548829,\r
+                -20.0,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 27,\r
+            "matrix": [\r
+                0.9861037135124208,\r
+                -0.16613082587718965,\r
+                0.0,\r
+                0.0,\r
+                0.16613082587718965,\r
+                0.9861037135124208,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                71.01806640625,\r
+                22.19301414489746,\r
+                22.0,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 28,\r
+            "matrix": [\r
+                4.4896641959724089e-11,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                -1.0,\r
+                0.0,\r
+                4.4896641959724089e-11,\r
+                0.0,\r
+                -100.0,\r
+                -66.15117645263672,\r
+                -5.627896785736084,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 28,\r
+            "matrix": [\r
+                4.4896641959724089e-11,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                -1.0,\r
+                0.0,\r
+                4.4896641959724089e-11,\r
+                0.0,\r
+                -100.0,\r
+                -66.15116882324219,\r
+                14.372098922729493,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 28,\r
+            "matrix": [\r
+                -2.220446049250313e-16,\r
+                6.349337028366264e-11,\r
+                1.0,\r
+                0.0,\r
+                -6.349337028366264e-11,\r
+                -1.0,\r
+                6.349337028366264e-11,\r
+                0.0,\r
+                1.0,\r
+                -6.349337028366264e-11,\r
+                -2.220446049250313e-16,\r
+                0.0,\r
+                100.0,\r
+                -133.8488311767578,\r
+                34.37212753295899,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 28,\r
+            "matrix": [\r
+                -2.220446049250313e-16,\r
+                6.349337028366264e-11,\r
+                1.0,\r
+                0.0,\r
+                -6.349337028366264e-11,\r
+                -1.0,\r
+                6.349337028366264e-11,\r
+                0.0,\r
+                1.0,\r
+                -6.349337028366264e-11,\r
+                -2.220446049250313e-16,\r
+                0.0,\r
+                100.0,\r
+                -133.8488311767578,\r
+                54.37212371826172,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 23,\r
+            "matrix": [\r
+                5.062397168131838e-10,\r
+                0.002356505487114191,\r
+                -0.9999971985816956,\r
+                0.0,\r
+                -4.2965237412317949e-7,\r
+                -0.9999971985816956,\r
+                -0.002356505487114191,\r
+                0.0,\r
+                -1.0,\r
+                4.2965237412317949e-7,\r
+                5.062397168131838e-10,\r
+                0.0,\r
+                -295.0,\r
+                123.80779266357422,\r
+                -100.10150146484377,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 23,\r
+            "matrix": [\r
+                5.062397168131838e-10,\r
+                0.002356505487114191,\r
+                -0.9999971985816956,\r
+                0.0,\r
+                -4.2965237412317949e-7,\r
+                -0.9999971985816956,\r
+                -0.002356505487114191,\r
+                0.0,\r
+                -1.0,\r
+                4.2965237412317949e-7,\r
+                5.062397168131838e-10,\r
+                0.0,\r
+                -295.0,\r
+                132.1395263671875,\r
+                -3.9833459854125978,\r
+                1.0\r
+            ]\r
+        },\r
+        {\r
+            "matrix": [\r
+                0.7071067690849304,\r
+                -2.1563657526257888e-7,\r
+                -0.7071068286895752,\r
+                0.0,\r
+                -0.3312943577766419,\r
+                0.883452296257019,\r
+                -0.33129459619522097,\r
+                0.0,\r
+                0.6246951818466187,\r
+                0.4685211479663849,\r
+                0.6246950030326843,\r
+                0.0,\r
+                1005.9874267578124,\r
+                766.3170776367188,\r
+                953.3455810546876,\r
+                1.0\r
+            ],\r
+            "camera": 0\r
+        }\r
+    ],\r
+    "cameras": [\r
+        {\r
+            "perspective": {\r
+                "aspectRatio": 1.0,\r
+                "yfov": 0.3143463730812073,\r
+                "zfar": 1000000.0,\r
+                "znear": 0.04999999701976776\r
+            },\r
+            "type": "perspective"\r
+        }\r
+    ],\r
+    "meshes": [\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 1,\r
+                        "POSITION": 2\r
+                    },\r
+                    "indices": 0,\r
+                    "mode": 4,\r
+                    "material": 0\r
+                },\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 4,\r
+                        "POSITION": 5\r
+                    },\r
+                    "indices": 3,\r
+                    "mode": 4,\r
+                    "material": 1\r
+                }\r
+            ],\r
+            "name": "Piston_123-844_0_Parts_1"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 7,\r
+                        "POSITION": 8\r
+                    },\r
+                    "indices": 6,\r
+                    "mode": 4,\r
+                    "material": 2\r
+                }\r
+            ],\r
+            "name": "body_24"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 10,\r
+                        "POSITION": 11\r
+                    },\r
+                    "indices": 9,\r
+                    "mode": 4,\r
+                    "material": 3\r
+                }\r
+            ],\r
+            "name": "body_23"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 13,\r
+                        "POSITION": 14\r
+                    },\r
+                    "indices": 12,\r
+                    "mode": 4,\r
+                    "material": 4\r
+                }\r
+            ],\r
+            "name": "body_22"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 16,\r
+                        "POSITION": 17\r
+                    },\r
+                    "indices": 15,\r
+                    "mode": 4,\r
+                    "material": 5\r
+                }\r
+            ],\r
+            "name": "body_21"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 19,\r
+                        "POSITION": 20\r
+                    },\r
+                    "indices": 18,\r
+                    "mode": 4,\r
+                    "material": 6\r
+                }\r
+            ],\r
+            "name": "body_20"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 22,\r
+                        "POSITION": 23\r
+                    },\r
+                    "indices": 21,\r
+                    "mode": 4,\r
+                    "material": 7\r
+                },\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 25,\r
+                        "POSITION": 26\r
+                    },\r
+                    "indices": 24,\r
+                    "mode": 4,\r
+                    "material": 8\r
+                },\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 28,\r
+                        "POSITION": 29\r
+                    },\r
+                    "indices": 27,\r
+                    "mode": 4,\r
+                    "material": 9\r
+                }\r
+            ],\r
+            "name": "Spring_Link__0_Parts_1"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 31,\r
+                        "POSITION": 32\r
+                    },\r
+                    "indices": 30,\r
+                    "mode": 4,\r
+                    "material": 10\r
+                }\r
+            ],\r
+            "name": "body_19"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 34,\r
+                        "POSITION": 35\r
+                    },\r
+                    "indices": 33,\r
+                    "mode": 4,\r
+                    "material": 11\r
+                }\r
+            ],\r
+            "name": "body_18"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 37,\r
+                        "POSITION": 38\r
+                    },\r
+                    "indices": 36,\r
+                    "mode": 4,\r
+                    "material": 12\r
+                }\r
+            ],\r
+            "name": "body_17"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 40,\r
+                        "POSITION": 41\r
+                    },\r
+                    "indices": 39,\r
+                    "mode": 4,\r
+                    "material": 13\r
+                }\r
+            ],\r
+            "name": "body_16"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 43,\r
+                        "POSITION": 44\r
+                    },\r
+                    "indices": 42,\r
+                    "mode": 4,\r
+                    "material": 14\r
+                }\r
+            ],\r
+            "name": "body_15"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 46,\r
+                        "POSITION": 47\r
+                    },\r
+                    "indices": 45,\r
+                    "mode": 4,\r
+                    "material": 15\r
+                }\r
+            ],\r
+            "name": "body_14"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 49,\r
+                        "POSITION": 50\r
+                    },\r
+                    "indices": 48,\r
+                    "mode": 4,\r
+                    "material": 16\r
+                }\r
+            ],\r
+            "name": "body_13"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 52,\r
+                        "POSITION": 53\r
+                    },\r
+                    "indices": 51,\r
+                    "mode": 4,\r
+                    "material": 17\r
+                }\r
+            ],\r
+            "name": "body_12"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 55,\r
+                        "POSITION": 56\r
+                    },\r
+                    "indices": 54,\r
+                    "mode": 4,\r
+                    "material": 18\r
+                }\r
+            ],\r
+            "name": "body_11"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 58,\r
+                        "POSITION": 59\r
+                    },\r
+                    "indices": 57,\r
+                    "mode": 4,\r
+                    "material": 19\r
+                }\r
+            ],\r
+            "name": "body_10"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 61,\r
+                        "POSITION": 62\r
+                    },\r
+                    "indices": 60,\r
+                    "mode": 4,\r
+                    "material": 20\r
+                }\r
+            ],\r
+            "name": "body_9"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 64,\r
+                        "POSITION": 65\r
+                    },\r
+                    "indices": 63,\r
+                    "mode": 4,\r
+                    "material": 21\r
+                }\r
+            ],\r
+            "name": "body_8"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 67,\r
+                        "POSITION": 68\r
+                    },\r
+                    "indices": 66,\r
+                    "mode": 4,\r
+                    "material": 22\r
+                }\r
+            ],\r
+            "name": "body_7"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 70,\r
+                        "POSITION": 71\r
+                    },\r
+                    "indices": 69,\r
+                    "mode": 4,\r
+                    "material": 23\r
+                }\r
+            ],\r
+            "name": "body_6"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 73,\r
+                        "POSITION": 74\r
+                    },\r
+                    "indices": 72,\r
+                    "mode": 4,\r
+                    "material": 24\r
+                }\r
+            ],\r
+            "name": "body_5"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 76,\r
+                        "POSITION": 77\r
+                    },\r
+                    "indices": 75,\r
+                    "mode": 4,\r
+                    "material": 25\r
+                }\r
+            ],\r
+            "name": "body_4"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 79,\r
+                        "POSITION": 80\r
+                    },\r
+                    "indices": 78,\r
+                    "mode": 4,\r
+                    "material": 26\r
+                }\r
+            ],\r
+            "name": "body"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 82,\r
+                        "POSITION": 83\r
+                    },\r
+                    "indices": 81,\r
+                    "mode": 4,\r
+                    "material": 27\r
+                }\r
+            ],\r
+            "name": "body_3"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 85,\r
+                        "POSITION": 86\r
+                    },\r
+                    "indices": 84,\r
+                    "mode": 4,\r
+                    "material": 28\r
+                }\r
+            ],\r
+            "name": "body_2"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 88,\r
+                        "POSITION": 89\r
+                    },\r
+                    "indices": 87,\r
+                    "mode": 4,\r
+                    "material": 29\r
+                }\r
+            ],\r
+            "name": "body_1"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 91,\r
+                        "POSITION": 92\r
+                    },\r
+                    "indices": 90,\r
+                    "mode": 4,\r
+                    "material": 30\r
+                },\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 94,\r
+                        "POSITION": 95\r
+                    },\r
+                    "indices": 93,\r
+                    "mode": 4,\r
+                    "material": 31\r
+                }\r
+            ],\r
+            "name": "rod_123-699_0_Parts_1"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 97,\r
+                        "POSITION": 98\r
+                    },\r
+                    "indices": 96,\r
+                    "mode": 4,\r
+                    "material": 32\r
+                },\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 100,\r
+                        "POSITION": 101\r
+                    },\r
+                    "indices": 99,\r
+                    "mode": 4,\r
+                    "material": 33\r
+                }\r
+            ],\r
+            "name": "Lifter_123-923_0_Parts_1"\r
+        }\r
+    ],\r
+    "accessors": [\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 0,\r
+            "componentType": 5123,\r
+            "count": 8250,\r
+            "max": [\r
+                2011\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 0,\r
+            "componentType": 5126,\r
+            "count": 2012,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 24144,\r
+            "componentType": 5126,\r
+            "count": 2012,\r
+            "max": [\r
+                63.35246658325195,\r
+                56.026153564453128,\r
+                46.5\r
+            ],\r
+            "min": [\r
+                -43.13071060180664,\r
+                -36.97384643554688,\r
+                -46.5\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 16500,\r
+            "componentType": 5123,\r
+            "count": 5034,\r
+            "max": [\r
+                1295\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 48288,\r
+            "componentType": 5126,\r
+            "count": 1296,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                0.9992589950561525\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -0.9992589950561525\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 63840,\r
+            "componentType": 5126,\r
+            "count": 1296,\r
+            "max": [\r
+                53.35246658325195,\r
+                56.026153564453128,\r
+                46.46559143066406\r
+            ],\r
+            "min": [\r
+                31.869285583496095,\r
+                -36.97384643554688,\r
+                -46.46559143066406\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 26568,\r
+            "componentType": 5123,\r
+            "count": 936,\r
+            "max": [\r
+                263\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 79392,\r
+            "componentType": 5126,\r
+            "count": 264,\r
+            "max": [\r
+                1.0,\r
+                0.9992589950561525,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -0.9992589950561525,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 82560,\r
+            "componentType": 5126,\r
+            "count": 264,\r
+            "max": [\r
+                -242.49256896972657,\r
+                107.58116149902344,\r
+                85.0\r
+            ],\r
+            "min": [\r
+                -264.4925537109375,\r
+                85.59744262695313,\r
+                0.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 28440,\r
+            "componentType": 5123,\r
+            "count": 1845,\r
+            "max": [\r
+                669\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 85728,\r
+            "componentType": 5126,\r
+            "count": 670,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 93768,\r
+            "componentType": 5126,\r
+            "count": 670,\r
+            "max": [\r
+                35.97993087768555,\r
+                35.75779342651367,\r
+                10.0\r
+            ],\r
+            "min": [\r
+                -35.97993087768555,\r
+                -35.97336959838867,\r
+                -15.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 32130,\r
+            "componentType": 5123,\r
+            "count": 6489,\r
+            "max": [\r
+                1552\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 101808,\r
+            "componentType": 5126,\r
+            "count": 1553,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 120444,\r
+            "componentType": 5126,\r
+            "count": 1553,\r
+            "max": [\r
+                63.0,\r
+                35.0,\r
+                113.5\r
+            ],\r
+            "min": [\r
+                -62.99030303955078,\r
+                -35.0,\r
+                -140.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 45108,\r
+            "componentType": 5123,\r
+            "count": 9267,\r
+            "max": [\r
+                2469\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 139080,\r
+            "componentType": 5126,\r
+            "count": 2470,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 168720,\r
+            "componentType": 5126,\r
+            "count": 2470,\r
+            "max": [\r
+                68.9806137084961,\r
+                -31.11992645263672,\r
+                10.0\r
+            ],\r
+            "min": [\r
+                -68.9806137084961,\r
+                -168.8800811767578,\r
+                -15.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 63642,\r
+            "componentType": 5123,\r
+            "count": 4839,\r
+            "max": [\r
+                1381\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 198360,\r
+            "componentType": 5126,\r
+            "count": 1382,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 214944,\r
+            "componentType": 5126,\r
+            "count": 1382,\r
+            "max": [\r
+                177.38954162597657,\r
+                -14.305740356445313,\r
+                18.985939025878908\r
+            ],\r
+            "min": [\r
+                -76.1104507446289,\r
+                -55.56795501708985,\r
+                -19.073078155517576\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 73320,\r
+            "componentType": 5123,\r
+            "count": 123,\r
+            "max": [\r
+                42\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 231528,\r
+            "componentType": 5126,\r
+            "count": 43,\r
+            "max": [\r
+                0.9818310141563416,\r
+                0.9992589950561525,\r
+                0.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -0.9992589950561525,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 232044,\r
+            "componentType": 5126,\r
+            "count": 43,\r
+            "max": [\r
+                131.15240478515626,\r
+                -35.549163818359378,\r
+                5.0\r
+            ],\r
+            "min": [\r
+                129.44984436035157,\r
+                -37.250465393066409,\r
+                0.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 73566,\r
+            "componentType": 5123,\r
+            "count": 123,\r
+            "max": [\r
+                42\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 232560,\r
+            "componentType": 5126,\r
+            "count": 43,\r
+            "max": [\r
+                0.9992589950561525,\r
+                1.0,\r
+                0.0\r
+            ],\r
+            "min": [\r
+                -0.9992589950561525,\r
+                -0.9818310141563416,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 233076,\r
+            "componentType": 5126,\r
+            "count": 43,\r
+            "max": [\r
+                131.19471740722657,\r
+                -61.50555038452149,\r
+                5.0\r
+            ],\r
+            "min": [\r
+                129.4075164794922,\r
+                -63.2940788269043,\r
+                0.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 73812,\r
+            "componentType": 5123,\r
+            "count": 4842,\r
+            "max": [\r
+                869\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 233592,\r
+            "componentType": 5126,\r
+            "count": 870,\r
+            "max": [\r
+                1.0,\r
+                0.9992589950561525,\r
+                0.9992589950561525\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -0.9992589950561525,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 244032,\r
+            "componentType": 5126,\r
+            "count": 870,\r
+            "max": [\r
+                132.30111694335938,\r
+                -34.40129470825195,\r
+                14.998519897460938\r
+            ],\r
+            "min": [\r
+                128.30111694335938,\r
+                -64.39833068847656,\r
+                0.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 83496,\r
+            "componentType": 5123,\r
+            "count": 987,\r
+            "max": [\r
+                293\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 254472,\r
+            "componentType": 5126,\r
+            "count": 294,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                0.9992589950561525\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -0.9992589950561525\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 258000,\r
+            "componentType": 5126,\r
+            "count": 294,\r
+            "max": [\r
+                181.5590057373047,\r
+                312.1140747070313,\r
+                17.986684799194337\r
+            ],\r
+            "min": [\r
+                71.55899810791016,\r
+                276.1140747070313,\r
+                -17.986684799194337\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 85470,\r
+            "componentType": 5123,\r
+            "count": 2145,\r
+            "max": [\r
+                623\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 261528,\r
+            "componentType": 5126,\r
+            "count": 624,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                0.9992589950561525\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -0.9992589950561525\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 269016,\r
+            "componentType": 5126,\r
+            "count": 624,\r
+            "max": [\r
+                211.16444396972657,\r
+                300.2055358886719,\r
+                15.988161087036133\r
+            ],\r
+            "min": [\r
+                202.16444396972657,\r
+                268.2055358886719,\r
+                -15.988161087036133\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 89760,\r
+            "componentType": 5123,\r
+            "count": 1566,\r
+            "max": [\r
+                407\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 276504,\r
+            "componentType": 5126,\r
+            "count": 408,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 281400,\r
+            "componentType": 5126,\r
+            "count": 408,\r
+            "max": [\r
+                54.54435348510742,\r
+                -38.61518478393555,\r
+                15.0\r
+            ],\r
+            "min": [\r
+                -44.45564651489258,\r
+                -80.61518859863281,\r
+                0.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 92892,\r
+            "componentType": 5123,\r
+            "count": 1359,\r
+            "max": [\r
+                339\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 286296,\r
+            "componentType": 5126,\r
+            "count": 340,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 290376,\r
+            "componentType": 5126,\r
+            "count": 340,\r
+            "max": [\r
+                54.54435348510742,\r
+                -48.61518478393555,\r
+                15.0\r
+            ],\r
+            "min": [\r
+                -44.45564651489258,\r
+                -61.61518478393555,\r
+                0.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 95610,\r
+            "componentType": 5123,\r
+            "count": 1032,\r
+            "max": [\r
+                323\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 294456,\r
+            "componentType": 5126,\r
+            "count": 324,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 298344,\r
+            "componentType": 5126,\r
+            "count": 324,\r
+            "max": [\r
+                30.5535831451416,\r
+                217.1583709716797,\r
+                0.0\r
+            ],\r
+            "min": [\r
+                15.553584098815918,\r
+                202.16946411132813,\r
+                -81.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 97674,\r
+            "componentType": 5123,\r
+            "count": 288,\r
+            "max": [\r
+                95\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 302232,\r
+            "componentType": 5126,\r
+            "count": 96,\r
+            "max": [\r
+                1.0,\r
+                0.9992589950561525,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -0.9992589950561525,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 303384,\r
+            "componentType": 5126,\r
+            "count": 96,\r
+            "max": [\r
+                -36.67900466918945,\r
+                469.6546325683594,\r
+                115.00000762939452\r
+            ],\r
+            "min": [\r
+                -49.67900466918945,\r
+                456.6642761230469,\r
+                0.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 98250,\r
+            "componentType": 5123,\r
+            "count": 1266,\r
+            "max": [\r
+                347\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 304536,\r
+            "componentType": 5126,\r
+            "count": 348,\r
+            "max": [\r
+                1.0,\r
+                0.9992589950561525,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -0.9992589950561525,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 308712,\r
+            "componentType": 5126,\r
+            "count": 348,\r
+            "max": [\r
+                -278.3986206054688,\r
+                -55.62376022338867,\r
+                65.0\r
+            ],\r
+            "min": [\r
+                -296.3985900878906,\r
+                -73.61044311523438,\r
+                0.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 100782,\r
+            "componentType": 5123,\r
+            "count": 5787,\r
+            "max": [\r
+                1408\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 312888,\r
+            "componentType": 5126,\r
+            "count": 1409,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 329796,\r
+            "componentType": 5126,\r
+            "count": 1409,\r
+            "max": [\r
+                105.0087432861328,\r
+                60.05363082885742,\r
+                75.0\r
+            ],\r
+            "min": [\r
+                -97.99126434326172,\r
+                -124.0295867919922,\r
+                2.307800054550171\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 112356,\r
+            "componentType": 5123,\r
+            "count": 8964,\r
+            "max": [\r
+                1939\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 346704,\r
+            "componentType": 5126,\r
+            "count": 1940,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 369984,\r
+            "componentType": 5126,\r
+            "count": 1940,\r
+            "max": [\r
+                40.85147476196289,\r
+                233.1286773681641,\r
+                70.0\r
+            ],\r
+            "min": [\r
+                19.2884521484375,\r
+                214.79766845703129,\r
+                -28.7859001159668\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 130284,\r
+            "componentType": 5123,\r
+            "count": 10167,\r
+            "max": [\r
+                1753\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 393264,\r
+            "componentType": 5126,\r
+            "count": 1754,\r
+            "max": [\r
+                1.0,\r
+                0.9992589950561525,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -0.9999880194664002,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 414312,\r
+            "componentType": 5126,\r
+            "count": 1754,\r
+            "max": [\r
+                -252.55343627929688,\r
+                -164.07803344726566,\r
+                85.0\r
+            ],\r
+            "min": [\r
+                -330.4447326660156,\r
+                -199.1783599853516,\r
+                0.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 150618,\r
+            "componentType": 5123,\r
+            "count": 10125,\r
+            "max": [\r
+                1748\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 435360,\r
+            "componentType": 5126,\r
+            "count": 1749,\r
+            "max": [\r
+                1.0,\r
+                0.9992589950561525,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -0.9999880194664002,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 456348,\r
+            "componentType": 5126,\r
+            "count": 1749,\r
+            "max": [\r
+                -248.47320556640626,\r
+                -164.07803344726566,\r
+                85.0\r
+            ],\r
+            "min": [\r
+                -326.3644409179688,\r
+                -199.1783599853516,\r
+                0.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 170868,\r
+            "componentType": 5123,\r
+            "count": 21150,\r
+            "max": [\r
+                5473\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 477336,\r
+            "componentType": 5126,\r
+            "count": 5474,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 543024,\r
+            "componentType": 5126,\r
+            "count": 5474,\r
+            "max": [\r
+                113.00873565673828,\r
+                59.71368026733399,\r
+                0.0026710000820457937\r
+            ],\r
+            "min": [\r
+                -102.99126434326172,\r
+                -120.6896514892578,\r
+                -82.39035034179688\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 213168,\r
+            "componentType": 5123,\r
+            "count": 1359,\r
+            "max": [\r
+                371\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 608712,\r
+            "componentType": 5126,\r
+            "count": 372,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 613176,\r
+            "componentType": 5126,\r
+            "count": 372,\r
+            "max": [\r
+                37.65166091918945,\r
+                226.15505981445316,\r
+                0.0\r
+            ],\r
+            "min": [\r
+                13.65165901184082,\r
+                202.1728057861328,\r
+                -17.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 215886,\r
+            "componentType": 5123,\r
+            "count": 684,\r
+            "max": [\r
+                239\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 617640,\r
+            "componentType": 5126,\r
+            "count": 240,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 620520,\r
+            "componentType": 5126,\r
+            "count": 240,\r
+            "max": [\r
+                -25.97146224975586,\r
+                156.0619659423828,\r
+                0.0\r
+            ],\r
+            "min": [\r
+                -188.97146606445316,\r
+                -6.938029766082764,\r
+                -230.0000152587891\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 217254,\r
+            "componentType": 5123,\r
+            "count": 5889,\r
+            "max": [\r
+                1413\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 623400,\r
+            "componentType": 5126,\r
+            "count": 1414,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 640368,\r
+            "componentType": 5126,\r
+            "count": 1414,\r
+            "max": [\r
+                105.0087432861328,\r
+                60.05352783203125,\r
+                147.6921844482422\r
+            ],\r
+            "min": [\r
+                -97.99126434326172,\r
+                -124.0295867919922,\r
+                75.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 229032,\r
+            "componentType": 5123,\r
+            "count": 20607,\r
+            "max": [\r
+                5363\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 657336,\r
+            "componentType": 5126,\r
+            "count": 5364,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 721704,\r
+            "componentType": 5126,\r
+            "count": 5364,\r
+            "max": [\r
+                113.00873565673828,\r
+                59.71368026733399,\r
+                82.39035034179688\r
+            ],\r
+            "min": [\r
+                -102.99126434326172,\r
+                -120.6896514892578,\r
+                -0.002749999985098839\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 270246,\r
+            "componentType": 5123,\r
+            "count": 573,\r
+            "max": [\r
+                191\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 786072,\r
+            "componentType": 5126,\r
+            "count": 192,\r
+            "max": [\r
+                1.0,\r
+                0.9992589950561525,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -0.9992589950561525,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 788376,\r
+            "componentType": 5126,\r
+            "count": 192,\r
+            "max": [\r
+                -25.97146224975586,\r
+                81.05715942382813,\r
+                0.0\r
+            ],\r
+            "min": [\r
+                -38.97146224975586,\r
+                68.06676483154297,\r
+                -230.0000152587891\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 271392,\r
+            "componentType": 5123,\r
+            "count": 32934,\r
+            "max": [\r
+                8617\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 790680,\r
+            "componentType": 5126,\r
+            "count": 8618,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 894096,\r
+            "componentType": 5126,\r
+            "count": 8618,\r
+            "max": [\r
+                99.06433868408205,\r
+                151.558837890625,\r
+                80.00056457519531\r
+            ],\r
+            "min": [\r
+                -61.93566513061524,\r
+                -38.4411506652832,\r
+                -80.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 337260,\r
+            "componentType": 5123,\r
+            "count": 33420,\r
+            "max": [\r
+                8617\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 997512,\r
+            "componentType": 5126,\r
+            "count": 8618,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1100928,\r
+            "componentType": 5126,\r
+            "count": 8618,\r
+            "max": [\r
+                99.06433868408205,\r
+                151.558837890625,\r
+                80.00056457519531\r
+            ],\r
+            "min": [\r
+                -61.93566513061524,\r
+                -38.4411506652832,\r
+                -80.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 404100,\r
+            "componentType": 5123,\r
+            "count": 17691,\r
+            "max": [\r
+                3671\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1204344,\r
+            "componentType": 5126,\r
+            "count": 3672,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1248408,\r
+            "componentType": 5126,\r
+            "count": 3672,\r
+            "max": [\r
+                95.0,\r
+                90.0,\r
+                112.99999237060549\r
+            ],\r
+            "min": [\r
+                -95.0,\r
+                -180.9715576171875,\r
+                -120.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 439482,\r
+            "componentType": 5123,\r
+            "count": 3903,\r
+            "max": [\r
+                1280\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1292472,\r
+            "componentType": 5126,\r
+            "count": 1281,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1307844,\r
+            "componentType": 5126,\r
+            "count": 1281,\r
+            "max": [\r
+                111.05359649658205,\r
+                30.67658042907715,\r
+                10.0\r
+            ],\r
+            "min": [\r
+                -70.94640350341797,\r
+                -43.32342147827149,\r
+                -10.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 447288,\r
+            "componentType": 5123,\r
+            "count": 960,\r
+            "max": [\r
+                179\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1323216,\r
+            "componentType": 5126,\r
+            "count": 180,\r
+            "max": [\r
+                1.0,\r
+                0.9992589950561525,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -0.9992589950561525,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1325376,\r
+            "componentType": 5126,\r
+            "count": 180,\r
+            "max": [\r
+                76.06450653076172,\r
+                2.5340518951416017,\r
+                7.0\r
+            ],\r
+            "min": [\r
+                -11.810647010803225,\r
+                -15.180893898010254,\r
+                -7.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 449208,\r
+            "componentType": 5123,\r
+            "count": 423,\r
+            "max": [\r
+                142\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1327536,\r
+            "componentType": 5126,\r
+            "count": 143,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -0.9992589950561525,\r
+                0.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1329252,\r
+            "componentType": 5126,\r
+            "count": 143,\r
+            "max": [\r
+                -16.62210273742676,\r
+                -26.09882354736328,\r
+                0.0\r
+            ],\r
+            "min": [\r
+                -32.12210464477539,\r
+                -41.59605407714844,\r
+                -81.32071685791016\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 450054,\r
+            "componentType": 5123,\r
+            "count": 2163,\r
+            "max": [\r
+                385\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1330968,\r
+            "componentType": 5126,\r
+            "count": 386,\r
+            "max": [\r
+                0.7761409878730774,\r
+                0.7761409878730774,\r
+                -0.6286939978599548\r
+            ],\r
+            "min": [\r
+                -0.7758179903030396,\r
+                -0.7761409878730774,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1335600,\r
+            "componentType": 5126,\r
+            "count": 386,\r
+            "max": [\r
+                -16.62210273742676,\r
+                -26.09882354736328,\r
+                -81.31858825683594\r
+            ],\r
+            "min": [\r
+                -32.11616134643555,\r
+                -41.59875106811524,\r
+                -85.0\r
+            ],\r
+            "type": "VEC3"\r
+        }\r
+    ],\r
+    "materials": [\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_20"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.0,\r
+                    0.0,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_21"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.5609999895095825,\r
+                    0.0,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_17"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.5609999895095825,\r
+                    0.0,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_17"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.42500001192092898,\r
+                    0.42500001192092898,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_22"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.5609999895095825,\r
+                    0.0,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_17"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.42500001192092898,\r
+                    0.42500001192092898,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_22"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.5609999895095825,\r
+                    0.0,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_17"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.0,\r
+                    0.0,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_18"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.5609999895095825,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_23"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.0,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_16"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.0,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_16"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_20"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_20"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.0,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_16"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.0,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_16"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.5609999895095825,\r
+                    0.0,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_17"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.5609999895095825,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_23"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_20"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.5609999895095825,\r
+                    0.0,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_17"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.5609999895095825,\r
+                    0.0,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_17"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_20"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.0,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_16"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.42500001192092898,\r
+                    0.42500001192092898,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_22"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.5609999895095825,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_23"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_20"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.0,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_16"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_20"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_20"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_20"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_19"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    0.8500000238418579,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_20"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.5609999895095825,\r
+                    0.0,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_17"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.8500000238418579,\r
+                    0.0,\r
+                    0.0,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "name": "Material_18"\r
+        }\r
+    ],\r
+    "bufferViews": [\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 1340232,\r
+            "byteLength": 454380,\r
+            "target": 34963\r
+        },\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 0,\r
+            "byteLength": 1340232,\r
+            "byteStride": 12,\r
+            "target": 34962\r
+        }\r
+    ],\r
+    "buffers": [\r
+        {\r
+            "byteLength": 1794612,\r
+            "uri": "2CylinderEngine0.bin"\r
+        }\r
+    ]\r
+}\r
diff --git a/automated-tests/resources/2CylinderEngine0.bin b/automated-tests/resources/2CylinderEngine0.bin
new file mode 100644 (file)
index 0000000..e8cb49d
Binary files /dev/null and b/automated-tests/resources/2CylinderEngine0.bin differ
diff --git a/automated-tests/resources/AnimatedMorphCube.bin b/automated-tests/resources/AnimatedMorphCube.bin
new file mode 100644 (file)
index 0000000..7b14a17
Binary files /dev/null and b/automated-tests/resources/AnimatedMorphCube.bin differ
diff --git a/automated-tests/resources/AnimatedMorphCube.gltf b/automated-tests/resources/AnimatedMorphCube.gltf
new file mode 100644 (file)
index 0000000..fa99bc5
--- /dev/null
@@ -0,0 +1,282 @@
+{\r
+  "accessors": [\r
+    {\r
+      "bufferView": 0,\r
+      "componentType": 5126,\r
+      "count": 24,\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 1,\r
+      "componentType": 5126,\r
+      "count": 24,\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 2,\r
+      "componentType": 5126,\r
+      "count": 24,\r
+      "type": "VEC3",\r
+      "max": [\r
+        0.0100000035,\r
+        0.0100000035,\r
+        0.01\r
+      ],\r
+      "min": [\r
+        -0.0100000044,\r
+        -0.0100000054,\r
+        -0.01\r
+      ]\r
+    },\r
+    {\r
+      "bufferView": 3,\r
+      "componentType": 5126,\r
+      "count": 24,\r
+      "type": "VEC3",\r
+      "name": "thin"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "componentType": 5126,\r
+      "count": 24,\r
+      "type": "VEC3",\r
+      "max": [\r
+        0.0,\r
+        0.01893253,\r
+        0.0\r
+      ],\r
+      "min": [\r
+        0.0,\r
+        0.0,\r
+        0.0\r
+      ],\r
+      "name": "thin"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "componentType": 5126,\r
+      "count": 24,\r
+      "type": "VEC3",\r
+      "name": "thin"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "componentType": 5126,\r
+      "count": 24,\r
+      "type": "VEC3",\r
+      "name": "angle"\r
+    },\r
+    {\r
+      "bufferView": 7,\r
+      "componentType": 5126,\r
+      "count": 24,\r
+      "type": "VEC3",\r
+      "max": [\r
+        0.0,\r
+        0.0198908355,\r
+        0.0\r
+      ],\r
+      "min": [\r
+        0.0,\r
+        0.0,\r
+        0.0\r
+      ],\r
+      "name": "angle"\r
+    },\r
+    {\r
+      "bufferView": 8,\r
+      "componentType": 5126,\r
+      "count": 24,\r
+      "type": "VEC3",\r
+      "name": "angle"\r
+    },\r
+    {\r
+      "bufferView": 9,\r
+      "componentType": 5123,\r
+      "count": 36,\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 10,\r
+      "componentType": 5126,\r
+      "count": 127,\r
+      "type": "SCALAR",\r
+      "max": [\r
+        4.19999743\r
+      ],\r
+      "min": [\r
+        0.0\r
+      ]\r
+    },\r
+    {\r
+      "bufferView": 11,\r
+      "componentType": 5126,\r
+      "count": 254,\r
+      "type": "SCALAR"\r
+    }\r
+  ],\r
+  "animations": [\r
+    {\r
+      "channels": [\r
+        {\r
+          "sampler": 0,\r
+          "target": {\r
+            "node": 0,\r
+            "path": "weights"\r
+          }\r
+        }\r
+      ],\r
+      "samplers": [\r
+        {\r
+          "input": 10,\r
+          "interpolation": "LINEAR",\r
+          "output": 11\r
+        }\r
+      ],\r
+      "name": "Square"\r
+    }\r
+  ],\r
+  "asset": {\r
+    "generator": "glTF Tools for Unity",\r
+    "version": "2.0"\r
+  },\r
+  "bufferViews": [\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 288\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 288,\r
+      "byteLength": 384\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 672,\r
+      "byteLength": 288\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 960,\r
+      "byteLength": 288\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 1248,\r
+      "byteLength": 288\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 1536,\r
+      "byteLength": 288\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 1824,\r
+      "byteLength": 288\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 2112,\r
+      "byteLength": 288\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 2400,\r
+      "byteLength": 288\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 2688,\r
+      "byteLength": 72\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 2760,\r
+      "byteLength": 508\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 3268,\r
+      "byteLength": 1016\r
+    }\r
+  ],\r
+  "buffers": [\r
+    {\r
+      "uri": "AnimatedMorphCube.bin",\r
+      "byteLength": 4284\r
+    }\r
+  ],\r
+  "meshes": [\r
+    {\r
+      "primitives": [\r
+        {\r
+          "attributes": {\r
+            "NORMAL": 0,\r
+            "TANGENT": 1,\r
+            "POSITION": 2\r
+          },\r
+          "indices": 9,\r
+          "material": 0,\r
+          "targets": [\r
+            {\r
+              "NORMAL": 3,\r
+              "POSITION": 4,\r
+              "TANGENT": 5\r
+            },\r
+            {\r
+              "NORMAL": 6,\r
+              "POSITION": 7,\r
+              "TANGENT": 8\r
+            }\r
+          ]\r
+        }\r
+      ],\r
+      "weights": [\r
+        0.0,\r
+        0.0\r
+      ],\r
+      "name": "Cube"\r
+    }\r
+  ],\r
+  "materials": [\r
+    {\r
+      "pbrMetallicRoughness": {\r
+        "baseColorFactor": [\r
+          0.6038274,\r
+          0.6038274,\r
+          0.6038274,\r
+          1.0\r
+        ],\r
+        "metallicFactor": 0.0,\r
+        "roughnessFactor": 0.5\r
+      },\r
+      "name": "Material"\r
+    }\r
+  ],\r
+  "nodes": [\r
+    {\r
+      "mesh": 0,\r
+      "rotation": [\r
+        0.0,\r
+        0.7071067,\r
+        -0.7071068,\r
+        0.0\r
+      ],\r
+      "scale": [\r
+        100.0,\r
+        100.0,\r
+        100.0\r
+      ],\r
+      "name": "AnimatedMorphCube"\r
+    }\r
+  ],\r
+  "scene": 0,\r
+  "scenes": [\r
+    {\r
+      "nodes": [\r
+        0\r
+      ]\r
+    }\r
+  ]\r
+}
\ No newline at end of file
diff --git a/automated-tests/resources/AnimatedMorphSphere.bin b/automated-tests/resources/AnimatedMorphSphere.bin
new file mode 100644 (file)
index 0000000..5bc8fc6
--- /dev/null
@@ -0,0 +1,185 @@
+\86ô=½u\18ñ>f\87a¿\86ô=½u\18ñ>f\87a¿\86ô=½u\18ñ>f\87a¿\86ô=½u\18ñ>f\87a¿å7±½Çì`?9sð>å7±½Çì`?9sð>å7±½Çì`?9sð>å7±½Çì`?9sð>nk\7f½±\16"?k\81E¿nk\7f½±\16"?k\81E¿nk\7f½±\16"?k\81E¿nk\7f½±\16"?k\81E¿Õw\9b½ßQE?\95ï!?Õw\9b½ßQE?\95ï!?Õw\9b½ßQE?\95ï!?Õw\9b½ßQE?\95ï!?Õw\9b½ßQE?\95ï!¿Õw\9b½ßQE?\95ï!¿Õw\9b½ßQE?\95ï!¿Õw\9b½ßQE?\95ï!¿¹k\7f½°\16"?j\81E?¹k\7f½°\16"?j\81E?¹k\7f½°\16"?j\81E?¹k\7f½°\16"?j\81E?ð7±½Çì`?6sð¾ð7±½Çì`?6sð¾ð7±½Çì`?6sð¾ð7±½Çì`?6sð¾\88ô=½\7f\18ñ>d\87a?\88ô=½\7f\18ñ>d\87a?\88ô=½\7f\18ñ>d\87a?\88ô=½\7f\18ñ>d\87a?\8b*À½_ås?4ø\93¾\8b*À½_ås?4ø\93¾\8b*À½_ås?4ø\93¾\8b*À½_ås?4ø\93¾\v\eê¼¢\90\94>màt?\v\eê¼¢\90\94>màt?\v\eê¼¢\90\94>màt?\v\eê¼¢\90\94>màt?ãÅǽe\8d}?IÈǽãÅǽe\8d}?IÈǽãÅǽe\8d}?IÈǽãÅǽe\8d}?IÈǽ\8f\1a\1e¼ËºÈ=dÁ~¿\8f\1a\1e¼ËºÈ=dÁ~¿\8f\1a\1e¼ËºÈ=dÁ~¿Î5ê¼÷\ 3Á=bÁ~¿A+>½Pâ±=bÁ~¿ù5ê<\v\ 4Á=bÁ~¿f+>=fâ±=bÁ~¿Rç\7f½ªê\9b=fÁ~¿Rç\7f½ªê\9b=fÁ~¿[ë\9b½Cê\7f=bÁ~¿íà±½u)>=hÁ~¿\8f\ 4Á½Ò3ê<bÁ~¿\8f\ 4Á½Ò3ê<bÁ~¿ZºÈ½\a)\1e<dÁ~¿Q»È½+)\1e¼bÁ~¿\r\ 3Á½\ 64ê¼fÁ~¿\r\ 3Á½\ 64ê¼fÁ~¿\16ã±½\99)>½bÁ~¿\é\9b½vê\7f½fÁ~¿·ë\7f½Êê\9b½bÁ~¿·ë\7f½Êê\9b½bÁ~¿E'>½xâ±½dÁ~¿ª-ê¼#\ 4Á½dÁ~¿!<\1e¼çºÈ½bÁ~¿!<\1e¼çºÈ½bÁ~¿#<\1e<éºÈ½bÁ~¿¾-ê<'\ 4Á½dÁ~¿T'>=\82â±½dÁ~¿T'>=\82â±½dÁ~¿×ë\7f=Õê\9b½bÁ~¿~ê\9b=\82ê\7f½dÁ~¿1â±=°)>½dÁ~¿1â±=°)>½dÁ~¿Â\ 4Á=þ3ê¼aÁ~¿_»È=+)\1e¼bÁ~¿qºÈ=M)\1e<dÁ~¿qºÈ=M)\1e<dÁ~¿C\ 5Á=\b4ê<aÁ~¿\16á±=¡)>=fÁ~¿wë\9b=rê\7f=bÁ~¿wë\9b=rê\7f=bÁ~¿\8aç\7f=Äê\9b=fÁ~¿\94#\1e¼ÂºÈ=dÁ~?\94#\1e¼ÂºÈ=dÁ~?\94#\1e¼ÂºÈ=dÁ~?Y1ê¼ñ\ 3Á=dÁ~?{+>½Râ±=bÁ~?s1ê<þ\ 3Á=dÁ~?\8a+>=Yâ±=bÁ~?«è\7f½ªê\9b=dÁ~?«è\7f½ªê\9b=dÁ~?øê\9b½;ê\7f=bÁ~?¯â±½l)>=bÁ~?¾\ 4Á½É3ê<aÁ~?¾\ 4Á½É3ê<aÁ~?qºÈ½+)\1e<dÁ~?{»È½))\1e¼aÁ~?¶\ 3Á½Ü3ê¼dÁ~?¶\ 3Á½Ü3ê¼dÁ~?¬â±½{)>½bÁ~?¾ê\9b½Kê\7f½dÁ~?µè\7f½¬ê\9b½dÁ~?µè\7f½¬ê\9b½dÁ~?\84+>½Vâ±½bÁ~?f1ê¼ü\ 3Á½dÁ~?´&\1e¼½ºÈ½dÁ~?´&\1e¼½ºÈ½dÁ~?='\1e<ÀºÈ½dÁ~?ò1ê<ú\ 3Á½dÁ~?\8a+>=Vâ±½bÁ~?\8a+>=Vâ±½bÁ~?½è\7f=®ê\9b½dÁ~?Ãê\9b=Kê\7f½bÁ~?½â±=t)>½bÁ~?½â±=t)>½bÁ~?Ç\ 3Á=á3ê¼dÁ~?\8c»È=7)\1e¼aÁ~?\82ºÈ=8)\1e<dÁ~?\82ºÈ=8)\1e<dÁ~?Ó\ 4Á=á3ê<aÁ~?>â±=\83)>=dÁ~?Äê\9b=Mê\7f=bÁ~?Äê\9b=Mê\7f=bÁ~?Áè\7f=³ê\9b=dÁ~?ÎÅǽe\8d}?FÈÇ=ÎÅǽe\8d}?FÈÇ=ÎÅǽe\8d}?FÈÇ=ÎÅǽe\8d}?FÈÇ=\ f\eê¼³\90\94>kàt¿\ f\eê¼³\90\94>kàt¿\ f\eê¼³\90\94>kàt¿\ f\eê¼³\90\94>kàt¿e*À½cås?'ø\93>e*À½cås?'ø\93>e*À½cås?'ø\93>e*À½cås?'ø\93>Æê\93¾îÎs?FÈÇ=Æê\93¾îÎs?FÈÇ=Æê\93¾îÎs?FÈÇ=Æê\93¾îÎs?FÈÇ=òV­½\13Û\8e>màt¿òV­½\13Û\8e>màt¿òV­½\13Û\8e>màt¿òV­½\13Û\8e>màt¿£H\8e¾è\85j?Dø\93>£H\8e¾è\85j?Dø\93>£H\8e¾è\85j?Dø\93>£H\8e¾è\85j?Dø\93>\¦\f¾\8eÔç>f\87a¿\¦\f¾\8eÔç>f\87a¿\¦\f¾\8eÔç>f\87a¿\¦\f¾\8eÔç>f\87a¿m7\83¾ûGX?\1csð>m7\83¾ûGX?\1csð>m7\83¾ûGX?\1csð>m7\83¾ûGX?\1csð>E\1e\12Ü\e?g\81E¿E\1e\12Ü\e?g\81E¿E\1e\12Ü\e?g\81E¿E\1e\12Ü\e?g\81E¿o9f¾\99¼=? ï!?o9f¾\99¼=? ï!?o9f¾\99¼=? ï!?o9f¾\99¼=? ï!?s9f¾\9a¼=?\9eï!¿s9f¾\9a¼=?\9eï!¿s9f¾\9a¼=?\9eï!¿s9f¾\9a¼=?\9eï!¿=\1e\10Ü\e?j\81E?=\1e\10Ü\e?j\81E?=\1e\10Ü\e?j\81E?=\1e\10Ü\e?j\81E?j7\83¾øGX?(sð¾j7\83¾øGX?(sð¾j7\83¾øGX?(sð¾j7\83¾øGX?(sð¾[¦\f¾\90Ôç>d\87a?[¦\f¾\90Ôç>d\87a?[¦\f¾\90Ôç>d\87a?[¦\f¾\90Ôç>d\87a?\9fH\8e¾è\85j?Iø\93¾\9fH\8e¾è\85j?Iø\93¾\9fH\8e¾è\85j?Iø\93¾\9fH\8e¾è\85j?Iø\93¾«W­½\ 6Û\8e>kàt?«W­½\ 6Û\8e>kàt?«W­½\ 6Û\8e>kàt?«W­½\ 6Û\8e>kàt?Àê\93¾îÎs?FÈǽÀê\93¾îÎs?FÈǽÀê\93¾îÎs?FÈǽÀê\93¾îÎs?FÈǽ.\8e\99¾\1f¤\ f?i\81E?.\8e\99¾\1f¤\ f?i\81E?.\8e\99¾\1f¤\ f?i\81E?.\8e\99¾\1f¤\ f?i\81E?t\15Õ¾nSG?\1fsð¾t\15Õ¾nSG?\1fsð¾t\15Õ¾nSG?\1fsð¾t\15Õ¾nSG?\1fsð¾xgd¾í§Õ>b\87a?xgd¾í§Õ>b\87a?xgd¾í§Õ>b\87a?xgd¾í§Õ>b\87a?Z\ eç¾<#X?\ø\93¾Z\ eç¾<#X?\ø\93¾Z\ eç¾<#X?\ø\93¾Z\ eç¾<#X?\ø\93¾r¾\f¾\ e¨\83>màt?r¾\f¾\ e¨\83>màt?r¾\f¾\ e¨\83>màt?r¾\f¾\ e¨\83>màt?@4ð¾ï±`?öÇǽ@4ð¾ï±`?öÇǽ@4ð¾ï±`?öÇǽ@4ð¾ï±`?öÇǽ@4ð¾ï±`?öÇÇ=@4ð¾ï±`?öÇÇ=@4ð¾ï±`?öÇÇ=@4ð¾ï±`?öÇÇ=l¾\f¾\17¨\83>màt¿l¾\f¾\17¨\83>màt¿l¾\f¾\17¨\83>màt¿l¾\f¾\17¨\83>màt¿\\ eç¾=#X?Pø\93>\\ eç¾=#X?Pø\93>\\ eç¾=#X?Pø\93>\\ eç¾=#X?Pø\93>Igd¾ë§Õ>d\87a¿Igd¾ë§Õ>d\87a¿Igd¾ë§Õ>d\87a¿Igd¾ë§Õ>d\87a¿~\15Õ¾qSG?\10sð>~\15Õ¾qSG?\10sð>~\15Õ¾qSG?\10sð>~\15Õ¾qSG?\10sð>1\8e\99¾\1e¤\ f?g\81E¿1\8e\99¾\1e¤\ f?g\81E¿1\8e\99¾\1e¤\ f?g\81E¿1\8e\99¾\1e¤\ f?g\81E¿\83ÇÜ.?\9fï!?\83ÇÜ.?\9fï!?\83ÇÜ.?\9fï!?\83ÇÜ.?\9fï!?\81ÆÜ.?¢ï!¿\81ÆÜ.?¢ï!¿\81ÆÜ.?¢ï!¿\81ÆÜ.?¢ï!¿{y\e¿Cr=?Eø\93>{y\e¿Cr=?Eø\93>{y\e¿Cr=?Eø\93>{y\e¿Cr=?Eø\93>z°\99¾gE»>f\87a¿z°\99¾gE»>f\87a¿z°\99¾gE»>f\87a¿z°\99¾gE»>f\87a¿\9ea\ f¿éµ.?!sð>\9ea\ f¿éµ.?!sð>\9ea\ f¿éµ.?!sð>\9ea\ f¿éµ.?!sð>¦¦Î¾\ fÎû>f\81E¿¦¦Î¾\ fÎû>f\81E¿¦¦Î¾\ fÎû>f\81E¿¦¦Î¾\ fÎû>f\81E¿b\91û¾©D\19?\9aï!?b\91û¾©D\19?\9aï!?b\91û¾©D\19?\9aï!?b\91û¾©D\19?\9aï!?b\91û¾¨D\19?\9dï!¿b\91û¾¨D\19?\9dï!¿b\91û¾¨D\19?\9dï!¿b\91û¾¨D\19?\9dï!¿§¦Î¾\aÎû>j\81E?§¦Î¾\aÎû>j\81E?§¦Î¾\aÎû>j\81E?§¦Î¾\aÎû>j\81E?\9da\ f¿éµ.?\1dsð¾\9da\ f¿éµ.?\1dsð¾\9da\ f¿éµ.?\1dsð¾\9da\ f¿éµ.?\1dsð¾g°\99¾iE»>i\87a?g°\99¾iE»>i\87a?g°\99¾iE»>i\87a?g°\99¾iE»>i\87a?zy\e¿?r=?Oø\93¾zy\e¿?r=?Oø\93¾zy\e¿?r=?Oø\93¾zy\e¿?r=?Oø\93¾\14i=¾¨Ëf>kàt?\14i=¾¨Ëf>kàt?\14i=¾¨Ëf>kàt?\14i=¾¨Ëf>kàt?O¡!¿eòD?dÈǽO¡!¿eòD?dÈǽO¡!¿eòD?dÈǽO¡!¿eòD?dÈǽN¡!¿fòD?eÈÇ=N¡!¿fòD?eÈÇ=N¡!¿fòD?eÈÇ=N¡!¿fòD?eÈÇ=\ fi=¾ÅËf>jàt¿\ fi=¾ÅËf>jàt¿\ fi=¾ÅËf>jàt¿\ fi=¾ÅËf>jàt¿]E»¾}°\99>f\87a?]E»¾}°\99>f\87a?]E»¾}°\99>f\87a?]E»¾}°\99>f\87a?Cr=¿{y\e?Bø\93¾Cr=¿{y\e?Bø\93¾Cr=¿{y\e?Bø\93¾Cr=¿{y\e?Bø\93¾vËf¾Ãh=>sàt?vËf¾Ãh=>sàt?vËf¾Ãh=>sàt?vËf¾Ãh=>sàt?hòD¿K¡!?uÈǽhòD¿K¡!?uÈǽhòD¿K¡!?uÈǽhòD¿K¡!?uÈǽhòD¿J¡!?sÈÇ=hòD¿J¡!?sÈÇ=hòD¿J¡!?sÈÇ=hòD¿J¡!?sÈÇ=\9cËf¾Ïh=>oàt¿\9cËf¾Ïh=>oàt¿\9cËf¾Ïh=>oàt¿\9cËf¾Ïh=>oàt¿Br=¿{y\e?Bø\93>Br=¿{y\e?Bø\93>Br=¿{y\e?Bø\93>Br=¿{y\e?Bø\93>kE»¾}°\99>b\87a¿kE»¾}°\99>b\87a¿kE»¾}°\99>b\87a¿kE»¾}°\99>b\87a¿êµ.¿\9da\ f?\esð>êµ.¿\9da\ f?\esð>êµ.¿\9da\ f?\esð>êµ.¿\9da\ f?\esð>\14Îû¾\9d¦Î>g\81E¿\14Îû¾\9d¦Î>g\81E¿\14Îû¾\9d¦Î>g\81E¿\14Îû¾\9d¦Î>g\81E¿¦D\19¿]\91û>\9eï!?¦D\19¿]\91û>\9eï!?¦D\19¿]\91û>\9eï!?¦D\19¿]\91û>\9eï!?¦D\19¿]\91û>\9eï!¿¦D\19¿]\91û>\9eï!¿¦D\19¿]\91û>\9eï!¿¦D\19¿]\91û>\9eï!¿\17Îû¾\98¦Î>i\81E?\17Îû¾\98¦Î>i\81E?\17Îû¾\98¦Î>i\81E?\17Îû¾\98¦Î>i\81E?êµ.¿\97a\ f?*sð¾êµ.¿\97a\ f?*sð¾êµ.¿\97a\ f?*sð¾êµ.¿\97a\ f?*sð¾ô§Õ¾0gd>d\87a¿ô§Õ¾0gd>d\87a¿ô§Õ¾0gd>d\87a¿ô§Õ¾0gd>d\87a¿qSG¿m\15Õ>\1asð>qSG¿m\15Õ>\1asð>qSG¿m\15Õ>\1asð>qSG¿m\15Õ>\1asð>\e¤\ f¿1\8e\99>k\81E¿\e¤\ f¿1\8e\99>k\81E¿\e¤\ f¿1\8e\99>k\81E¿\e¤\ f¿1\8e\99>k\81E¿ÊÜ.¿\81îº>\9bï!?ÊÜ.¿\81îº>\9bï!?ÊÜ.¿\81îº>\9bï!?ÊÜ.¿\81îº>\9bï!?ÊÜ.¿\84îº>\9dï!¿ÊÜ.¿\84îº>\9dï!¿ÊÜ.¿\84îº>\9dï!¿ÊÜ.¿\84îº>\9dï!¿\1a¤\ f¿8\8e\99>k\81E?\1a¤\ f¿8\8e\99>k\81E?\1a¤\ f¿8\8e\99>k\81E?\1a¤\ f¿8\8e\99>k\81E?nSG¿t\15Õ>\1fsð¾nSG¿t\15Õ>\1fsð¾nSG¿t\15Õ>\1fsð¾nSG¿t\15Õ>\1fsð¾÷§Õ¾+gd>b\87a?÷§Õ¾+gd>b\87a?÷§Õ¾+gd>b\87a?÷§Õ¾+gd>b\87a?F#X¿F\ eç>Cø\93¾F#X¿F\ eç>Cø\93¾F#X¿F\ eç>Cø\93¾F#X¿F\ eç>Cø\93¾\15¨\83¾a¾\f>màt?\15¨\83¾a¾\f>màt?\15¨\83¾a¾\f>màt?\15¨\83¾a¾\f>màt?ò±`¿04ð>~Èǽò±`¿04ð>~Èǽò±`¿04ð>~Èǽò±`¿04ð>~Èǽò±`¿.4ð>~ÈÇ=ò±`¿.4ð>~ÈÇ=ò±`¿.4ð>~ÈÇ=ò±`¿.4ð>~ÈÇ=\12¨\83¾i¾\f>màt¿\12¨\83¾i¾\f>màt¿\12¨\83¾i¾\f>màt¿\12¨\83¾i¾\f>màt¿G#X¿G\ eç>=ø\93>G#X¿G\ eç>=ø\93>G#X¿G\ eç>=ø\93>G#X¿G\ eç>=ø\93\85j¿\9fH\8e>Aø\93¾é\85j¿\9fH\8e>Aø\93¾é\85j¿\9fH\8e>Aø\93¾é\85j¿\9fH\8e>Aø\93¾ÿÚ\8e¾ÉV­=qàt?ÿÚ\8e¾ÉV­=qàt?ÿÚ\8e¾ÉV­=qàt?ÿÚ\8e¾ÉV­=qàt?îÎs¿¼ê\93>\90ÈǽîÎs¿¼ê\93>\90ÈǽîÎs¿¼ê\93>\90ÈǽîÎs¿¼ê\93>\90ÈǽîÎs¿¼ê\93>\90ÈÇ=îÎs¿¼ê\93>\90ÈÇ=îÎs¿¼ê\93>\90ÈÇ=îÎs¿¼ê\93>\90ÈÇ=\1eÛ\8e¾ÅV­=kàt¿\1eÛ\8e¾ÅV­=kàt¿\1eÛ\8e¾ÅV­=kàt¿\1eÛ\8e¾ÅV­=kàt¿é\85j¿\9fH\8e>@ø\93\85j¿\9fH\8e>@ø\93\85j¿\9fH\8e>@ø\93\85j¿\9fH\8e>@ø\93>\92Ôç¾N¦\f>f\87a¿\92Ôç¾N¦\f>f\87a¿\92Ôç¾N¦\f>f\87a¿\92Ôç¾N¦\f>f\87a¿ÿGX¿b7\83>\13sð>ÿGX¿b7\83>\13sð>ÿGX¿b7\83>\13sð>ÿGX¿b7\83>\13sð>\10Ü\e¿1\1e=>i\81E¿\10Ü\e¿1\1e=>i\81E¿\10Ü\e¿1\1e=>i\81E¿\10Ü\e¿1\1e=>i\81E¿\9d¼=¿H9f>\9fï!?\9d¼=¿H9f>\9fï!?\9d¼=¿H9f>\9fï!?\9d¼=¿H9f>\9fï!?\9d¼=¿H9f>\9fï!¿\9d¼=¿H9f>\9fï!¿\9d¼=¿H9f>\9fï!¿\9d¼=¿H9f>\9fï!¿\11Ü\e¿>\1e=>g\81E?\11Ü\e¿>\1e=>g\81E?\11Ü\e¿>\1e=>g\81E?\11Ü\e¿>\1e=>g\81E?ÿGX¿[7\83>\19sð¾ÿGX¿[7\83>\19sð¾ÿGX¿[7\83>\19sð¾ÿGX¿[7\83>\19sð¾\8bÔç¾Z¦\f>f\87a?\8bÔç¾Z¦\f>f\87a?\8bÔç¾Z¦\f>f\87a?\8bÔç¾Z¦\f>f\87a?Ìì`¿\9b9±=\18sð>Ìì`¿\9b9±=\18sð>Ìì`¿\9b9±=\18sð>Ìì`¿\9b9±=\18sð>²\16"¿ðm\7f=f\81E¿²\16"¿ðm\7f=f\81E¿²\16"¿ðm\7f=f\81E¿²\16"¿ðm\7f=f\81E¿ÒQE¿yy\9b=\9fï!?ÒQE¿yy\9b=\9fï!?ÒQE¿yy\9b=\9fï!?ÒQE¿yy\9b=\9fï!?ÒQE¿yy\9b=\9fï!¿ÒQE¿yy\9b=\9fï!¿ÒQE¿yy\9b=\9fï!¿ÒQE¿yy\9b=\9fï!¿²\16"¿\95m\7f=f\81E?²\16"¿\95m\7f=f\81E?²\16"¿\95m\7f=f\81E?²\16"¿\95m\7f=f\81E?Çì`¿¢9±=#sð¾Çì`¿¢9±=#sð¾Çì`¿¢9±=#sð¾Çì`¿¢9±=#sð¾\7f\18ñ¾\r÷==a\87a?\7f\18ñ¾\r÷==a\87a?\7f\18ñ¾\r÷==a\87a?\7f\18ñ¾\r÷==a\87a?Zås¿:,À==ø\93¾Zås¿:,À==ø\93¾Zås¿:,À==ø\93¾Zås¿:,À==ø\93¾\9b\90\94¾Æ\1dê<oàt?\9b\90\94¾Æ\1dê<oàt?\9b\90\94¾Æ\1dê<oàt?\9b\90\94¾Æ\1dê<oàt?]\8d}¿óÇÇ=´Èǽ]\8d}¿óÇÇ=´Èǽ]\8d}¿óÇÇ=´Èǽ]\8d}¿óÇÇ=´Èǽ]\8d}¿óÇÇ=´ÈÇ=]\8d}¿óÇÇ=´ÈÇ=]\8d}¿óÇÇ=´ÈÇ=]\8d}¿óÇÇ=´ÈÇ=®\90\94¾\19\1eê<kàt¿®\90\94¾\19\1eê<kàt¿®\90\94¾\19\1eê<kàt¿®\90\94¾\19\1eê<kàt¿Zås¿:,À=<ø\93>Zås¿:,À=<ø\93>Zås¿:,À=<ø\93>Zås¿:,À=<ø\93>q\18ñ¾\7f÷==d\87a¿q\18ñ¾\7f÷==d\87a¿q\18ñ¾\7f÷==d\87a¿q\18ñ¾\7f÷==d\87a¿\9b\90\94¾#\1eê¼oàt?\9b\90\94¾#\1eê¼oàt?\9b\90\94¾#\1eê¼oàt?\9b\90\94¾#\1eê¼oàt?[\8d}¿(ÈǽÛÈǽ[\8d}¿(ÈǽÛÈǽ[\8d}¿(ÈǽÛÈǽ[\8d}¿(ÈǽÛÈǽ[\8d}¿(ÈǽÛÈÇ=[\8d}¿(ÈǽÛÈÇ=[\8d}¿(ÈǽÛÈÇ=[\8d}¿(ÈǽÛÈÇ=®\90\94¾\1d\1eê¼kàt¿®\90\94¾\1d\1eê¼kàt¿®\90\94¾\1d\1eê¼kàt¿®\90\94¾\1d\1eê¼kàt¿Zås¿q,À½7ø\93>Zås¿q,À½7ø\93>Zås¿q,À½7ø\93>Zås¿q,À½7ø\93>n\18ñ¾¸÷=½f\87a¿n\18ñ¾¸÷=½f\87a¿n\18ñ¾¸÷=½f\87a¿n\18ñ¾¸÷=½f\87a¿Êì`¿Õ9±½\14sð>Êì`¿Õ9±½\14sð>Êì`¿Õ9±½\14sð>Êì`¿Õ9±½\14sð>­\16"¿ým\7f½k\81E¿­\16"¿ým\7f½k\81E¿­\16"¿ým\7f½k\81E¿­\16"¿ým\7f½k\81E¿ÕQE¿wy\9b½\9dï!?ÕQE¿wy\9b½\9dï!?ÕQE¿wy\9b½\9dï!?ÕQE¿wy\9b½\9dï!?ÕQE¿wy\9b½\9dï!¿ÕQE¿wy\9b½\9dï!¿ÕQE¿wy\9b½\9dï!¿ÕQE¿wy\9b½\9dï!¿¬\16"¿Bn\7f½k\81E?¬\16"¿Bn\7f½k\81E?¬\16"¿Bn\7f½k\81E?¬\16"¿Bn\7f½k\81E?Çì`¿¢9±½#sð¾Çì`¿¢9±½#sð¾Çì`¿¢9±½#sð¾Çì`¿¢9±½#sð¾s\18ñ¾H÷=½d\87a?s\18ñ¾H÷=½d\87a?s\18ñ¾H÷=½d\87a?s\18ñ¾H÷=½d\87a?Zås¿p,À½6ø\93¾Zås¿p,À½6ø\93¾Zås¿p,À½6ø\93¾Zås¿p,À½6ø\93¾\13Ü\e¿4\1e=¾i\81E¿\13Ü\e¿4\1e=¾i\81E¿\13Ü\e¿4\1e=¾i\81E¿\13Ü\e¿4\1e=¾i\81E¿\9e¼=¿h9f¾\9aï!?\9e¼=¿h9f¾\9aï!?\9e¼=¿h9f¾\9aï!?\9e¼=¿h9f¾\9aï!?\9e¼=¿h9f¾\9aï!¿\9e¼=¿h9f¾\9aï!¿\9e¼=¿h9f¾\9aï!¿\9e¼=¿h9f¾\9aï!¿\ eÜ\e¿B\1e=¾j\81E?\ eÜ\e¿B\1e=¾j\81E?\ eÜ\e¿B\1e=¾j\81E?\ eÜ\e¿B\1e=¾j\81E?øGX¿i7\83¾(sð¾øGX¿i7\83¾(sð¾øGX¿i7\83¾(sð¾øGX¿i7\83¾(sð¾\91Ôç¾g¦\f¾b\87a?\91Ôç¾g¦\f¾b\87a?\91Ôç¾g¦\f¾b\87a?\91Ôç¾g¦\f¾b\87a?ë\85j¿\9fH\8e¾3ø\93¾ë\85j¿\9fH\8e¾3ø\93¾ë\85j¿\9fH\8e¾3ø\93¾ë\85j¿\9fH\8e¾3ø\93¾\17Û\8e¾àV­½kàt?\17Û\8e¾àV­½kàt?\17Û\8e¾àV­½kàt?\17Û\8e¾àV­½kàt?ìÎs¿»ê\93¾àÈǽìÎs¿»ê\93¾àÈǽìÎs¿»ê\93¾àÈǽìÎs¿»ê\93¾àÈǽìÎs¿»ê\93¾àÈÇ=ìÎs¿»ê\93¾àÈÇ=ìÎs¿»ê\93¾àÈÇ=ìÎs¿»ê\93¾àÈÇ=\ 1Û\8e¾ùV­½oàt¿\ 1Û\8e¾ùV­½oàt¿\ 1Û\8e¾ùV­½oàt¿\ 1Û\8e¾ùV­½oàt¿é\85j¿¡H\8e¾6ø\93\85j¿¡H\8e¾6ø\93\85j¿¡H\8e¾6ø\93\85j¿¡H\8e¾6ø\93>\8eÔç¾\¦\f¾f\87a¿\8eÔç¾\¦\f¾f\87a¿\8eÔç¾\¦\f¾f\87a¿\8eÔç¾\¦\f¾f\87a¿þGX¿c7\83¾\1csð>þGX¿c7\83¾\1csð>þGX¿c7\83¾\1csð>þGX¿c7\83¾\1csð>ì±`¿?4ð¾ÛÈǽì±`¿?4ð¾ÛÈǽì±`¿?4ð¾ÛÈǽì±`¿?4ð¾ÛÈǽì±`¿=4ð¾ÙÈÇ=ì±`¿=4ð¾ÙÈÇ=ì±`¿=4ð¾ÙÈÇ=ì±`¿=4ð¾ÙÈÇ=,¨\83¾n¾\f¾jàt¿,¨\83¾n¾\f¾jàt¿,¨\83¾n¾\f¾jàt¿,¨\83¾n¾\f¾jàt¿B#X¿c\ eç¾4ø\93>B#X¿c\ eç¾4ø\93>B#X¿c\ eç¾4ø\93>B#X¿c\ eç¾4ø\93>ó§Õ¾5gd¾d\87a¿ó§Õ¾5gd¾d\87a¿ó§Õ¾5gd¾d\87a¿ó§Õ¾5gd¾d\87a¿mSG¿x\15Õ¾\1dsð>mSG¿x\15Õ¾\1dsð>mSG¿x\15Õ¾\1dsð>mSG¿x\15Õ¾\1dsð> ¤\ f¿?\8e\99¾e\81E¿ ¤\ f¿?\8e\99¾e\81E¿ ¤\ f¿?\8e\99¾e\81E¿ ¤\ f¿?\8e\99¾e\81E¿ÈÜ.¿\93\98ï!?ÈÜ.¿\93\98ï!?ÈÜ.¿\93\98ï!?ÈÜ.¿\93\98ï!?ÈÜ.¿\93\9aï!¿ÈÜ.¿\93\9aï!¿ÈÜ.¿\93\9aï!¿ÈÜ.¿\93\9aï!¿\1a¤\ f¿1\8e\99¾k\81E?\1a¤\ f¿1\8e\99¾k\81E?\1a¤\ f¿1\8e\99¾k\81E?\1a¤\ f¿1\8e\99¾k\81E?kSG¿t\15Õ¾+sð¾kSG¿t\15Õ¾+sð¾kSG¿t\15Õ¾+sð¾kSG¿t\15Õ¾+sð¾ð§Õ¾0gd¾d\87a?ð§Õ¾0gd¾d\87a?ð§Õ¾0gd¾d\87a?ð§Õ¾0gd¾d\87a?B#X¿c\ eç¾2ø\93¾B#X¿c\ eç¾2ø\93¾B#X¿c\ eç¾2ø\93¾B#X¿c\ eç¾2ø\93¾\ 2¨\83¾e¾\f¾qàt?\ 2¨\83¾e¾\f¾qàt?\ 2¨\83¾e¾\f¾qàt?\ 2¨\83¾e¾\f¾qàt?§D\19¿k\91û¾\9aï!?§D\19¿k\91û¾\9aï!?§D\19¿k\91û¾\9aï!?§D\19¿k\91û¾\9aï!?¤D\19¿l\91û¾\9cï!¿¤D\19¿l\91û¾\9cï!¿¤D\19¿l\91û¾\9cï!¿¤D\19¿l\91û¾\9cï!¿\ 6Îû¾¨¦Î¾k\81E?\ 6Îû¾¨¦Î¾k\81E?\ 6Îû¾¨¦Î¾k\81E?\ 6Îû¾¨¦Î¾k\81E?çµ.¿\9ba\ f¿-sð¾çµ.¿\9ba\ f¿-sð¾çµ.¿\9ba\ f¿-sð¾çµ.¿\9ba\ f¿-sð¾kE»¾\84°\99¾b\87a?kE»¾\84°\99¾b\87a?kE»¾\84°\99¾b\87a?kE»¾\84°\99¾b\87a?Hr=¿vy\e¿8ø\93¾Hr=¿vy\e¿8ø\93¾Hr=¿vy\e¿8ø\93¾Hr=¿vy\e¿8ø\93¾\0Ìf¾¾h=¾jàt?\0Ìf¾¾h=¾jàt?\0Ìf¾¾h=¾jàt?\0Ìf¾¾h=¾jàt?bòD¿P¡!¿ËÈǽbòD¿P¡!¿ËÈǽbòD¿P¡!¿ËÈǽbòD¿P¡!¿ËÈǽbòD¿P¡!¿ËÈÇ=bòD¿P¡!¿ËÈÇ=bòD¿P¡!¿ËÈÇ=bòD¿P¡!¿ËÈÇ=ËËf¾Ðh=¾kàt¿ËËf¾Ðh=¾kàt¿ËËf¾Ðh=¾kàt¿ËËf¾Ðh=¾kàt¿Hr=¿wy\e¿5ø\93>Hr=¿wy\e¿5ø\93>Hr=¿wy\e¿5ø\93>Hr=¿wy\e¿5ø\93>^E»¾{°\99¾g\87a¿^E»¾{°\99¾g\87a¿^E»¾{°\99¾g\87a¿^E»¾{°\99¾g\87a¿ëµ.¿\9ba\ f¿"sð>ëµ.¿\9ba\ f¿"sð>ëµ.¿\9ba\ f¿"sð>ëµ.¿\9ba\ f¿"sð>\13Îû¾¦¦Î¾f\81E¿\13Îû¾¦¦Î¾f\81E¿\13Îû¾¦¦Î¾f\81E¿\13Îû¾¦¦Î¾f\81E¿O¡!¿dòD¿¶ÈÇ=O¡!¿dòD¿¶ÈÇ=O¡!¿dòD¿¶ÈÇ=O¡!¿dòD¿¶ÈÇ=¿h=¾ÒËf¾màt¿¿h=¾ÒËf¾màt¿¿h=¾ÒËf¾màt¿¿h=¾ÒËf¾màt¿zy\e¿Fr=¿8ø\93>zy\e¿Fr=¿8ø\93>zy\e¿Fr=¿8ø\93>zy\e¿Fr=¿8ø\93>z°\99¾lE»¾d\87a¿z°\99¾lE»¾d\87a¿z°\99¾lE»¾d\87a¿z°\99¾lE»¾d\87a¿\9ba\ f¿çµ.¿,sð>\9ba\ f¿çµ.¿,sð>\9ba\ f¿çµ.¿,sð>\9ba\ f¿çµ.¿,sð>¯¦Î¾\fÎû¾e\81E¿¯¦Î¾\fÎû¾e\81E¿¯¦Î¾\fÎû¾e\81E¿¯¦Î¾\fÎû¾e\81E¿\\91û¾ªD\19¿\9cï!?\\91û¾ªD\19¿\9cï!?\\91û¾ªD\19¿\9cï!?\\91û¾ªD\19¿\9cï!?]\91û¾ªD\19¿\9aï!¿]\91û¾ªD\19¿\9aï!¿]\91û¾ªD\19¿\9aï!¿]\91û¾ªD\19¿\9aï!¿\9f¦Î¾\fÎû¾j\81E?\9f¦Î¾\fÎû¾j\81E?\9f¦Î¾\fÎû¾j\81E?\9f¦Î¾\fÎû¾j\81E?\98a\ f¿çµ.¿3sð¾\98a\ f¿çµ.¿3sð¾\98a\ f¿çµ.¿3sð¾\98a\ f¿çµ.¿3sð¾b°\99¾rE»¾g\87a?b°\99¾rE»¾g\87a?b°\99¾rE»¾g\87a?b°\99¾rE»¾g\87a?zy\e¿Fr=¿8ø\93¾zy\e¿Fr=¿8ø\93¾zy\e¿Fr=¿8ø\93¾zy\e¿Fr=¿8ø\93¾Äh=¾»Ëf¾màt?Äh=¾»Ëf¾màt?Äh=¾»Ëf¾màt?Äh=¾»Ëf¾màt?O¡!¿dòD¿¶ÈǽO¡!¿dòD¿¶ÈǽO¡!¿dòD¿¶ÈǽO¡!¿dòD¿¶Èǽ/\8e\99¾\1f¤\ f¿g\81E?/\8e\99¾\1f¤\ f¿g\81E?/\8e\99¾\1f¤\ f¿g\81E?/\8e\99¾\1f¤\ f¿g\81E?b\15Õ¾mSG¿3sð¾b\15Õ¾mSG¿3sð¾b\15Õ¾mSG¿3sð¾b\15Õ¾mSG¿3sð¾\19gd¾ù§Õ¾d\87a?\19gd¾ù§Õ¾d\87a?\19gd¾ù§Õ¾d\87a?\19gd¾ù§Õ¾d\87a?S\ eç¾D#X¿7ø\93¾S\ eç¾D#X¿7ø\93¾S\ eç¾D#X¿7ø\93¾S\ eç¾D#X¿7ø\93¾y¾\f¾\ f¨\83¾màt?y¾\f¾\ f¨\83¾màt?y¾\f¾\ f¨\83¾màt?y¾\f¾\ f¨\83¾màt?/4ð¾ñ±`¿ËÈǽ/4ð¾ñ±`¿ËÈǽ/4ð¾ñ±`¿ËÈǽ/4ð¾ñ±`¿ËÈǽ14ð¾ñ±`¿ËÈÇ=14ð¾ñ±`¿ËÈÇ=14ð¾ñ±`¿ËÈÇ=14ð¾ñ±`¿ËÈÇ=y¾\f¾\1e¨\83¾kàt¿y¾\f¾\1e¨\83¾kàt¿y¾\f¾\1e¨\83¾kàt¿y¾\f¾\1e¨\83¾kàt¿Q\ eç¾D#X¿>ø\93>Q\ eç¾D#X¿>ø\93>Q\ eç¾D#X¿>ø\93>Q\ eç¾D#X¿>ø\93>Ngd¾î§Õ¾b\87a¿Ngd¾î§Õ¾b\87a¿Ngd¾î§Õ¾b\87a¿Ngd¾î§Õ¾b\87a¿f\15Õ¾nSG¿+sð>f\15Õ¾nSG¿+sð>f\15Õ¾nSG¿+sð>f\15Õ¾nSG¿+sð>5\8e\99¾\1f¤\ f¿g\81E¿5\8e\99¾\1f¤\ f¿g\81E¿5\8e\99¾\1f¤\ f¿g\81E¿5\8e\99¾\1f¤\ f¿g\81E¿\82ÌÜ.¿\99ï!?\82ÌÜ.¿\99ï!?\82ÌÜ.¿\99ï!?\82ÌÜ.¿\99ï!?{ËÜ.¿\9aï!¿{ËÜ.¿\9aï!¿{ËÜ.¿\9aï!¿{ËÜ.¿\9aï!¿­W­½\13Û\8e¾jàt¿­W­½\13Û\8e¾jàt¿­W­½\13Û\8e¾jàt¿­W­½\13Û\8e¾jàt¿\98H\8e¾í\85j¿2ø\93>\98H\8e¾í\85j¿2ø\93>\98H\8e¾í\85j¿2ø\93>\98H\8e¾í\85j¿2ø\93>^¦\f¾\8bÔç¾f\87a¿^¦\f¾\8bÔç¾f\87a¿^¦\f¾\8bÔç¾f\87a¿^¦\f¾\8bÔç¾f\87a¿V7\83¾ùGX¿0sð>V7\83¾ùGX¿0sð>V7\83¾ùGX¿0sð>V7\83¾ùGX¿0sð>:\1e\18Ü\e¿c\81E¿:\1e\18Ü\e¿c\81E¿:\1e\18Ü\e¿c\81E¿:\1e\18Ü\e¿c\81E¿_9f¾ ¼=¿\97ï!?_9f¾ ¼=¿\97ï!?_9f¾ ¼=¿\97ï!?_9f¾ ¼=¿\97ï!?_9f¾ ¼=¿\97ï!¿_9f¾ ¼=¿\97ï!¿_9f¾ ¼=¿\97ï!¿_9f¾ ¼=¿\97ï!¿0\1e\11Ü\e¿i\81E?0\1e\11Ü\e¿i\81E?0\1e\11Ü\e¿i\81E?0\1e\11Ü\e¿i\81E?U7\83¾øGX¿3sð¾U7\83¾øGX¿3sð¾U7\83¾øGX¿3sð¾U7\83¾øGX¿3sð¾_¦\f¾\93Ôç¾d\87a?_¦\f¾\93Ôç¾d\87a?_¦\f¾\93Ôç¾d\87a?_¦\f¾\93Ôç¾d\87a?\99H\8e¾í\85j¿2ø\93¾\99H\8e¾í\85j¿2ø\93¾\99H\8e¾í\85j¿2ø\93¾\99H\8e¾í\85j¿2ø\93¾\ 2W­½\rÛ\8e¾màt?\ 2W­½\rÛ\8e¾màt?\ 2W­½\rÛ\8e¾màt?\ 2W­½\rÛ\8e¾màt?·ê\93¾ìÎs¿&Éǽ·ê\93¾ìÎs¿&Éǽ·ê\93¾ìÎs¿&Éǽ·ê\93¾ìÎs¿&Éǽ·ê\93¾ìÎs¿&ÉÇ=·ê\93¾ìÎs¿&ÉÇ=·ê\93¾ìÎs¿&ÉÇ=·ê\93¾ìÎs¿&ÉÇ=£9±½Äì`¿-sð¾£9±½Äì`¿-sð¾£9±½Äì`¿-sð¾£9±½Äì`¿-sð¾ó÷=½r\18ñ¾b\87a?ó÷=½r\18ñ¾b\87a?ó÷=½r\18ñ¾b\87a?ó÷=½r\18ñ¾b\87a??,À½Xås¿Bø\93¾?,À½Xås¿Bø\93¾?,À½Xås¿Bø\93¾?,À½Xås¿Bø\93¾þ\1aê¼\9f\90\94¾oàt?þ\1aê¼\9f\90\94¾oàt?þ\1aê¼\9f\90\94¾oàt?þ\1aê¼\9f\90\94¾oàt?ãÇǽ]\8d}¿ÇÈǽãÇǽ]\8d}¿ÇÈǽãÇǽ]\8d}¿ÇÈǽãÇǽ]\8d}¿ÇÈǽãÇǽ]\8d}¿ÇÈÇ=ãÇǽ]\8d}¿ÇÈÇ=ãÇǽ]\8d}¿ÇÈÇ=ãÇǽ]\8d}¿ÇÈÇ=ý\1aê¼®\90\94¾kàt¿ý\1aê¼®\90\94¾kàt¿ý\1aê¼®\90\94¾kàt¿ý\1aê¼®\90\94¾kàt¿G,À½Xås¿Bø\93>G,À½Xås¿Bø\93>G,À½Xås¿Bø\93>G,À½Xås¿Bø\93>\14÷=½k\18ñ¾g\87a¿\14÷=½k\18ñ¾g\87a¿\14÷=½k\18ñ¾g\87a¿\14÷=½k\18ñ¾g\87a¿«9±½Çì`¿#sð>«9±½Çì`¿#sð>«9±½Çì`¿#sð>«9±½Çì`¿#sð>úm\7f½±\16"¿i\81E¿úm\7f½±\16"¿i\81E¿úm\7f½±\16"¿i\81E¿úm\7f½±\16"¿i\81E¿<y\9b½ÛQE¿\95ï!?<y\9b½ÛQE¿\95ï!?<y\9b½ÛQE¿\95ï!?<y\9b½ÛQE¿\95ï!?<y\9b½ÛQE¿\95ï!¿<y\9b½ÛQE¿\95ï!¿<y\9b½ÛQE¿\95ï!¿<y\9b½ÛQE¿\95ï!¿\9am\7f½¬\16"¿l\81E?\9am\7f½¬\16"¿l\81E?\9am\7f½¬\16"¿l\81E?\9am\7f½¬\16"¿l\81E?l,À=Xås¿<ø\93>l,À=Xås¿<ø\93>l,À=Xås¿<ø\93>l,À=Xås¿<ø\93>\11÷==f\18ñ¾i\87a¿\11÷==f\18ñ¾i\87a¿\11÷==f\18ñ¾i\87a¿\11÷==f\18ñ¾i\87a¿Â9±=Âì`¿4sð>Â9±=Âì`¿4sð>Â9±=Âì`¿4sð>Â9±=Âì`¿4sð>\ 1n\7f\16"¿a\81E¿\ 1n\7f\16"¿a\81E¿\ 1n\7f\16"¿a\81E¿\ 1n\7f\16"¿a\81E¿\83y\9b=ÞQE¿\8fï!?\83y\9b=ÞQE¿\8fï!?\83y\9b=ÞQE¿\8fï!?\83y\9b=ÞQE¿\8fï!?\83y\9b=ÞQE¿\8fï!¿\83y\9b=ÞQE¿\8fï!¿\83y\9b=ÞQE¿\8fï!¿\83y\9b=ÞQE¿\8fï!¿An\7f\16"¿i\81E?An\7f\16"¿i\81E?An\7f\16"¿i\81E?An\7f\16"¿i\81E?¾9±=¿ì`¿Asð¾¾9±=¿ì`¿Asð¾¾9±=¿ì`¿Asð¾¾9±=¿ì`¿Asð¾\18÷==w\18ñ¾b\87a?\18÷==w\18ñ¾b\87a?\18÷==w\18ñ¾b\87a?\18÷==w\18ñ¾b\87a?l,À=Xås¿<ø\93¾l,À=Xås¿<ø\93¾l,À=Xås¿<ø\93¾l,À=Xås¿<ø\93¾Ó\1dê<\9d\90\94¾oàt?Ó\1dê<\9d\90\94¾oàt?Ó\1dê<\9d\90\94¾oàt?Ó\1dê<\9d\90\94¾oàt?6ÈÇ=[\8d}¿ÕÈǽ6ÈÇ=[\8d}¿ÕÈǽ6ÈÇ=[\8d}¿ÕÈǽ6ÈÇ=[\8d}¿ÕÈǽ4ÈÇ=[\8d}¿ÕÈÇ=4ÈÇ=[\8d}¿ÕÈÇ=4ÈÇ=[\8d}¿ÕÈÇ=4ÈÇ=[\8d}¿ÕÈÇ=Î\1dê<«\90\94¾kàt¿Î\1dê<«\90\94¾kàt¿Î\1dê<«\90\94¾kàt¿Î\1dê<«\90\94¾kàt¿^¦\f>\8eÔç¾d\87a?^¦\f>\8eÔç¾d\87a?^¦\f>\8eÔç¾d\87a?^¦\f>\8eÔç¾d\87a?\9fH\8e\85j¿#ø\93¾\9fH\8e\85j¿#ø\93¾\9fH\8e\85j¿#ø\93¾\9fH\8e\85j¿#ø\93¾\ 2W­=\vÛ\8e¾màt?\ 2W­=\vÛ\8e¾màt?\ 2W­=\vÛ\8e¾màt?\ 2W­=\vÛ\8e¾màt?Âê\93>ìÎs¿\fÉǽÂê\93>ìÎs¿\fÉǽÂê\93>ìÎs¿\fÉǽÂê\93>ìÎs¿\fÉǽÂê\93>ìÎs¿\fÉÇ=Âê\93>ìÎs¿\fÉÇ=Âê\93>ìÎs¿\fÉÇ=Âê\93>ìÎs¿\fÉÇ=\ 2W­=\18Û\8e¾kàt¿\ 2W­=\18Û\8e¾kàt¿\ 2W­=\18Û\8e¾kàt¿\ 2W­=\18Û\8e¾kàt¿\9fH\8e\85j¿#ø\93>\9fH\8e\85j¿#ø\93>\9fH\8e\85j¿#ø\93>\9fH\8e\85j¿#ø\93>\¦\f>\8aÔç¾g\87a¿\¦\f>\8aÔç¾g\87a¿\¦\f>\8aÔç¾g\87a¿\¦\f>\8aÔç¾g\87a¿h7\83>ôGX¿;sð>h7\83>ôGX¿;sð>h7\83>ôGX¿;sð>h7\83>ôGX¿;sð>:\1e=>\15Ü\e¿e\81E¿:\1e=>\15Ü\e¿e\81E¿:\1e=>\15Ü\e¿e\81E¿:\1e=>\15Ü\e¿e\81E¿u9f> ¼=¿\96ï!?u9f> ¼=¿\96ï!?u9f> ¼=¿\96ï!?u9f> ¼=¿\96ï!?u9f> ¼=¿\96ï!¿u9f> ¼=¿\96ï!¿u9f> ¼=¿\96ï!¿u9f> ¼=¿\96ï!¿G\1e=>\12Ü\e¿g\81E?G\1e=>\12Ü\e¿g\81E?G\1e=>\12Ü\e¿g\81E?G\1e=>\12Ü\e¿g\81E?e7\83>ïGX¿Msð¾e7\83>ïGX¿Msð¾e7\83>ïGX¿Msð¾e7\83>ïGX¿Msð¾Hgd>å§Õ¾g\87a¿Hgd>å§Õ¾g\87a¿Hgd>å§Õ¾g\87a¿Hgd>å§Õ¾g\87a¿r\15Õ>cSG¿Csð>r\15Õ>cSG¿Csð>r\15Õ>cSG¿Csð>r\15Õ>cSG¿Csð>E\8e\99>"¤\ f¿c\81E¿E\8e\99>"¤\ f¿c\81E¿E\8e\99>"¤\ f¿c\81E¿E\8e\99>"¤\ f¿c\81E¿\9eîº>ÒÜ.¿\8bï!?\9eîº>ÒÜ.¿\8bï!?\9eîº>ÒÜ.¿\8bï!?\9eîº>ÒÜ.¿\8bï!?\9eîº>ÒÜ.¿\8bï!¿\9eîº>ÒÜ.¿\8bï!¿\9eîº>ÒÜ.¿\8bï!¿\9eîº>ÒÜ.¿\8bï!¿3\8e\99>\1e¤\ f¿i\81E?3\8e\99>\1e¤\ f¿i\81E?3\8e\99>\1e¤\ f¿i\81E?3\8e\99>\1e¤\ f¿i\81E?l\15Õ>`SG¿Tsð¾l\15Õ>`SG¿Tsð¾l\15Õ>`SG¿Tsð¾l\15Õ>`SG¿Tsð¾\85gd>ï§Õ¾a\87a?\85gd>ï§Õ¾a\87a?\85gd>ï§Õ¾a\87a?\85gd>ï§Õ¾a\87a?b\ eç>D#X¿&ø\93¾b\ eç>D#X¿&ø\93¾b\ eç>D#X¿&ø\93¾b\ eç>D#X¿&ø\93¾×¾\f>\r¨\83¾kàt?×¾\f>\r¨\83¾kàt?×¾\f>\r¨\83¾kàt?×¾\f>\r¨\83¾kàt??4ð>ì±`¿\vÉǽ?4ð>ì±`¿\vÉǽ?4ð>ì±`¿\vÉǽ?4ð>ì±`¿\vÉǽ?4ð>ì±`¿\vÉÇ=?4ð>ì±`¿\vÉÇ=?4ð>ì±`¿\vÉÇ=?4ð>ì±`¿\vÉÇ=Ö¾\f>\1c¨\83¾hàt¿Ö¾\f>\1c¨\83¾hàt¿Ö¾\f>\1c¨\83¾hàt¿Ö¾\f>\1c¨\83¾hàt¿]\ eç>D#X¿$ø\93>]\ eç>D#X¿$ø\93>]\ eç>D#X¿$ø\93>]\ eç>D#X¿$ø\93>\84y\e?>r=¿.ø\93¾\84y\e?>r=¿.ø\93¾\84y\e?>r=¿.ø\93¾\84y\e?>r=¿.ø\93¾Ëh=>½Ëf¾màt?Ëh=>½Ëf¾màt?Ëh=>½Ëf¾màt?Ëh=>½Ëf¾màt?S¡!?`òD¿ãÈǽS¡!?`òD¿ãÈǽS¡!?`òD¿ãÈǽS¡!?`òD¿ãÈǽS¡!?`òD¿ãÈÇ=S¡!?`òD¿ãÈÇ=S¡!?`òD¿ãÈÇ=S¡!?`òD¿ãÈÇ=Èh=>ØËf¾kàt¿Èh=>ØËf¾kàt¿Èh=>ØËf¾kàt¿Èh=>ØËf¾kàt¿\83y\e?=r=¿3ø\93>\83y\e?=r=¿3ø\93>\83y\e?=r=¿3ø\93>\83y\e?=r=¿3ø\93>u°\99>^E»¾i\87a¿u°\99>^E»¾i\87a¿u°\99>^E»¾i\87a¿u°\99>^E»¾i\87a¿\99a\ f?âµ.¿=sð>\99a\ f?âµ.¿=sð>\99a\ f?âµ.¿=sð>\99a\ f?âµ.¿=sð>§¦Î>\1aÎû¾c\81E¿§¦Î>\1aÎû¾c\81E¿§¦Î>\1aÎû¾c\81E¿§¦Î>\1aÎû¾c\81E¿t\91û>®D\19¿\8fï!?t\91û>®D\19¿\8fï!?t\91û>®D\19¿\8fï!?t\91û>®D\19¿\8fï!?t\91û>®D\19¿\8fï!¿t\91û>®D\19¿\8fï!¿t\91û>®D\19¿\8fï!¿t\91û>®D\19¿\8fï!¿ ¦Î>\aÎû¾k\81E? ¦Î>\aÎû¾k\81E? ¦Î>\aÎû¾k\81E? ¦Î>\aÎû¾k\81E?\98a\ f?àµ.¿Fsð¾\98a\ f?àµ.¿Fsð¾\98a\ f?àµ.¿Fsð¾\98a\ f?àµ.¿Fsð¾o°\99>gE»¾g\87a?o°\99>gE»¾g\87a?o°\99>gE»¾g\87a?o°\99>gE»¾g\87a?âµ.?\98a\ f¿Asð>âµ.?\98a\ f¿Asð>âµ.?\98a\ f¿Asð>âµ.?\98a\ f¿Asð>\15Îû>\9e¦Î¾i\81E¿\15Îû>\9e¦Î¾i\81E¿\15Îû>\9e¦Î¾i\81E¿\15Îû>\9e¦Î¾i\81E¿¯D\19?n\91û¾\92ï!?¯D\19?n\91û¾\92ï!?¯D\19?n\91û¾\92ï!?¯D\19?n\91û¾\92ï!?­D\19?j\91û¾\94ï!¿­D\19?j\91û¾\94ï!¿­D\19?j\91û¾\94ï!¿­D\19?j\91û¾\94ï!¿\14Îû>\97¦Î¾j\81E?\14Îû>\97¦Î¾j\81E?\14Îû>\97¦Î¾j\81E?\14Îû>\97¦Î¾j\81E?áµ.?\91a\ f¿Ssð¾áµ.?\91a\ f¿Ssð¾áµ.?\91a\ f¿Ssð¾áµ.?\91a\ f¿Ssð¾rE»>\80°\99¾b\87a?rE»>\80°\99¾b\87a?rE»>\80°\99¾b\87a?rE»>\80°\99¾b\87a?Cr=?\81y\e¿$ø\93¾Cr=?\81y\e¿$ø\93¾Cr=?\81y\e¿$ø\93¾Cr=?\81y\e¿$ø\93¾´Ëf>Æh=¾oàt?´Ëf>Æh=¾oàt?´Ëf>Æh=¾oàt?´Ëf>Æh=¾oàt?fòD?K¡!¿úÈǽfòD?K¡!¿úÈǽfòD?K¡!¿úÈǽfòD?K¡!¿úÈǽfòD?K¡!¿úÈÇ=fòD?K¡!¿úÈÇ=fòD?K¡!¿úÈÇ=fòD?K¡!¿úÈÇ=µËf>Óh=¾màt¿µËf>Óh=¾màt¿µËf>Óh=¾màt¿µËf>Óh=¾màt¿Er=?\80y\e¿!ø\93>Er=?\80y\e¿!ø\93>Er=?\80y\e¿!ø\93>Er=?\80y\e¿!ø\93>kE»>s°\99¾f\87a¿kE»>s°\99¾f\87a¿kE»>s°\99¾f\87a¿kE»>s°\99¾f\87a¿ø§\83>a¾\f¾qàt?ø§\83>a¾\f¾qàt?ø§\83>a¾\f¾qàt?ø§\83>a¾\f¾qàt?ò±`?'4ð¾ñÈǽò±`?'4ð¾ñÈǽò±`?'4ð¾ñÈǽò±`?'4ð¾ñÈǽò±`?'4ð¾ñÈÇ=ò±`?'4ð¾ñÈÇ=ò±`?'4ð¾ñÈÇ=ò±`?'4ð¾ñÈÇ="¨\83>n¾\f¾kàt¿"¨\83>n¾\f¾kàt¿"¨\83>n¾\f¾kàt¿"¨\83>n¾\f¾kàt¿I#X?\\ eç¾\1cø\93>I#X?\\ eç¾\1cø\93>I#X?\\ eç¾\1cø\93>I#X?\\ eç¾\1cø\93>ñ§Õ>\10gd¾g\87a¿ñ§Õ>\10gd¾g\87a¿ñ§Õ>\10gd¾g\87a¿ñ§Õ>\10gd¾g\87a¿fSG?e\15Õ¾Esð>fSG?e\15Õ¾Esð>fSG?e\15Õ¾Esð>fSG?e\15Õ¾Esð>\1d¤\ f?5\8e\99¾g\81E¿\1d¤\ f?5\8e\99¾g\81E¿\1d¤\ f?5\8e\99¾g\81E¿\1d¤\ f?5\8e\99¾g\81E¿ÏÜ.?\95\90ï!?ÏÜ.?\95\90ï!?ÏÜ.?\95\90ï!?ÏÜ.?\95\90ï!?ÑÜ.?\95\8fï!¿ÑÜ.?\95\8fï!¿ÑÜ.?\95\8fï!¿ÑÜ.?\95\8fï!¿\e¤\ f?C\8e\99¾f\81E?\e¤\ f?C\8e\99¾f\81E?\e¤\ f?C\8e\99¾f\81E?\e¤\ f?C\8e\99¾f\81E?bSG?k\15Õ¾Msð¾bSG?k\15Õ¾Msð¾bSG?k\15Õ¾Msð¾bSG?k\15Õ¾Msð¾ú§Õ>\19gd¾d\87a?ú§Õ>\19gd¾d\87a?ú§Õ>\19gd¾d\87a?ú§Õ>\19gd¾d\87a?I#X?\\ eç¾\1cø\93¾I#X?\\ eç¾\1cø\93¾I#X?\\ eç¾\1cø\93¾I#X?\\ eç¾\1cø\93¾ª¼=?Z9f¾\8eï!?ª¼=?Z9f¾\8eï!?ª¼=?Z9f¾\8eï!?ª¼=?Z9f¾\8eï!?ª¼=?Z9f¾\8eï!¿ª¼=?Z9f¾\8eï!¿ª¼=?Z9f¾\8eï!¿ª¼=?Z9f¾\8eï!¿\rÜ\e?\1d\1e=¾o\81E?\rÜ\e?\1d\1e=¾o\81E?\rÜ\e?\1d\1e=¾o\81E?\rÜ\e?\1d\1e=¾o\81E?ïGX?U7\83¾Tsð¾ïGX?U7\83¾Tsð¾ïGX?U7\83¾Tsð¾ïGX?U7\83¾Tsð¾\9cÔç>k¦\f¾b\87a?\9cÔç>k¦\f¾b\87a?\9cÔç>k¦\f¾b\87a?\9cÔç>k¦\f¾b\87a?ð\85j?\95H\8e¾\18ø\93¾ð\85j?\95H\8e¾\18ø\93¾ð\85j?\95H\8e¾\18ø\93¾ð\85j?\95H\8e¾\18ø\93¾\11Û\8e>½V­½màt?\11Û\8e>½V­½màt?\11Û\8e>½V­½màt?\11Û\8e>½V­½màt?îÎs?²ê\93¾ñÈǽîÎs?²ê\93¾ñÈǽîÎs?²ê\93¾ñÈǽîÎs?²ê\93¾ñÈǽîÎs?²ê\93¾îÈÇ=îÎs?²ê\93¾îÈÇ=îÎs?²ê\93¾îÈÇ=îÎs?²ê\93¾îÈÇ=&Û\8e>çV­½jàt¿&Û\8e>çV­½jàt¿&Û\8e>çV­½jàt¿&Û\8e>çV­½jàt¿ð\85j?\98H\8e¾\1aø\93\85j?\98H\8e¾\1aø\93\85j?\98H\8e¾\1aø\93\85j?\98H\8e¾\1aø\93>\8bÔç>[¦\f¾f\87a¿\8bÔç>[¦\f¾f\87a¿\8bÔç>[¦\f¾f\87a¿\8bÔç>[¦\f¾f\87a¿òGX?^7\83¾Fsð>òGX?^7\83¾Fsð>òGX?^7\83¾Fsð>òGX?^7\83¾Fsð>\11Ü\e?6\1e=¾i\81E¿\11Ü\e?6\1e=¾i\81E¿\11Ü\e?6\1e=¾i\81E¿\11Ü\e?6\1e=¾i\81E¿]\8d}?öÇǽÝÈÇ=]\8d}?öÇǽÝÈÇ=]\8d}?öÇǽÝÈÇ=]\8d}?öÇǽÝÈÇ=°\90\94\1dê¼kàt¿°\90\94\1dê¼kàt¿°\90\94\1dê¼kàt¿°\90\94\1dê¼kàt¿]ås?D,À½\1cø\93>]ås?D,À½\1cø\93>]ås?D,À½\1cø\93>]ås?D,À½\1cø\93>h\18ñ>\15÷=½g\87a¿h\18ñ>\15÷=½g\87a¿h\18ñ>\15÷=½g\87a¿h\18ñ>\15÷=½g\87a¿¿ì`?\949±½Asð>¿ì`?\949±½Asð>¿ì`?\949±½Asð>¿ì`?\949±½Asð>®\16"?úm\7f½i\81E¿®\16"?úm\7f½i\81E¿®\16"?úm\7f½i\81E¿®\16"?úm\7f½i\81E¿âQE?Cy\9b½\8fï!?âQE?Cy\9b½\8fï!?âQE?Cy\9b½\8fï!?âQE?Cy\9b½\8fï!?âQE?Cy\9b½\8fï!¿âQE?Cy\9b½\8fï!¿âQE?Cy\9b½\8fï!¿âQE?Cy\9b½\8fï!¿¬\16"?\9em\7f½l\81E?¬\16"?\9em\7f½l\81E?¬\16"?\9em\7f½l\81E?¬\16"?\9em\7f½l\81E?¼ì`?\9e9±½Msð¾¼ì`?\9e9±½Msð¾¼ì`?\9e9±½Msð¾¼ì`?\9e9±½Msð¾n\18ñ>\18÷=½f\87a?n\18ñ>\18÷=½f\87a?n\18ñ>\18÷=½f\87a?n\18ñ>\18÷=½f\87a?_ås?C,À½\eø\93¾_ås?C,À½\eø\93¾_ås?C,À½\eø\93¾_ås?C,À½\eø\93¾ \90\94>\93\1eê¼màt? \90\94>\93\1eê¼màt? \90\94>\93\1eê¼màt? \90\94>\93\1eê¼màt?]\8d}?ùÇǽáÈǽ]\8d}?ùÇǽáÈǽ]\8d}?ùÇǽáÈǽ]\8d}?ùÇǽáÈǽáQE?\8ay\9b=\8eï!¿áQE?\8ay\9b=\8eï!¿áQE?\8ay\9b=\8eï!¿áQE?\8ay\9b=\8eï!¿®\16"?ëm\7f=i\81E?®\16"?ëm\7f=i\81E?®\16"?ëm\7f=i\81E?®\16"?ëm\7f=i\81E?¼ì`?\9e9±=Msð¾¼ì`?\9e9±=Msð¾¼ì`?\9e9±=Msð¾¼ì`?\9e9±=Msð¾n\18ñ>Q÷==f\87a?n\18ñ>Q÷==f\87a?n\18ñ>Q÷==f\87a?n\18ñ>Q÷==f\87a?]ås?°,À=\18ø\93¾]ås?°,À=\18ø\93¾]ås?°,À=\18ø\93¾]ås?°,À=\18ø\93¾©\90\94>\92\1eê<kàt?©\90\94>\92\1eê<kàt?©\90\94>\92\1eê<kàt?©\90\94>\92\1eê<kàt?[\8d}?bÈÇ=ÛÈǽ[\8d}?bÈÇ=ÛÈǽ[\8d}?bÈÇ=ÛÈǽ[\8d}?bÈÇ=ÛÈǽ[\8d}?bÈÇ=ÛÈÇ=[\8d}?bÈÇ=ÛÈÇ=[\8d}?bÈÇ=ÛÈÇ=[\8d}?bÈÇ=ÛÈÇ=ª\90\94\1dê<màt¿ª\90\94\1dê<màt¿ª\90\94\1dê<màt¿ª\90\94\1dê<màt¿]ås?°,À=\18ø\93>]ås?°,À=\18ø\93>]ås?°,À=\18ø\93>]ås?°,À=\18ø\93>f\18ñ>P÷==g\87a¿f\18ñ>P÷==g\87a¿f\18ñ>P÷==g\87a¿f\18ñ>P÷==g\87a¿¾ì`?Ò9±=Csð>¾ì`?Ò9±=Csð>¾ì`?Ò9±=Csð>¾ì`?Ò9±=Csð>²\16"?En\7f=e\81E¿²\16"?En\7f=e\81E¿²\16"?En\7f=e\81E¿²\16"?En\7f=e\81E¿áQE?\8ay\9b=\8eï!?áQE?\8ay\9b=\8eï!?áQE?\8ay\9b=\8eï!?áQE?\8ay\9b=\8eï!?ìÎs?Áê\93>ÞÈÇ=ìÎs?Áê\93>ÞÈÇ=ìÎs?Áê\93>ÞÈÇ=ìÎs?Áê\93>ÞÈÇ=\ fÛ\8e>þV­=màt¿\ fÛ\8e>þV­=màt¿\ fÛ\8e>þV­=màt¿\ fÛ\8e>þV­=màt¿ð\85j?\99H\8e>\10ø\93\85j?\99H\8e>\10ø\93\85j?\99H\8e>\10ø\93\85j?\99H\8e>\10ø\93>\83Ôç>b¦\f>i\87a¿\83Ôç>b¦\f>i\87a¿\83Ôç>b¦\f>i\87a¿\83Ôç>b¦\f>i\87a¿ñGX?a7\83>Hsð>ñGX?a7\83>Hsð>ñGX?a7\83>Hsð>ñGX?a7\83>Hsð>\ fÜ\e?E\1e=>i\81E¿\ fÜ\e?E\1e=>i\81E¿\ fÜ\e?E\1e=>i\81E¿\ fÜ\e?E\1e=>i\81E¿¨¼=?\809f>\8dï!?¨¼=?\809f>\8dï!?¨¼=?\809f>\8dï!?¨¼=?\809f>\8dï!?¨¼=?\809f>\8dï!¿¨¼=?\809f>\8dï!¿¨¼=?\809f>\8dï!¿¨¼=?\809f>\8dï!¿\vÜ\e?-\1e=>m\81E?\vÜ\e?-\1e=>m\81E?\vÜ\e?-\1e=>m\81E?\vÜ\e?-\1e=>m\81E?ïGX?e7\83>Osð¾ïGX?e7\83>Osð¾ïGX?e7\83>Osð¾ïGX?e7\83>Osð¾\96Ôç>n¦\f>b\87a?\96Ôç>n¦\f>b\87a?\96Ôç>n¦\f>b\87a?\96Ôç>n¦\f>b\87a?ð\85j?\9aH\8e>\10ø\93¾ð\85j?\9aH\8e>\10ø\93¾ð\85j?\9aH\8e>\10ø\93¾ð\85j?\9aH\8e>\10ø\93¾\11Û\8e>ÔV­=màt?\11Û\8e>ÔV­=màt?\11Û\8e>ÔV­=màt?\11Û\8e>ÔV­=màt?ìÎs?Áê\93>âÈǽìÎs?Áê\93>âÈǽìÎs?Áê\93>âÈǽìÎs?Áê\93>âÈǽ\18¤\ f?A\8e\99>j\81E?\18¤\ f?A\8e\99>j\81E?\18¤\ f?A\8e\99>j\81E?\18¤\ f?A\8e\99>j\81E?`SG?o\15Õ>Rsð¾`SG?o\15Õ>Rsð¾`SG?o\15Õ>Rsð¾`SG?o\15Õ>Rsð¾ô§Õ>Egd>b\87a?ô§Õ>Egd>b\87a?ô§Õ>Egd>b\87a?ô§Õ>Egd>b\87a?D#X?i\ eç>\11ø\93¾D#X?i\ eç>\11ø\93¾D#X?i\ eç>\11ø\93¾D#X?i\ eç>\11ø\93¾\10¨\83>s¾\f>màt?\10¨\83>s¾\f>màt?\10¨\83>s¾\f>màt?\10¨\83>s¾\f>màt?é±`?M4ð>ÑÈǽé±`?M4ð>ÑÈǽé±`?M4ð>ÑÈǽé±`?M4ð>ÑÈǽé±`?K4ð>ÎÈÇ=é±`?K4ð>ÎÈÇ=é±`?K4ð>ÎÈÇ=é±`?K4ð>ÎÈÇ=#¨\83>|¾\f>kàt¿#¨\83>|¾\f>kàt¿#¨\83>|¾\f>kàt¿#¨\83>|¾\f>kàt¿D#X?k\ eç>\12ø\93>D#X?k\ eç>\12ø\93>D#X?k\ eç>\12ø\93>D#X?k\ eç>\12ø\93>Ó§Õ>?gd>l\87a¿Ó§Õ>?gd>l\87a¿Ó§Õ>?gd>l\87a¿Ó§Õ>?gd>l\87a¿bSG?r\15Õ>Gsð>bSG?r\15Õ>Gsð>bSG?r\15Õ>Gsð>bSG?r\15Õ>Gsð>\1e¤\ f?3\8e\99>g\81E¿\1e¤\ f?3\8e\99>g\81E¿\1e¤\ f?3\8e\99>g\81E¿\1e¤\ f?3\8e\99>g\81E¿ÒÜ.?\9dîº>\8aï!?ÒÜ.?\9dîº>\8aï!?ÒÜ.?\9dîº>\8aï!?ÒÜ.?\9dîº>\8aï!?ÒÜ.?\9dîº>\8bï!¿ÒÜ.?\9dîº>\8bï!¿ÒÜ.?\9dîº>\8bï!¿ÒÜ.?\9dîº>\8bï!¿ºËf>âh=>màt¿ºËf>âh=>màt¿ºËf>âh=>màt¿ºËf>âh=>màt¿Fr=?\82y\e?\12ø\93>Fr=?\82y\e?\12ø\93>Fr=?\82y\e?\12ø\93>Fr=?\82y\e?\12ø\93>aE»>\81°\99>d\87a¿aE»>\81°\99>d\87a¿aE»>\81°\99>d\87a¿aE»>\81°\99>d\87a¿ßµ.?\99a\ f?Csð>ßµ.?\99a\ f?Csð>ßµ.?\99a\ f?Csð>ßµ.?\99a\ f?Csð>ñÍû>¢¦Î>q\81E¿ñÍû>¢¦Î>q\81E¿ñÍû>¢¦Î>q\81E¿ñÍû>¢¦Î>q\81E¿³D\19?{\91û>\8aï!?³D\19?{\91û>\8aï!?³D\19?{\91û>\8aï!?³D\19?{\91û>\8aï!?³D\19?{\91û>\8aï!¿³D\19?{\91û>\8aï!¿³D\19?{\91û>\8aï!¿³D\19?{\91û>\8aï!¿\ 1Îû>\9b¦Î>o\81E?\ 1Îû>\9b¦Î>o\81E?\ 1Îû>\9b¦Î>o\81E?\ 1Îû>\9b¦Î>o\81E?ݵ.?\96a\ f?Osð¾Ýµ.?\96a\ f?Osð¾Ýµ.?\96a\ f?Osð¾Ýµ.?\96a\ f?Osð¾jE»>\82°\99>b\87a?jE»>\82°\99>b\87a?jE»>\82°\99>b\87a?jE»>\82°\99>b\87a?Fr=?\82y\e?\12ø\93¾Fr=?\82y\e?\12ø\93¾Fr=?\82y\e?\12ø\93¾Fr=?\82y\e?\12ø\93¾ëËf>Âh=>kàt?ëËf>Âh=>kàt?ëËf>Âh=>kàt?ëËf>Âh=>kàt?aòD?R¡!?ÇÈǽaòD?R¡!?ÇÈǽaòD?R¡!?ÇÈǽaòD?R¡!?ÇÈǽaòD?R¡!?ÇÈÇ=aòD?R¡!?ÇÈÇ=aòD?R¡!?ÇÈÇ=aòD?R¡!?ÇÈÇ=\90a\ f?ãµ.?Osð¾\90a\ f?ãµ.?Osð¾\90a\ f?ãµ.?Osð¾\90a\ f?ãµ.?Osð¾}°\99>jE»>b\87a?}°\99>jE»>b\87a?}°\99>jE»>b\87a?}°\99>jE»>b\87a?|y\e?Nr=?\ 5ø\93¾|y\e?Nr=?\ 5ø\93¾|y\e?Nr=?\ 5ø\93¾|y\e?Nr=?\ 5ø\93¾Îh=>ÃËf>màt?Îh=>ÃËf>màt?Îh=>ÃËf>màt?Îh=>ÃËf>màt?G¡!?jòD?ÈÈǽG¡!?jòD?ÈÈǽG¡!?jòD?ÈÈǽG¡!?jòD?ÈÈǽG¡!?jòD?ÈÈÇ=G¡!?jòD?ÈÈÇ=G¡!?jòD?ÈÈÇ=G¡!?jòD?ÈÈÇ=Óh=>ÙËf>kàt¿Óh=>ÙËf>kàt¿Óh=>ÙËf>kàt¿Óh=>ÙËf>kàt¿{y\e?Mr=?\vø\93>{y\e?Mr=?\vø\93>{y\e?Mr=?\vø\93>{y\e?Mr=?\vø\93>}°\99>dE»>f\87a¿}°\99>dE»>f\87a¿}°\99>dE»>f\87a¿}°\99>dE»>f\87a¿\92a\ f?æµ.?Bsð>\92a\ f?æµ.?Bsð>\92a\ f?æµ.?Bsð>\92a\ f?æµ.?Bsð>¥¦Î>\rÎû>g\81E¿¥¦Î>\rÎû>g\81E¿¥¦Î>\rÎû>g\81E¿¥¦Î>\rÎû>g\81E¿m\91û>µD\19?\8aï!?m\91û>µD\19?\8aï!?m\91û>µD\19?\8aï!?m\91û>µD\19?\8aï!?i\91û>¶D\19?\8bï!¿i\91û>¶D\19?\8bï!¿i\91û>¶D\19?\8bï!¿i\91û>¶D\19?\8bï!¿\99¦Î>\vÎû>l\81E?\99¦Î>\vÎû>l\81E?\99¦Î>\vÎû>l\81E?\99¦Î>\vÎû>l\81E?Z\ eç>L#X?\ 6ø\93>Z\ eç>L#X?\ 6ø\93>Z\ eç>L#X?\ 6ø\93>Z\ eç>L#X?\ 6ø\93>"gd>í§Õ>g\87a¿"gd>í§Õ>g\87a¿"gd>í§Õ>g\87a¿"gd>í§Õ>g\87a¿`\15Õ>bSG?Ssð>`\15Õ>bSG?Ssð>`\15Õ>bSG?Ssð>`\15Õ>bSG?Ssð>7\8e\99>\e¤\ f?j\81E¿7\8e\99>\e¤\ f?j\81E¿7\8e\99>\e¤\ f?j\81E¿7\8e\99>\e¤\ f?j\81E¿\97îº>ØÜ.?\8aï!?\97îº>ØÜ.?\8aï!?\97îº>ØÜ.?\8aï!?\97îº>ØÜ.?\8aï!?\95îº>ÖÜ.?\8bï!¿\95îº>ÖÜ.?\8bï!¿\95îº>ÖÜ.?\8bï!¿\95îº>ÖÜ.?\8bï!¿/\8e\99>\e¤\ f?k\81E?/\8e\99>\e¤\ f?k\81E?/\8e\99>\e¤\ f?k\81E?/\8e\99>\e¤\ f?k\81E?Y\15Õ>cSG?Wsð¾Y\15Õ>cSG?Wsð¾Y\15Õ>cSG?Wsð¾Y\15Õ>cSG?Wsð¾ gd>ú§Õ>d\87a? gd>ú§Õ>d\87a? gd>ú§Õ>d\87a? gd>ú§Õ>d\87a?]\ eç>L#X?\ 5ø\93¾]\ eç>L#X?\ 5ø\93¾]\ eç>L#X?\ 5ø\93¾]\ eç>L#X?\ 5ø\93¾\82¾\f>\16¨\83>màt?\82¾\f>\16¨\83>màt?\82¾\f>\16¨\83>màt?\82¾\f>\16¨\83>màt?/4ð>ñ±`?»Èǽ/4ð>ñ±`?»Èǽ/4ð>ñ±`?»Èǽ/4ð>ñ±`?»Èǽ/4ð>ñ±`?»ÈÇ=/4ð>ñ±`?»ÈÇ=/4ð>ñ±`?»ÈÇ=/4ð>ñ±`?»ÈÇ=ܾ\f>#¨\83>hàt¿Ü¾\f>#¨\83>hàt¿Ü¾\f>#¨\83>hàt¿Ü¾\f>#¨\83>hàt¿f¦\f>\9cÔç>b\87a?f¦\f>\9cÔç>b\87a?f¦\f>\9cÔç>b\87a?f¦\f>\9cÔç>b\87a?\9cH\8e\85j?\ 1ø\93¾\9cH\8e\85j?\ 1ø\93¾\9cH\8e\85j?\ 1ø\93¾\9cH\8e\85j?\ 1ø\93¾\bW­=\ fÛ\8e>màt?\bW­=\ fÛ\8e>màt?\bW­=\ fÛ\8e>màt?\bW­=\ fÛ\8e>màt?­ê\93>ðÎs?¶Èǽ­ê\93>ðÎs?¶Èǽ­ê\93>ðÎs?¶Èǽ­ê\93>ðÎs?¶Èǽ®ê\93>ðÎs?µÈÇ=®ê\93>ðÎs?µÈÇ=®ê\93>ðÎs?µÈÇ=®ê\93>ðÎs?µÈÇ=       W­=\1fÛ\8e>jàt¿     W­=\1fÛ\8e>jàt¿     W­=\1fÛ\8e>jàt¿     W­=\1fÛ\8e>jàt¿\9cH\8e\85j?\ 1ø\93>\9cH\8e\85j?\ 1ø\93>\9cH\8e\85j?\ 1ø\93>\9cH\8e\85j?\ 1ø\93>,¦\f>\93Ôç>f\87a¿,¦\f>\93Ôç>f\87a¿,¦\f>\93Ôç>f\87a¿,¦\f>\93Ôç>f\87a¿X7\83>ñGX?Jsð>X7\83>ñGX?Jsð>X7\83>ñGX?Jsð>X7\83>ñGX?Jsð>1\1e=>\rÜ\e?l\81E¿1\1e=>\rÜ\e?l\81E¿1\1e=>\rÜ\e?l\81E¿1\1e=>\rÜ\e?l\81E¿e9f>°¼=?\86ï!?e9f>°¼=?\86ï!?e9f>°¼=?\86ï!?e9f>°¼=?\86ï!?e9f>°¼=?\86ï!¿e9f>°¼=?\86ï!¿e9f>°¼=?\86ï!¿e9f>°¼=?\86ï!¿)\1e=>\vÜ\e?o\81E?)\1e=>\vÜ\e?o\81E?)\1e=>\vÜ\e?o\81E?)\1e=>\vÜ\e?o\81E?W7\83>íGX?]sð¾W7\83>íGX?]sð¾W7\83>íGX?]sð¾W7\83>íGX?]sð¾\899±=¿ì`?Dsð>\899±=¿ì`?Dsð>\899±=¿ì`?Dsð>\899±=¿ì`?Dsð>Nm\7f\16"?m\81E¿Nm\7f\16"?m\81E¿Nm\7f\16"?m\81E¿Nm\7f\16"?m\81E¿Hy\9b=çQE?\88ï!?Hy\9b=çQE?\88ï!?Hy\9b=çQE?\88ï!?Hy\9b=çQE?\88ï!?Hy\9b=çQE?\88ï!¿Hy\9b=çQE?\88ï!¿Hy\9b=çQE?\88ï!¿Hy\9b=çQE?\88ï!¿\9am\7f\16"?o\81E?\9am\7f\16"?o\81E?\9am\7f\16"?o\81E?\9am\7f\16"?o\81E?\819±=¼ì`?Msð¾\819±=¼ì`?Msð¾\819±=¼ì`?Msð¾\819±=¼ì`?Msð¾"÷==}\18ñ>b\87a?"÷==}\18ñ>b\87a?"÷==}\18ñ>b\87a?"÷==}\18ñ>b\87a?2,À=aås?\aø\93¾2,À=aås?\aø\93¾2,À=aås?\aø\93¾2,À=aås?\aø\93¾ß\1dê<¢\90\94>màt?ß\1dê<¢\90\94>màt?ß\1dê<¢\90\94>màt?ß\1dê<¢\90\94>màt?ëÇÇ=]\8d}?fÈǽëÇÇ=]\8d}?fÈǽëÇÇ=]\8d}?fÈǽëÇÇ=]\8d}?fÈǽêÇÇ=]\8d}?fÈÇ=êÇÇ=]\8d}?fÈÇ=êÇÇ=]\8d}?fÈÇ=êÇÇ=]\8d}?fÈÇ=
+\eê<±\90\94>kàt¿
+\eê<±\90\94>kàt¿
+\eê<±\90\94>kàt¿
+\eê<±\90\94>kàt¿=,À=aås?\aø\93>=,À=aås?\aø\93>=,À=aås?\aø\93>=,À=aås?\aø\93>ý÷==o\18ñ>f\87a¿ý÷==o\18ñ>f\87a¿ý÷==o\18ñ>f\87a¿ý÷==o\18ñ>f\87a¿\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?\0\0\80?\0\0\0\0\0\0\0\80\0\0\80?ÑÎ\94°\90\f¶;]:\b¼u\10\8eº\1d\8d²;]:\b¼\13¶Cºùùu;P^\17¼\1d²W°\8cËz;P^\17¼@\v;±T:\b<\9b\f¶;;\9dÔºC\9c\ 5<\9b\f¶;\83>캽u\14<\99Ëz;[¼.°K^\17<\99Ëz;u?Ò°a´ç;j´ç»Aдºª@ã;j´ç»u\10\8eº\1d\8d²;]:\b¼ÑÎ\94°\90\f¶;]:\b¼u?Ò°a´ç;j´ç;Aдºª@ã;j´ç;;\9dÔºC\9c\ 5<\9b\f¶;@\v;±T:\b<\9b\f¶;@\v;±T:\b<\9b\f¶»;\9dÔºC\9c\ 5<\9b\f¶»Aдºª@ã;j´ç»u?Ò°a´ç;j´ç»³I©°\8e\f¶;]:\b<t\10\8eº\1c\8d²;]:\b<Aдºª@ã;j´ç;u?Ò°a´ç;j´ç;[¼.°K^\17< Ëz»\82>캽u\14< Ëz»;\9dÔºC\9c\ 5<\9b\f¶»@\v;±T:\b<\9b\f¶»\1d²W°\8aËz;R^\17<\r¶Cºòùu;R^\17<t\10\8eº\1c\8d²;]:\b<³I©°\8e\f¶;]:\b<ÏÍ0±\13± <uµÿº¥Ëúº­\9a\1d<uµÿº\82>캽u\14< Ëz»[¼.°K^\17< Ëz»\8av&¯Pµÿ:\1e± <\9b\8bǹ\8bËú:\1e± <\r¶Cºòùu;R^\17<\1d²W°\8aËz;R^\17<@\v\ 1×#<ì\86O°xµÿº\1d± <ì\86O°¥Ëúº­\9a\1d<uµÿºÏÍ0±\13± <uµÿº\8c\1cίaµÿ:\1d± ¼ª\8bǹ Ëú:\1d± ¼uÍø0N\fͱ
+×#¼\1c¶Cº}>ì:\1d± ¼\7f\10\8eº0\9dÔ:\1d± ¼\92\8bÇ9\8fËú:\1d± ¼
+¶C:n>ì:\1d± ¼Pдº4д:\1d± ¼uÍø0N\fͱ
+×#¼K\9dÔºb\10\8e:\1d± ¼\97>ìºâµC:\1d± ¼¹Ëúº8\8bÇ9\1d± ¼uÍø0N\fͱ
+×#¼\8bµÿºfv`±\1d± ¼¸Ëúº\18\8cǹ\1d± ¼\93>ìºP¶Cº\1d± ¼uÍø0N\fͱ
+×#¼F\9dÔº\97\10\8eº\1d± ¼Jдºgдº\1d± ¼y\10\8eºa\9dÔº\1d± ¼uÍø0N\fͱ
+×#¼\14¶Cº«>ìº\1d± ¼¡\8bǹÎËúº\1d± ¼
+\98\9e. µÿº\1d± ¼uÍø0N\fͱ
+×#¼¥\8bÇ9ËËúº\1d± ¼\13¶C:§>ìº\1d± ¼w\10\8e:\\9dÔº\1d± ¼uÍø0N\fͱ
+×#¼Fд:aдº\1d± ¼@\9dÔ:\91\10\8eº\1d± ¼\8a>ì:F¶Cº\1d± ¼uÍø0N\fͱ
+×#¼¬Ëú:
+\8cǹ\1d± ¼~µÿ:õ8V±\1d± ¼«Ëú:2\8bÇ9\1d± ¼uÍø0N\fͱ
+×#¼\89>ì:صC:\1d± ¼=\9dÔ:Y\10\8e:\1d± ¼Bд:)д:\1d± ¼uÍø0N\fͱ
+×#¼s\10\8e:"\9dÔ:\1d± ¼\0\0\0\0\9eê_±
+×#<\9b\8bǹ\8bËú:\1e± <\8av&¯Pµÿ:\1e± <\v¶Cºi>ì:\1e± <r\10\8eº \9dÔ:\1e± <\8a\8bÇ9~Ëú:\1e± <þµC:]>ì:\1e± <\0\0\0\0\9eê_±
+×#<@дº&д:\1e± <9\9dÔºW\10\8e:\1e± <\82>ìºÕµC:\1e± <\0\0\0\0\9eê_±
+×#<£Ëúº.\8bÇ9\1e± <wµÿº\99©S±\1e± <£Ëúº\ 2\8cǹ\1e± <\0\0\0\0\9eê_±
+×#<\81>ìº>¶Cº\1e± <7\9dÔº\8c\10\8eº\1e± <=дºYдº\1e± <\0\0\0\0\9eê_±
+×#<p\10\8eºR\9dÔº\1e± <        ¶Cº\9b>ìº\1e± <\97\8bǹ¼Ëúº\1e± <\0\0\0\0\9eê_±
+×#<
+U.®\8eµÿº\1e± <\93\8bÇ9»Ëúº\1e± <\ 4¶C:\97>ìº\1e± <\0\0\0\0\9eê_±
+×#<l\10\8e:L\9dÔº\1e± <8д:Tдº\1e± <0\9dÔ:\87\10\8eº\1e± <\0\0\0\0\9eê_±
+×#<x>ì:7¶Cº\1e± <\99Ëú:ü\8bǹ\1e± <lµÿ:RÈX±\1e± <\0\0\0\0\9eê_±
+×#<\99Ëú:"\8bÇ9\1e± <x>ì:ʵC:\1e± </\9dÔ:O\10\8e:\1e± <\0\0\0\0\9eê_±
+×#<7д:\1cд:\1e± <j\10\8e:\14\9dÔ:\1e± <ÏÍ0±\13± <iµÿ:¦Ëúº­\9a\1d<iµÿ:xµÿº\1d± <ì\86O°@\v\ 1×#<ì\86\1d²W°\8cËz;P^\17¼\13¶Cºùùu;P^\17¼ª\8bǹ Ëú:\1d± ¼\8c\1cίaµÿ:\1d± ¼[¼.°K^\17<\99Ëz;\83>캽u\14<\99Ëz;¦Ëúº­\9a\1d<iµÿ:ÏÍ0±\13± <iµÿ:¦Ëúº­\9a\1d<iµÿ:ýùu»¼u\14<iµÿ:¥Ëz»P^\17\86O°xµÿº\1d± <ì\86\13¶Cºùùu;P^\17¼^ó¿ºk´g;P^\17¼\1c¶Cº}>ì:\1d± ¼ª\8bǹ Ëú:\1d± ¼\83>캽u\14<\99Ëz;o´g»\9fØ\v<\99Ëz;ýùu»¼u\14<iµÿ:¦Ëúº­\9a\1d<iµÿ:u\10\8eº\1d\8d²;]:\b¼¥U\v»
+1¨;]:\b¼^ó¿ºk´g;P^\17¼\13¶Cºùùu;P^\17¼;\9dÔºC\9c\ 5<\9b\f¶;b\87P»j·û;\9b\f¶;o´g»\9fØ\v<\99Ëz;\83>캽u\14<\99Ëz;Aдºª@ã;j´ç»×V1»7\11Ö;j´ç»¥U\v»
+1¨;]:\b¼u\10\8eº\1d\8d²;]:\b¼Aдºª@ã;j´ç;×V1»7\11Ö;j´ç;b\87P»j·û;\9b\f¶;;\9dÔºC\9c\ 5<\9b\f¶;;\9dÔºC\9c\ 5<\9b\f¶»b\87P»j·û;\9b\f¶»×V1»7\11Ö;j´ç»Aдºª@ã;j´ç»t\10\8eº\1c\8d²;]:\b<¤U\v»    1¨;]:\b<×V1»7\11Ö;j´ç;Aдºª@ã;j´ç;\82>캽u\14< Ëz»n´g»\9fØ\v< Ëz»b\87P»j·û;\9b\f¶»;\9dÔºC\9c\ 5<\9b\f¶»\r¶Cºòùu;R^\17<Yó¿ºd´g;R^\17<¤U\v»     1¨;]:\b<t\10\8eº\1c\8d²;]:\b<¥Ëúº­\9a\1d<uµÿºýùu»¼u\14<uµÿºn´g»\9fØ\v< Ëz»\82>캽u\14< Ëz»\9b\8bǹ\8bËú:\1e± <\v¶Cºi>ì:\1e± <Yó¿ºd´g;R^\17<\r¶Cºòùu;R^\17<xµÿº\1d± <ì\86O°¥Ëz»P^\17\86O°ýùu»¼u\14<uµÿº¥Ëúº­\9a\1d<uµÿº¤U\v» 1¨;]:\b<JHJ»N^\97;]:\b<sº\80»Ê§À;j´ç;×V1»7\11Ö;j´ç;n´g»\9fØ\v< Ëz»\ e1¨»i·û; Ëz»T^\97»ð\89â;\9b\f¶»b\87P»j·û;\9b\f¶»Yó¿ºd´g;R^\17<¤U\v»U\87P;R^\17<JHJ»N^\97;]:\b<¤U\v»     1¨;]:\b<ýùu»¼u\14<uµÿº \8d²»A\9c\ 5<uµÿº\ e1¨»i·û; Ëz»n´g»\9fØ\v< Ëz»\v¶Cºi>ì:\1e± <r\10\8eº \9dÔ:\1e± <¤U\v»U\87P;R^\17<Yó¿ºd´g;R^\17<¥Ëz»P^\17\86O° \f¶»[:\b\86O° \8d²»A\9c\ 5<uµÿºýùu»¼u\14<uµÿºýùu»¼u\14<iµÿ: \8d²»A\9c\ 5<iµÿ: \f¶»[:\b\86O°¥Ëz»P^\17\86O°^ó¿ºk´g;P^\17¼§U\v»\\87P;P^\17¼\7f\10\8eº0\9dÔ:\1d± ¼\1c¶Cº}>ì:\1d± ¼o´g»\9fØ\v<\99Ëz;\ e1¨»j·û;\99Ëz; \8d²»A\9c\ 5<iµÿ:ýùu»¼u\14<iµÿ:¥U\v»
+1¨;]:\b¼KHJ»O^\97;]:\b¼§U\v»\\87P;P^\17¼^ó¿ºk´g;P^\17¼b\87P»j·û;\9b\f¶;T^\97»ð\89â;\9b\f¶;\ e1¨»j·û;\99Ëz;o´g»\9fØ\v<\99Ëz;×V1»7\11Ö;j´ç»sº\80»Ê§À;j´ç»KHJ»O^\97;]:\b¼¥U\v»
+1¨;]:\b¼×V1»7\11Ö;j´ç;sº\80»Ê§À;j´ç;T^\97»ð\89â;\9b\f¶;b\87P»j·û;\9b\f¶;b\87P»j·û;\9b\f¶»T^\97»ð\89â;\9b\f¶»sº\80»Ê§À;j´ç»×V1»7\11Ö;j´ç»\ e1¨»j·û;\99Ëz;<\11Ö»7\11Ö;\99Ëz;®@㻦@ã;iµÿ: \8d²»A\9c\ 5<iµÿ:KHJ»O^\97;]:\b¼tº\80»mº\80;]:\b¼ÜV1»ÎV1;P^\17¼§U\v»\\87P;P^\17¼T^\97»ð\89â;\9b\f¶;ЧÀ»É§À;\9b\f¶;<\11Ö»7\11Ö;\99Ëz;\ e1¨»j·û;\99Ëz;sº\80»Ê§À;j´ç»\ e×£»\a×£;j´ç»tº\80»mº\80;]:\b¼KHJ»O^\97;]:\b¼sº\80»Ê§À;j´ç;\ e×£»\a×£;j´ç;ЧÀ»É§À;\9b\f¶;T^\97»ð\89â;\9b\f¶;T^\97»ð\89â;\9b\f¶»Ð§À»É§À;\9b\f¶»\ e×£»\a×£;j´ç»sº\80»Ê§À;j´ç»JHJ»N^\97;]:\b<sº\80»kº\80;]:\b<\ e×£»\a×£;j´ç;sº\80»Ê§À;j´ç;\ e1¨»i·û; Ëz»<\11Ö»5\11Ö; Ëz»Ð§À»É§À;\9b\f¶»T^\97»ð\89â;\9b\f¶»¤U\v»U\87P;R^\17<×V1»ÊV1;R^\17<sº\80»kº\80;]:\b<JHJ»N^\97;]:\b\8d²»A\9c\ 5<uµÿº®@㻦@ã;uµÿº<\11Ö»5\11Ö; Ëz»\ e1¨»i·û; Ëz»r\10\8eº \9dÔ:\1e± <@дº&д:\1e± <×V1»ÊV1;R^\17<¤U\v»U\87P;R^\17\f¶»[:\b\86O°o´ç»f´ç;ì\86O°®@㻦@ã;uµÿº \8d²»A\9c\ 5<uµÿº \8d²»A\9c\ 5<iµÿ:®@㻦@ã;iµÿ:o´ç»f´ç;ì\86O° \f¶»[:\b\86O°§U\v»\\87P;P^\17¼ÜV1»ÎV1;P^\17¼Pдº4д:\1d± ¼\7f\10\8eº0\9dÔ:\1d± ¼×V1»ÊV1;R^\17<d\87\97U\v;R^\17<T^\97»;HJ;]:\b<sº\80»kº\80;]:\b<®@㻦@ã;uµÿºD\9c\ 5¼\17\8d²;uµÿºo·û»\a1¨; Ëz»<\11Ö»5\11Ö; Ëz»@дº&д:\1e± <9\9dÔºW\10\8e:\1e± <d\87\97U\v;R^\17<×V1»ÊV1;R^\17<o´ç»f´ç;ì\86O°_:\b¼\97\f¶;ì\86O°D\9c\ 5¼\17\8d²;uµÿº®@㻦@ã;uµÿº®@㻦@ã;iµÿ:D\9c\ 5¼\17\8d²;iµÿ:_:\b¼\97\f¶;ì\86O°o´ç»f´ç;ì\86O°ÜV1»ÎV1;P^\17¼i\87\9aU\v;P^\17¼K\9dÔºb\10\8e:\1d± ¼Pдº4д:\1d± ¼<\11Ö»7\11Ö;\99Ëz;o·û»        1¨;\99Ëz;D\9c\ 5¼\17\8d²;iµÿ:®@㻦@ã;iµÿ:tº\80»mº\80;]:\b¼V^\97»=HJ;]:\b¼i\87\9aU\v;P^\17¼ÜV1»ÎV1;P^\17¼Ð§À»É§À;\9b\f¶;÷\89â»M^\97;\9b\f¶;o·û»     1¨;\99Ëz;<\11Ö»7\11Ö;\99Ëz;\ e×£»\a×£;j´ç»Ð§À»kº\80;j´ç»V^\97»=HJ;]:\b¼tº\80»mº\80;]:\b¼\ e×£»\a×£;j´ç;ЧÀ»kº\80;j´ç;÷\89â»M^\97;\9b\f¶;ЧÀ»É§À;\9b\f¶;ЧÀ»É§À;\9b\f¶»÷\89â»M^\97;\9b\f¶»Ð§À»kº\80;j´ç»\ e×£»\a×£;j´ç»sº\80»kº\80;]:\b<T^\97»;HJ;]:\b<ЧÀ»kº\80;j´ç;\ e×£»\a×£;j´ç;<\11Ö»5\11Ö; Ëz»o·û»\a1¨; Ëz»÷\89â»M^\97;\9b\f¶»Ð§À»É§À;\9b\f¶»V^\97»=HJ;]:\b¼\101¨»\97U\v;]:\b¼w´g»Dó¿:P^\17¼i\87\9aU\v;P^\17¼÷\89â»M^\97;\9b\f¶;p·û»R\87P;\9b\f¶;¡Ø\v¼d´g;\99Ëz;o·û»     1¨;\99Ëz;ЧÀ»kº\80;j´ç»=\11Ö»ÇV1;j´ç»\101¨»\97U\v;]:\b¼V^\97»=HJ;]:\b¼Ð§À»kº\80;j´ç;=\11Ö»ÇV1;j´ç;p·û»R\87P;\9b\f¶;÷\89â»M^\97;\9b\f¶;÷\89â»M^\97;\9b\f¶»p·û»R\87P;\9b\f¶»=\11Ö»ÇV1;j´ç»Ð§À»kº\80;j´ç»T^\97»;HJ;]:\b<\ f1¨»\94U\v;]:\b<=\11Ö»ÇV1;j´ç;ЧÀ»kº\80;j´ç;o·û»\a1¨; Ëz»¡Ø\v¼a´g; Ëz»p·û»R\87P;\9b\f¶»÷\89â»M^\97;\9b\f¶»d\87\97U\v;R^\17<p´g»@ó¿:R^\17<\ f1¨»\94U\v;]:\b<T^\97»;HJ;]:\b<D\9c\ 5¼\17\8d²;uµÿº¾u\14¼éùu;uµÿº¡Ø\v¼a´g; Ëz»o·û»\a1¨; Ëz»9\9dÔºW\10\8e:\1e± <\82>ìºÕµC:\1e± <p´g»@ó¿:R^\17<d\87\97U\v;R^\17<_:\b¼\97\f¶;ì\86O°S^\17¼\94Ëz;ì\86O°¾u\14¼éùu;uµÿºD\9c\ 5¼\17\8d²;uµÿºD\9c\ 5¼\17\8d²;iµÿ:¾u\14¼éùu;iµÿ:S^\17¼\94Ëz;ì\86O°_:\b¼\97\f¶;ì\86O°i\87\9aU\v;P^\17¼w´g»Dó¿:P^\17¼\97>ìºâµC:\1d± ¼K\9dÔºb\10\8e:\1d± ¼o·û»     1¨;\99Ëz;¡Ø\v¼d´g;\99Ëz;¾u\14¼éùu;iµÿ:D\9c\ 5¼\17\8d²;iµÿ:¾u\14¼éùu;uµÿº®\9a\1d¼\80Ëú:uµÿº¿u\14¼i>ì: Ëz»¡Ø\v¼a´g; Ëz»\82>ìºÕµC:\1e± <£Ëúº.\8bÇ9\1e± <þùu»×µC:R^\17<p´g»@ó¿:R^\17<S^\17¼\94Ëz;ì\86O° ± ¼Wµÿ:ì\86O°®\9a\1d¼\80Ëú:uµÿº¾u\14¼éùu;uµÿº¾u\14¼éùu;iµÿ:®\9a\1d¼\80Ëú:iµÿ: ± ¼Wµÿ:ì\86O°S^\17¼\94Ëz;ì\86O°w´g»Dó¿:P^\17¼\ 5úu»ÙµC:P^\17¼¹Ëúº8\8bÇ9\1d± ¼\97>ìºâµC:\1d± ¼¡Ø\v¼d´g;\99Ëz;¿u\14¼n>ì:\99Ëz;®\9a\1d¼\80Ëú:iµÿ:¾u\14¼éùu;iµÿ:\101¨»\97U\v;]:\b¼"\8d²»X\10\8e:]:\b¼\ 5úu»ÙµC:P^\17¼w´g»Dó¿:P^\17¼p·û»R\87P;\9b\f¶;E\9c\ 5¼\e\9dÔ:\9b\f¶;¿u\14¼n>ì:\99Ëz;¡Ø\v¼d´g;\99Ëz;=\11Ö»ÇV1;j´ç»¯@ã»%д:j´ç»"\8d²»X\10\8e:]:\b¼\101¨»\97U\v;]:\b¼=\11Ö»ÇV1;j´ç;¯@ã»%д:j´ç;E\9c\ 5¼\e\9dÔ:\9b\f¶;p·û»R\87P;\9b\f¶;p·û»R\87P;\9b\f¶»E\9c\ 5¼\e\9dÔ:\9b\f¶»¯@ã»%д:j´ç»=\11Ö»ÇV1;j´ç»\ f1¨»\94U\v;]:\b<!\8d²»T\10\8e:]:\b<¯@ã»%д:j´ç;=\11Ö»ÇV1;j´ç;¡Ø\v¼a´g; Ëz»¿u\14¼i>ì: Ëz»E\9c\ 5¼\e\9dÔ:\9b\f¶»p·û»R\87P;\9b\f¶»p´g»@ó¿:R^\17<þùu»×µC:R^\17<!\8d²»T\10\8e:]:\b<\ f1¨»\94U\v;]:\b<E\9c\ 5¼\e\9dÔ:\9b\f¶;_:\b¼\1e\95\9b\f¶;R^\17¼
\1d±\99Ëz;¿u\14¼n>ì:\99Ëz;¯@ã»%д:j´ç»n´ç»®W[±j´ç»¡\f¶»õ8V±]:\b¼"\8d²»X\10\8e:]:\b¼¯@ã»%д:j´ç;n´ç»®W[±j´ç;_:\b¼\1e\95\9b\f¶;E\9c\ 5¼\e\9dÔ:\9b\f¶;E\9c\ 5¼\e\9dÔ:\9b\f¶»_:\b¼\1e\95\9b\f¶»n´ç»®W[±j´ç»¯@ã»%д:j´ç»!\8d²»T\10\8e:]:\b<\9e\f¶»×³j±]:\b<n´ç»®W[±j´ç;¯@ã»%д:j´ç;¿u\14¼i>ì: Ëz»R^\17¼\\9f<± Ëz»_:\b¼\1e\95\9b\f¶»E\9c\ 5¼\e\9dÔ:\9b\f¶»þùu»×µC:R^\17<¥Ëz»=\1aQ±R^\17<\9e\f¶»×³j±]:\b<!\8d²»T\10\8e:]:\b\9a\1d¼\80Ëú:uµÿº\1d± ¼pE\87±uµÿºR^\17¼\\9f<± Ëz»¿u\14¼i>ì: Ëz»£Ëúº.\8bÇ9\1e± <wµÿº\99©S±\1e± <¥Ëz»=\1aQ±R^\17<þùu»×µC:R^\17< ± ¼Wµÿ:ì\86\r×#¼¸&\82±ì\86\1d± ¼pE\87±uµÿº®\9a\1d¼\80Ëú:uµÿº®\9a\1d¼\80Ëú:iµÿ:\1d± ¼pE\87±iµÿ:\r×#¼¸&\82±ì\86O° ± ¼Wµÿ:ì\86\ 5úu»ÙµC:P^\17¼«Ëz»®W[±P^\17¼\8bµÿºfv`±\1d± ¼¹Ëúº8\8bÇ9\1d± ¼¿u\14¼n>ì:\99Ëz;R^\17¼
\1d±\99Ëz;\1d± ¼pE\87±iµÿ:®\9a\1d¼\80Ëú:iµÿ:"\8d²»X\10\8e:]:\b¼¡\f¶»õ8V±]:\b¼«Ëz»®W[±P^\17¼\ 5úu»ÙµC:P^\17¼wµÿº\99©S±\1e± <£Ëúº\ 2\8cǹ\1e± <ýùu»>¶CºR^\17<¥Ëz»=\1aQ±R^\17<\r×#¼¸&\82±ì\86O° ± ¼\97µÿºì\86O°­\9a\1d¼ÁËúºuµÿº\1d± ¼pE\87±uµÿº\1d± ¼pE\87±iµÿ:­\9a\1d¼ÁËúºiµÿ: ± ¼\97µÿºì\86\r×#¼¸&\82±ì\86O°«Ëz»®W[±P^\17¼\ 3úu»F¶CºP^\17¼¸Ëúº\18\8cǹ\1d± ¼\8bµÿºfv`±\1d± ¼R^\17¼
\1d±\99Ëz;¿u\14¼\94>ìº\99Ëz;­\9a\1d¼ÁËúºiµÿ:\1d± ¼pE\87±iµÿ:¡\f¶»õ8V±]:\b¼"\8d²»\8e\10\8eº]:\b¼\ 3úu»F¶CºP^\17¼«Ëz»®W[±P^\17¼_:\b¼\1e\95\9b\f¶;D\9c\ 5¼S\9dÔº\9b\f¶;¿u\14¼\94>ìº\99Ëz;R^\17¼
\1d±\99Ëz;n´ç»®W[±j´ç»®@ã»Yдºj´ç»"\8d²»\8e\10\8eº]:\b¼¡\f¶»õ8V±]:\b¼n´ç»®W[±j´ç;®@ã»Yдºj´ç;D\9c\ 5¼S\9dÔº\9b\f¶;_:\b¼\1e\95\9b\f¶;_:\b¼\1e\95\9b\f¶»D\9c\ 5¼S\9dÔº\9b\f¶»®@ã»Yдºj´ç»n´ç»®W[±j´ç»\9e\f¶»×³j±]:\b\8d²»\8f\10\8eº]:\b<®@ã»Yдºj´ç;n´ç»®W[±j´ç;R^\17¼\\9f<± Ëz»¿u\14¼\98>캠Ëz»D\9c\ 5¼S\9dÔº\9b\f¶»_:\b¼\1e\95\9b\f¶»¥Ëz»=\1aQ±R^\17<ýùu»>¶CºR^\17\8d²»\8f\10\8eº]:\b<\9e\f¶»×³j±]:\b<\1d± ¼pE\87±uµÿº­\9a\1d¼ÁËúºuµÿº¿u\14¼\98>캠Ëz»R^\17¼\\9f<± Ëz»®@ã»Yдºj´ç»<\11Ö»áV1»j´ç»\ f1¨»°U\v»]:\b¼"\8d²»\8e\10\8eº]:\b¼®@ã»Yдºj´ç;<\11Ö»áV1»j´ç;l·û»n\87\9b\f¶;D\9c\ 5¼S\9dÔº\9b\f¶;D\9c\ 5¼S\9dÔº\9b\f¶»l·û»n\87\9b\f¶»<\11Ö»áV1»j´ç»®@ã»Yдºj´ç» \8d²»\8f\10\8eº]:\b<\r1¨»°U\v»]:\b<<\11Ö»áV1»j´ç;®@ã»Yдºj´ç;¿u\14¼\98>캠Ëz»¡Ø\v¼x´g» Ëz»l·û»n\87\9b\f¶»D\9c\ 5¼S\9dÔº\9b\f¶»ýùu»>¶CºR^\17<n´g»ró¿ºR^\17<\r1¨»°U\v»]:\b\8d²»\8f\10\8eº]:\b\9a\1d¼ÁËúºuµÿº¼u\14¼\aúu»uµÿº¡Ø\v¼x´g» Ëz»¿u\14¼\98>캠Ëz»£Ëúº\ 2\8cǹ\1e± <\81>ìº>¶Cº\1e± <n´g»ró¿ºR^\17<ýùu»>¶CºR^\17< ± ¼\97µÿºì\86O°R^\17¼³Ëz»ì\86O°¼u\14¼\aúu»uµÿº­\9a\1d¼ÁËúºuµÿº­\9a\1d¼ÁËúºiµÿ:¼u\14¼\aúu»iµÿ:R^\17¼³Ëz»ì\86O° ± ¼\97µÿºì\86\ 3úu»F¶CºP^\17¼t´g»wó¿ºP^\17¼\93>ìºP¶Cº\1d± ¼¸Ëúº\18\8cǹ\1d± ¼¿u\14¼\94>ìº\99Ëz;¡Ø\v¼u´g»\99Ëz;¼u\14¼\aúu»iµÿ:­\9a\1d¼ÁËúºiµÿ:"\8d²»\8e\10\8eº]:\b¼\ f1¨»°U\v»]:\b¼t´g»wó¿ºP^\17¼\ 3úu»F¶CºP^\17¼D\9c\ 5¼S\9dÔº\9b\f¶;l·û»n\87\9b\f¶;¡Ø\v¼u´g»\99Ëz;¿u\14¼\94>ìº\99Ëz;R^\17¼³Ëz»ì\86O°^:\b¼¥\f¶»ì\86O°B\9c\ 5¼$\8d²»uµÿº¼u\14¼\aúu»uµÿº¼u\14¼\aúu»iµÿ:B\9c\ 5¼$\8d²»iµÿ:^:\b¼¥\f¶»ì\86O°R^\17¼³Ëz»ì\86O°t´g»wó¿ºP^\17¼f\87P»²U\v»P^\17¼F\9dÔº\97\10\8eº\1d± ¼\93>ìºP¶Cº\1d± ¼¡Ø\v¼u´g»\99Ëz;n·û»\121¨»\99Ëz;B\9c\ 5¼$\8d²»iµÿ:¼u\14¼\aúu»iµÿ:\ f1¨»°U\v»]:\b¼T^\97»UHJ»]:\b¼f\87P»²U\v»P^\17¼t´g»wó¿ºP^\17¼l·û»n\87\9b\f¶;ó\89â»Y^\97»\9b\f¶;n·û»\121¨»\99Ëz;¡Ø\v¼u´g»\99Ëz;<\11Ö»áV1»j´ç»Ï§À»wº\80»j´ç»T^\97»UHJ»]:\b¼\ f1¨»°U\v»]:\b¼<\11Ö»áV1»j´ç;ϧÀ»wº\80»j´ç;ó\89â»Y^\97»\9b\f¶;l·û»n\87\9b\f¶;l·û»n\87\9b\f¶»ó\89â»Y^\97»\9b\f¶»Ï§À»wº\80»j´ç»<\11Ö»áV1»j´ç»\r1¨»°U\v»]:\b<R^\97»THJ»]:\b<ϧÀ»wº\80»j´ç;<\11Ö»áV1»j´ç;¡Ø\v¼x´g» Ëz»n·û»\121¨» Ëz»ó\89â»Y^\97»\9b\f¶»l·û»n\87\9b\f¶»n´g»ró¿ºR^\17<`\87P»¯U\v»R^\17<R^\97»THJ»]:\b<\r1¨»°U\v»]:\b<¼u\14¼\aúu»uµÿºB\9c\ 5¼$\8d²»uµÿºn·û»\121¨» Ëz»¡Ø\v¼x´g» Ëz»\81>ìº>¶Cº\1e± <7\9dÔº\8c\10\8eº\1e± <`\87P»¯U\v»R^\17<n´g»ró¿ºR^\17<ϧÀ»wº\80»j´ç;\v×£»\12×£»j´ç;˧À»Ô§À»\9b\f¶;ó\89â»Y^\97»\9b\f¶;ó\89â»Y^\97»\9b\f¶»Ë§À»Ô§À»\9b\f¶»\v×£»\12×£»j´ç»Ï§À»wº\80»j´ç»R^\97»THJ»]:\b<pº\80»wº\80»]:\b<\v×£»\12×£»j´ç;ϧÀ»wº\80»j´ç;n·û»\121¨» Ëz»<\11Ö»@\11Ö» Ëz»Ë§À»Ô§À»\9b\f¶»ó\89â»Y^\97»\9b\f¶»`\87P»¯U\v»R^\17<ÓV1»áV1»R^\17<pº\80»wº\80»]:\b<R^\97»THJ»]:\b<B\9c\ 5¼$\8d²»uµÿº©@ã»°@ã»uµÿº<\11Ö»@\11Ö» Ëz»n·û»\121¨» Ëz»7\9dÔº\8c\10\8eº\1e± <=дºYдº\1e± <ÓV1»áV1»R^\17<`\87P»¯U\v»R^\17<^:\b¼¥\f¶»ì\86O°k´ç»t´ç»ì\86O°©@ã»°@ã»uµÿºB\9c\ 5¼$\8d²»uµÿºB\9c\ 5¼$\8d²»iµÿ:©@ã»°@ã»iµÿ:k´ç»t´ç»ì\86O°^:\b¼¥\f¶»ì\86O°f\87P»²U\v»P^\17¼ÙV1»åV1»P^\17¼Jдºgдº\1d± ¼F\9dÔº\97\10\8eº\1d± ¼n·û»\121¨»\99Ëz;<\11Ö»@\11Ö»\99Ëz;©@ã»°@ã»iµÿ:B\9c\ 5¼$\8d²»iµÿ:T^\97»UHJ»]:\b¼rº\80»xº\80»]:\b¼ÙV1»åV1»P^\17¼f\87P»²U\v»P^\17¼ó\89â»Y^\97»\9b\f¶;˧À»Ô§À»\9b\f¶;<\11Ö»@\11Ö»\99Ëz;n·û»\121¨»\99Ëz;ϧÀ»wº\80»j´ç»\v×£»\12×£»j´ç»rº\80»xº\80»]:\b¼T^\97»UHJ»]:\b¼©@ã»°@ã»iµÿ:\e\8d²»F\9c\ 5¼iµÿ:\9c\f¶»b:\b¼ì\86O°k´ç»t´ç»ì\86O°ÙV1»åV1»P^\17¼¥U\v»p\87P»P^\17¼y\10\8eºa\9dÔº\1d± ¼Jдºgдº\1d± ¼<\11Ö»@\11Ö»\99Ëz;\ e1¨»s·û»\99Ëz;\e\8d²»F\9c\ 5¼iµÿ:©@ã»°@ã»iµÿ:rº\80»xº\80»]:\b¼GHJ»Y^\97»]:\b¼¥U\v»p\87P»P^\17¼ÙV1»åV1»P^\17¼Ë§À»Ô§À»\9b\f¶;P^\97»ù\89â»\9b\f¶;\ e1¨»s·û»\99Ëz;<\11Ö»@\11Ö»\99Ëz;\v×£»\12×£»j´ç»pº\80»Ô§À»j´ç»GHJ»Y^\97»]:\b¼rº\80»xº\80»]:\b¼\v×£»\12×£»j´ç;pº\80»Ô§À»j´ç;P^\97»ù\89â»\9b\f¶;˧À»Ô§À»\9b\f¶;˧À»Ô§À»\9b\f¶»P^\97»ù\89â»\9b\f¶»pº\80»Ô§À»j´ç»\v×£»\12×£»j´ç»pº\80»wº\80»]:\b<DHJ»X^\97»]:\b<pº\80»Ô§À»j´ç;\v×£»\12×£»j´ç;<\11Ö»@\11Ö» Ëz»\ e1¨»s·û» Ëz»P^\97»ù\89â»\9b\f¶»Ë§À»Ô§À»\9b\f¶»ÓV1»áV1»R^\17< U\v»k\87P»R^\17<DHJ»X^\97»]:\b<pº\80»wº\80»]:\b<©@ã»°@ã»uµÿº\e\8d²»F\9c\ 5¼uµÿº\ e1¨»s·û» Ëz»<\11Ö»@\11Ö» Ëz»=дºYдº\1e± <p\10\8eºR\9dÔº\1e± < U\v»k\87P»R^\17<ÓV1»áV1»R^\17<k´ç»t´ç»ì\86\9c\f¶»b:\b¼ì\86\e\8d²»F\9c\ 5¼uµÿº©@ã»°@ã»uµÿºDHJ»X^\97»]:\b<\9fU\v»\121¨»]:\b<ÐV1»@\11Ö»j´ç;pº\80»Ô§À»j´ç;\ e1¨»s·û» Ëz»n´g»£Ø\v¼ Ëz»Y\87P»r·û»\9b\f¶»P^\97»ù\89â»\9b\f¶» U\v»k\87P»R^\17<Tó¿ºx´g»R^\17<\9fU\v»\121¨»]:\b<DHJ»X^\97»]:\b<\e\8d²»F\9c\ 5¼uµÿºóùu»¿u\14¼uµÿºn´g»£Ø\v¼ Ëz»\ e1¨»s·û» Ëz»p\10\8eºR\9dÔº\1e± <    ¶Cº\9b>ìº\1e± <Tó¿ºx´g»R^\17< U\v»k\87P»R^\17<\9c\f¶»b:\b¼ì\86\9eËz»V^\17¼ì\86O°óùu»¿u\14¼uµÿº\e\8d²»F\9c\ 5¼uµÿº\e\8d²»F\9c\ 5¼iµÿ:óùu»¿u\14¼iµÿ:\9eËz»V^\17¼ì\86\9c\f¶»b:\b¼ì\86O°¥U\v»p\87P»P^\17¼\ó¿º}´g»P^\17¼\14¶Cº«>ìº\1d± ¼y\10\8eºa\9dÔº\1d± ¼\ e1¨»s·û»\99Ëz;n´g»£Ø\v¼\99Ëz;óùu»¿u\14¼iµÿ:\e\8d²»F\9c\ 5¼iµÿ:GHJ»Y^\97»]:\b¼¢U\v»\141¨»]:\b¼\ó¿º}´g»P^\17¼¥U\v»p\87P»P^\17¼P^\97»ù\89â»\9b\f¶;Y\87P»r·û»\9b\f¶;n´g»£Ø\v¼\99Ëz;\ e1¨»s·û»\99Ëz;pº\80»Ô§À»j´ç»ÐV1»@\11Ö»j´ç»¢U\v»\141¨»]:\b¼GHJ»Y^\97»]:\b¼pº\80»Ô§À»j´ç;ÐV1»@\11Ö»j´ç;Y\87P»r·û»\9b\f¶;P^\97»ù\89â»\9b\f¶;P^\97»ù\89â»\9b\f¶»Y\87P»r·û»\9b\f¶»ÐV1»@\11Ö»j´ç»pº\80»Ô§À»j´ç»\ó¿º}´g»P^\17¼\12¶Cº\vúu»P^\17¼¡\8bǹÎËúº\1d± ¼\14¶Cº«>ìº\1d± ¼n´g»£Ø\v¼\99Ëz;\83>ìºÀu\14¼\99Ëz;\97Ëúº¯\9a\1d¼iµÿ:óùu»¿u\14¼iµÿ:¢U\v»\141¨»]:\b¼s\10\8eº&\8d²»]:\b¼\12¶Cº\vúu»P^\17¼\ó¿º}´g»P^\17¼Y\87P»r·û»\9b\f¶;+\9dÔºE\9c\ 5¼\9b\f¶;\83>ìºÀu\14¼\99Ëz;n´g»£Ø\v¼\99Ëz;ÐV1»@\11Ö»j´ç»9дº²@ã»j´ç»s\10\8eº&\8d²»]:\b¼¢U\v»\141¨»]:\b¼ÐV1»@\11Ö»j´ç;9дº²@ã»j´ç;+\9dÔºE\9c\ 5¼\9b\f¶;Y\87P»r·û»\9b\f¶;Y\87P»r·û»\9b\f¶»+\9dÔºE\9c\ 5¼\9b\f¶»9дº²@ã»j´ç»ÐV1»@\11Ö»j´ç»\9fU\v»\121¨»]:\b<m\10\8eº$\8d²»]:\b<9дº²@ã»j´ç;ÐV1»@\11Ö»j´ç;n´g»£Ø\v¼ Ëz»\83>ìºÀu\14¼ Ëz»+\9dÔºE\9c\ 5¼\9b\f¶»Y\87P»r·û»\9b\f¶»Tó¿ºx´g»R^\17<\a¶Cº\ 6úu»R^\17<m\10\8eº$\8d²»]:\b<\9fU\v»\121¨»]:\b<óùu»¿u\14¼uµÿº\97Ëúº¯\9a\1d¼uµÿº\83>ìºÀu\14¼ Ëz»n´g»£Ø\v¼ Ëz»     ¶Cº\9b>ìº\1e± <\97\8bǹ¼Ëúº\1e± <\a¶Cº\ 6úu»R^\17<Tó¿ºx´g»R^\17<\9eËz»V^\17¼ì\86O°lµÿº"± ¼ì\86\97Ëúº¯\9a\1d¼uµÿºóùu»¿u\14¼uµÿºóùu»¿u\14¼iµÿ:\97Ëúº¯\9a\1d¼iµÿ:lµÿº"± ¼ì\86\9eËz»V^\17¼ì\86\83>ìºÀu\14¼ Ëz»[¼.°T^\17¼ Ëz»>Ýj0^:\b¼\9b\f¶»+\9dÔºE\9c\ 5¼\9b\f¶»\a¶Cº\ 6úu»R^\17<
+U.®¬Ëz»R^\17<\8f\83ð.¡\f¶»]:\b<m\10\8eº$\8d²»]:\b<\97Ëúº¯\9a\1d¼uµÿº>Ýj0\1e± ¼uµÿº[¼.°T^\17¼ Ëz»\83>ìºÀu\14¼ Ëz»\97\8bǹ¼Ëúº\1e± <
+U.®\8eµÿº\1e± <
+U.®¬Ëz»R^\17<\a¶Cº\ 6úu»R^\17<lµÿº"± ¼ì\86\80é\890\ e×#¼ì\86O°>Ýj0\1e± ¼uµÿº\97Ëúº¯\9a\1d¼uµÿº\97Ëúº¯\9a\1d¼iµÿ:>Ýj0\1e± ¼iµÿ:\80é\890\ e×#¼ì\86O°lµÿº"± ¼ì\86\12¶Cº\vúu»P^\17¼n\97⯲Ëz»P^\17¼
+\98\9e. µÿº\1d± ¼¡\8bǹÎËúº\1d± ¼\83>ìºÀu\14¼\99Ëz;[¼.°T^\17¼\99Ëz;>Ýj0\1e± ¼iµÿ:\97Ëúº¯\9a\1d¼iµÿ:s\10\8eº&\8d²»]:\b¼é«\90¯¥\f¶»]:\b¼n\97⯲Ëz»P^\17¼\12¶Cº\vúu»P^\17¼+\9dÔºE\9c\ 5¼\9b\f¶;>Ýj0^:\b¼\9b\f¶;[¼.°T^\17¼\99Ëz;\83>ìºÀu\14¼\99Ëz;9дº²@ã»j´ç»i\f\8e/p´ç»j´ç»é«\90¯¥\f¶»]:\b¼s\10\8eº&\8d²»]:\b¼9дº²@ã»j´ç;i\f\8e/p´ç»j´ç;>Ýj0^:\b¼\9b\f¶;+\9dÔºE\9c\ 5¼\9b\f¶;+\9dÔºE\9c\ 5¼\9b\f¶»>Ýj0^:\b¼\9b\f¶»i\f\8e/p´ç»j´ç»9дº²@ã»j´ç»m\10\8eº$\8d²»]:\b<\8f\83ð.¡\f¶»]:\b<i\f\8e/p´ç»j´ç;9дº²@ã»j´ç;[¼.°T^\17¼\99Ëz;y>ì:Àu\14¼\99Ëz;£Ëú:®\9a\1d¼iµÿ:>Ýj0\1e± ¼iµÿ:é«\90¯¥\f¶»]:\b¼n\10\8e:&\8d²»]:\b¼\ 2¶C:    úu»P^\17¼n\97⯲Ëz»P^\17¼>Ýj0^:\b¼\9b\f¶;9\9dÔ:D\9c\ 5¼\9b\f¶;y>ì:Àu\14¼\99Ëz;[¼.°T^\17¼\99Ëz;i\f\8e/p´ç»j´ç»>д:°@ã»j´ç»n\10\8e:&\8d²»]:\b¼é«\90¯¥\f¶»]:\b¼i\f\8e/p´ç»j´ç;>д:°@ã»j´ç;9\9dÔ:D\9c\ 5¼\9b\f¶;>Ýj0^:\b¼\9b\f¶;>Ýj0^:\b¼\9b\f¶»9\9dÔ:D\9c\ 5¼\9b\f¶»>д:°@ã»j´ç»i\f\8e/p´ç»j´ç»\8f\83ð.¡\f¶»]:\b<n\10\8e:"\8d²»]:\b<>д:°@ã»j´ç;i\f\8e/p´ç»j´ç;[¼.°T^\17¼ Ëz»y>ì:Àu\14¼ Ëz»9\9dÔ:D\9c\ 5¼\9b\f¶»>Ýj0^:\b¼\9b\f¶»
+U.®¬Ëz»R^\17<\ 4¶C:\ 3úu»R^\17<n\10\8e:"\8d²»]:\b<\8f\83ð.¡\f¶»]:\b<>Ýj0\1e± ¼uµÿº£Ëú:®\9a\1d¼uµÿºy>ì:Àu\14¼ Ëz»[¼.°T^\17¼ Ëz»
+U.®\8eµÿº\1e± <\93\8bÇ9»Ëúº\1e± <\ 4¶C:\ 3úu»R^\17<
+U.®¬Ëz»R^\17<\80é\890\ e×#¼ì\86O°}µÿ:!± ¼ì\86O°£Ëú:®\9a\1d¼uµÿº>Ýj0\1e± ¼uµÿº>Ýj0\1e± ¼iµÿ:£Ëú:®\9a\1d¼iµÿ:}µÿ:!± ¼ì\86\80é\890\ e×#¼ì\86O°n\97⯲Ëz»P^\17¼\ 2¶C:    úu»P^\17¼¥\8bÇ9ËËúº\1d± ¼
+\98\9e. µÿº\1d± ¼\ 4¶C:\ 3úu»R^\17<Ró¿:t´g»R^\17<\9fU\v;\101¨»]:\b<n\10\8e:"\8d²»]:\b<£Ëú:®\9a\1d¼uµÿºøùu;½u\14¼uµÿºg´g;£Ø\v¼ Ëz»y>ì:Àu\14¼ Ëz»\93\8bÇ9»Ëúº\1e± <\ 4¶C:\97>ìº\1e± <Ró¿:t´g»R^\17<\ 4¶C:\ 3úu»R^\17<}µÿ:!± ¼ì\86O°¦Ëz;T^\17¼ì\86O°øùu;½u\14¼uµÿº£Ëú:®\9a\1d¼uµÿº£Ëú:®\9a\1d¼iµÿ:øùu;½u\14¼iµÿ:¦Ëz;T^\17¼ì\86O°}µÿ:!± ¼ì\86\ 2¶C:        úu»P^\17¼Só¿:y´g»P^\17¼\13¶C:§>ìº\1d± ¼¥\8bÇ9ËËúº\1d± ¼y>ì:Àu\14¼\99Ëz;g´g;£Ø\v¼\99Ëz;øùu;½u\14¼iµÿ:£Ëú:®\9a\1d¼iµÿ:n\10\8e:&\8d²»]:\b¼ U\v;\141¨»]:\b¼Só¿:y´g»P^\17¼\ 2¶C:     úu»P^\17¼9\9dÔ:D\9c\ 5¼\9b\f¶;]\87P;k·û»\9b\f¶;g´g;£Ø\v¼\99Ëz;y>ì:Àu\14¼\99Ëz;>д:°@ã»j´ç»ÒV1;>\11Ö»j´ç» U\v;\141¨»]:\b¼n\10\8e:&\8d²»]:\b¼>д:°@ã»j´ç;ÒV1;>\11Ö»j´ç;]\87P;k·û»\9b\f¶;9\9dÔ:D\9c\ 5¼\9b\f¶;9\9dÔ:D\9c\ 5¼\9b\f¶»]\87P;k·û»\9b\f¶»ÒV1;>\11Ö»j´ç»>д:°@ã»j´ç»n\10\8e:"\8d²»]:\b<\9fU\v;\101¨»]:\b<ÒV1;>\11Ö»j´ç;>д:°@ã»j´ç;y>ì:Àu\14¼ Ëz»g´g;£Ø\v¼ Ëz»]\87P;k·û»\9b\f¶»9\9dÔ:D\9c\ 5¼\9b\f¶» U\v;\141¨»]:\b¼EHJ;Y^\97»]:\b¼\9fU\v;k\87P»P^\17¼Só¿:y´g»P^\17¼]\87P;k·û»\9b\f¶;O^\97\89â»\9b\f¶;
+1¨;r·û»\99Ëz;g´g;£Ø\v¼\99Ëz;ÒV1;>\11Ö»j´ç»oº\80;Ò§À»j´ç»EHJ;Y^\97»]:\b¼ U\v;\141¨»]:\b¼ÒV1;>\11Ö»j´ç;oº\80;Ò§À»j´ç;O^\97\89â»\9b\f¶;]\87P;k·û»\9b\f¶;]\87P;k·û»\9b\f¶»O^\97\89â»\9b\f¶»oº\80;Ò§À»j´ç»ÒV1;>\11Ö»j´ç»\9fU\v;\101¨»]:\b<BHJ;V^\97»]:\b<oº\80;Ò§À»j´ç;ÒV1;>\11Ö»j´ç;g´g;£Ø\v¼ Ëz»
+1¨;r·û» Ëz»O^\97\89â»\9b\f¶»]\87P;k·û»\9b\f¶»Ró¿:t´g»R^\17<\9dU\v;f\87P»R^\17<BHJ;V^\97»]:\b<\9fU\v;\101¨»]:\b<øùu;½u\14¼uµÿº\1c\8d²;C\9c\ 5¼uµÿº
+1¨;r·û» Ëz»g´g;£Ø\v¼ Ëz»\ 4¶C:\97>ìº\1e± <l\10\8e:L\9dÔº\1e± <\9dU\v;f\87P»R^\17<Ró¿:t´g»R^\17<¦Ëz;T^\17¼ì\86\9e\f¶;_:\b¼ì\86\1c\8d²;C\9c\ 5¼uµÿºøùu;½u\14¼uµÿºøùu;½u\14¼iµÿ:\1c\8d²;C\9c\ 5¼iµÿ:\9e\f¶;_:\b¼ì\86O°¦Ëz;T^\17¼ì\86O°Só¿:y´g»P^\17¼\9fU\v;k\87P»P^\17¼w\10\8e:\\9dÔº\1d± ¼\13¶C:§>ìº\1d± ¼g´g;£Ø\v¼\99Ëz;
+1¨;r·û»\99Ëz;\1c\8d²;C\9c\ 5¼iµÿ:øùu;½u\14¼iµÿ:\1c\8d²;C\9c\ 5¼uµÿº§@ã;ª@ã»uµÿº7\11Ö;>\11Ö» Ëz»
+1¨;r·û» Ëz»l\10\8e:L\9dÔº\1e± <8д:Tдº\1e± <ÏV1;ÛV1»R^\17<\9dU\v;f\87P»R^\17<\9e\f¶;_:\b¼ì\86O°l´ç;n´ç»ì\86O°§@ã;ª@ã»uµÿº\1c\8d²;C\9c\ 5¼uµÿº\1c\8d²;C\9c\ 5¼iµÿ:§@ã;ª@ã»iµÿ:l´ç;n´ç»ì\86\9e\f¶;_:\b¼ì\86\9fU\v;k\87P»P^\17¼ÒV1;ÞV1»P^\17¼Fд:aдº\1d± ¼w\10\8e:\\9dÔº\1d± ¼
+1¨;r·û»\99Ëz;7\11Ö;>\11Ö»\99Ëz;§@ã;ª@ã»iµÿ:\1c\8d²;C\9c\ 5¼iµÿ:EHJ;Y^\97»]:\b¼pº\80;wº\80»]:\b¼ÒV1;ÞV1»P^\17¼\9fU\v;k\87P»P^\17¼O^\97\89â»\9b\f¶;ɧÀ;˧À»\9b\f¶;7\11Ö;>\11Ö»\99Ëz;
+1¨;r·û»\99Ëz;oº\80;Ò§À»j´ç»
+×£;\ f×£»j´ç»pº\80;wº\80»]:\b¼EHJ;Y^\97»]:\b¼oº\80;Ò§À»j´ç;
+×£;\ f×£»j´ç;ɧÀ;˧À»\9b\f¶;O^\97\89â»\9b\f¶;O^\97\89â»\9b\f¶»É§À;˧À»\9b\f¶»
+×£;\ f×£»j´ç»oº\80;Ò§À»j´ç»BHJ;V^\97»]:\b<nº\80;tº\80»]:\b<
+×£;\ f×£»j´ç;oº\80;Ò§À»j´ç;
+1¨;r·û» Ëz»7\11Ö;>\11Ö» Ëz»É§À;˧À»\9b\f¶»O^\97\89â»\9b\f¶»\9dU\v;f\87P»R^\17<ÏV1;ÛV1»R^\17<nº\80;tº\80»]:\b<BHJ;V^\97»]:\b<ɧÀ;˧À»\9b\f¶;î\89â;Q^\97»\9b\f¶;j·û;\121¨»\99Ëz;7\11Ö;>\11Ö»\99Ëz;
+×£;\ f×£»j´ç»Ë§À;tº\80»j´ç»Q^\97;SHJ»]:\b¼pº\80;wº\80»]:\b¼
+×£;\ f×£»j´ç;˧À;tº\80»j´ç;î\89â;Q^\97»\9b\f¶;ɧÀ;˧À»\9b\f¶;ɧÀ;˧À»\9b\f¶»î\89â;Q^\97»\9b\f¶»Ë§À;tº\80»j´ç»
+×£;\ f×£»j´ç»nº\80;tº\80»]:\b<O^\97;MHJ»]:\b<˧À;tº\80»j´ç;
+×£;\ f×£»j´ç;7\11Ö;>\11Ö» Ëz»j·û;\121¨» Ëz»î\89â;Q^\97»\9b\f¶»É§À;˧À»\9b\f¶»ÏV1;ÛV1»R^\17<Y\87P;§U\v»R^\17<O^\97;MHJ»]:\b<nº\80;tº\80»]:\b<§@ã;ª@ã»uµÿº@\9c\ 5<\1c\8d²»uµÿºj·û;\121¨» Ëz»7\11Ö;>\11Ö» Ëz»8д:Tдº\1e± <0\9dÔ:\87\10\8eº\1e± <Y\87P;§U\v»R^\17<ÏV1;ÛV1»R^\17<l´ç;n´ç»ì\86O°]:\b<\9e\f¶»ì\86O°@\9c\ 5<\1c\8d²»uµÿº§@ã;ª@ã»uµÿº§@ã;ª@ã»iµÿ:@\9c\ 5<\1c\8d²»iµÿ:]:\b<\9e\f¶»ì\86O°l´ç;n´ç»ì\86O°ÒV1;ÞV1»P^\17¼]\87P;«U\v»P^\17¼@\9dÔ:\91\10\8eº\1d± ¼Fд:aдº\1d± ¼7\11Ö;>\11Ö»\99Ëz;j·û;\121¨»\99Ëz;@\9c\ 5<\1c\8d²»iµÿ:§@ã;ª@ã»iµÿ:pº\80;wº\80»]:\b¼Q^\97;SHJ»]:\b¼]\87P;«U\v»P^\17¼ÒV1;ÞV1»P^\17¼0\9dÔ:\87\10\8eº\1e± <x>ì:7¶Cº\1e± <e´g;eó¿ºR^\17<Y\87P;§U\v»R^\17<]:\b<\9e\f¶»ì\86O°P^\17<¥Ëz»ì\86O°ºu\14<÷ùu»uµÿº@\9c\ 5<\1c\8d²»uµÿº@\9c\ 5<\1c\8d²»iµÿ:ºu\14<÷ùu»iµÿ:P^\17<¥Ëz»ì\86O°]:\b<\9e\f¶»ì\86O°]\87P;«U\v»P^\17¼i´g;jó¿ºP^\17¼\8a>ì:F¶Cº\1d± ¼@\9dÔ:\91\10\8eº\1d± ¼j·û;\121¨»\99Ëz;\9fØ\v<x´g»\99Ëz;ºu\14<÷ùu»iµÿ:@\9c\ 5<\1c\8d²»iµÿ:Q^\97;SHJ»]:\b¼\v1¨;­U\v»]:\b¼i´g;jó¿ºP^\17¼]\87P;«U\v»P^\17¼î\89â;Q^\97»\9b\f¶;e·û;^\87\9b\f¶;\9fØ\v<x´g»\99Ëz;j·û;\121¨»\99Ëz;˧À;tº\80»j´ç»8\11Ö;ÛV1»j´ç»\v1¨;­U\v»]:\b¼Q^\97;SHJ»]:\b¼Ë§À;tº\80»j´ç;8\11Ö;ÛV1»j´ç;e·û;^\87\9b\f¶;î\89â;Q^\97»\9b\f¶;î\89â;Q^\97»\9b\f¶»e·û;^\87\9b\f¶»8\11Ö;ÛV1»j´ç»Ë§À;tº\80»j´ç»O^\97;MHJ»]:\b<        1¨;©U\v»]:\b<8\11Ö;ÛV1»j´ç;˧À;tº\80»j´ç;j·û;\121¨» Ëz»\9fØ\v<x´g» Ëz»e·û;^\87\9b\f¶»î\89â;Q^\97»\9b\f¶»Y\87P;§U\v»R^\17<e´g;eó¿ºR^\17<     1¨;©U\v»]:\b<O^\97;MHJ»]:\b<@\9c\ 5<\1c\8d²»uµÿººu\14<÷ùu»uµÿº\9fØ\v<x´g» Ëz»j·û;\121¨» Ëz»8\11Ö;ÛV1»j´ç;ª@ã;Mдºj´ç;?\9c\ 5<;\9dÔº\9b\f¶;e·û;^\87\9b\f¶;e·û;^\87\9b\f¶»?\9c\ 5<;\9dÔº\9b\f¶»ª@ã;Mдºj´ç»8\11Ö;ÛV1»j´ç» 1¨;©U\v»]:\b<\e\8d²;\84\10\8eº]:\b<ª@ã;Mдºj´ç;8\11Ö;ÛV1»j´ç;\9fØ\v<x´g» Ëz»½u\14<\99>캠Ëz»?\9c\ 5<;\9dÔº\9b\f¶»e·û;^\87\9b\f¶»e´g;eó¿ºR^\17<òùu;-¶CºR^\17<\e\8d²;\84\10\8eº]:\b<     1¨;©U\v»]:\b<ºu\14<÷ùu»uµÿº©\9a\1d<¢Ëúºuµÿº½u\14<\99>캠Ëz»\9fØ\v<x´g» Ëz»x>ì:7¶Cº\1e± <\99Ëú:ü\8bǹ\1e± <òùu;-¶CºR^\17<e´g;eó¿ºR^\17<P^\17<¥Ëz»ì\86\1d± <}µÿºì\86O°©\9a\1d<¢Ëúºuµÿººu\14<÷ùu»uµÿººu\14<÷ùu»iµÿ:©\9a\1d<¢Ëúºiµÿ:\1d± <}µÿºì\86O°P^\17<¥Ëz»ì\86O°i´g;jó¿ºP^\17¼õùu;4¶CºP^\17¼¬Ëú:
+\8cǹ\1d± ¼\8a>ì:F¶Cº\1d± ¼\9fØ\v<x´g»\99Ëz;½u\14<\99>ìº\99Ëz;©\9a\1d<¢Ëúºiµÿ:ºu\14<÷ùu»iµÿ:\v1¨;­U\v»]:\b¼\1d\8d²;\87\10\8eº]:\b¼õùu;4¶CºP^\17¼i´g;jó¿ºP^\17¼e·û;^\87\9b\f¶;?\9c\ 5<;\9dÔº\9b\f¶;½u\14<\99>ìº\99Ëz;\9fØ\v<x´g»\99Ëz;8\11Ö;ÛV1»j´ç»ª@ã;Mдºj´ç»\1d\8d²;\87\10\8eº]:\b¼\v1¨;­U\v»]:\b¼©\9a\1d<¢Ëúºiµÿ:\19± <)|\83°iµÿ:\b×#<ÍìÀ°ì\86\1d± <}µÿºì\86O°õùu;4¶CºP^\17¼\9dËz;\14¾A±P^\17¼~µÿ:õ8V±\1d± ¼¬Ëú:
+\8cǹ\1d± ¼½u\14<\99>ìº\99Ëz;P^\17<®W[±\99Ëz;\19± <)|\83°iµÿ:©\9a\1d<¢Ëúºiµÿ:\1d\8d²;\87\10\8eº]:\b¼\9b\f¶;\14¾A±]:\b¼\9dËz;\14¾A±P^\17¼õùu;4¶CºP^\17¼?\9c\ 5<;\9dÔº\9b\f¶;Y:\b<ÍìÀ°\9b\f¶;P^\17<®W[±\99Ëz;½u\14<\99>ìº\99Ëz;ª@ã;Mдºj´ç»i´ç;p]þ°j´ç»\9b\f¶;\14¾A±]:\b¼\1d\8d²;\87\10\8eº]:\b¼ª@ã;Mдºj´ç;i´ç;p]þ°j´ç;Y:\b<ÍìÀ°\9b\f¶;?\9c\ 5<;\9dÔº\9b\f¶;?\9c\ 5<;\9dÔº\9b\f¶»Y:\b<ÍìÀ°\9b\f¶»i´ç;p]þ°j´ç»ª@ã;Mдºj´ç»\e\8d²;\84\10\8eº]:\b<\98\f¶;ëa2±]:\b<i´ç;p]þ°j´ç;ª@ã;Mдºj´ç;½u\14<\99>캠Ëz»P^\17<®W[± Ëz»Y:\b<ÍìÀ°\9b\f¶»?\9c\ 5<;\9dÔº\9b\f¶»òùu;-¶CºR^\17<\98Ëz;3C-±R^\17<\98\f¶;ëa2±]:\b<\e\8d²;\84\10\8eº]:\b\9a\1d<¢Ëúºuµÿº\19± <)|\83°uµÿºP^\17<®W[± Ëz»½u\14<\99>캠Ëz»\99Ëú:ü\8bǹ\1e± <lµÿ:RÈX±\1e± <\98Ëz;3C-±R^\17<òùu;-¶CºR^\17<\1d± <}µÿºì\86\b×#<ÍìÀ°ì\86\19± <)|\83°uµÿº©\9a\1d<¢ËúºuµÿºY:\b<ÍìÀ°\9b\f¶»>\9c\ 5<"\9dÔ:\9b\f¶»©@ã;+д:j´ç»i´ç;p]þ°j´ç»\98\f¶;ëa2±]:\b<\19\8d²;V\10\8e:]:\b<©@ã;+д:j´ç;i´ç;p]þ°j´ç;P^\17<®W[± Ëz»¼u\14<b>ì: Ëz»>\9c\ 5<"\9dÔ:\9b\f¶»Y:\b<ÍìÀ°\9b\f¶»\98Ëz;3C-±R^\17<ðùu;ÔµC:R^\17<\19\8d²;V\10\8e:]:\b<\98\f¶;ëa2±]:\b<\19± <)|\83°uµÿº§\9a\1d<\90Ëú:uµÿº¼u\14<b>ì: Ëz»P^\17<®W[± Ëz»lµÿ:RÈX±\1e± <\99Ëú:"\8bÇ9\1e± <ðùu;ÔµC:R^\17<\98Ëz;3C-±R^\17<\b×#<ÍìÀ°ì\86\e± <eµÿ:ì\86O°§\9a\1d<\90Ëú:uµÿº\19± <)|\83°uµÿº\19± <)|\83°iµÿ:§\9a\1d<\90Ëú:iµÿ:\e± <eµÿ:ì\86\b×#<ÍìÀ°ì\86\9dËz;\14¾A±P^\17¼õùu;ÓµC:P^\17¼«Ëú:2\8bÇ9\1d± ¼~µÿ:õ8V±\1d± ¼P^\17<®W[±\99Ëz;¼u\14<b>ì:\99Ëz;§\9a\1d<\90Ëú:iµÿ:\19± <)|\83°iµÿ:\9b\f¶;\14¾A±]:\b¼\1c\8d²;V\10\8e:]:\b¼õùu;ÓµC:P^\17¼\9dËz;\14¾A±P^\17¼Y:\b<ÍìÀ°\9b\f¶;>\9c\ 5<"\9dÔ:\9b\f¶;¼u\14<b>ì:\99Ëz;P^\17<®W[±\99Ëz;i´ç;p]þ°j´ç»©@ã;+д:j´ç»\1c\8d²;V\10\8e:]:\b¼\9b\f¶;\14¾A±]:\b¼i´ç;p]þ°j´ç;©@ã;+д:j´ç;>\9c\ 5<"\9dÔ:\9b\f¶;Y:\b<ÍìÀ°\9b\f¶;§\9a\1d<\90Ëú:iµÿ:·u\14<ëùu;iµÿ:M^\17<\97Ëz;ì\86\e± <eµÿ:ì\86O°õùu;ÓµC:P^\17¼g´g;9ó¿:P^\17¼\89>ì:صC:\1d± ¼«Ëú:2\8bÇ9\1d± ¼¼u\14<b>ì:\99Ëz;\9fØ\v<\´g;\99Ëz;·u\14<ëùu;iµÿ:§\9a\1d<\90Ëú:iµÿ:\1c\8d²;V\10\8e:]:\b¼    1¨;\93U\v;]:\b¼g´g;9ó¿:P^\17¼õùu;ÓµC:P^\17¼>\9c\ 5<"\9dÔ:\9b\f¶;a·û;P\87P;\9b\f¶;\9fØ\v<\´g;\99Ëz;¼u\14<b>ì:\99Ëz;©@ã;+д:j´ç»5\11Ö;ÉV1;j´ç»     1¨;\93U\v;]:\b¼\1c\8d²;V\10\8e:]:\b¼©@ã;+д:j´ç;5\11Ö;ÉV1;j´ç;a·û;P\87P;\9b\f¶;>\9c\ 5<"\9dÔ:\9b\f¶;>\9c\ 5<"\9dÔ:\9b\f¶»a·û;P\87P;\9b\f¶»5\11Ö;ÉV1;j´ç»©@ã;+д:j´ç»\19\8d²;V\10\8e:]:\b<\ 61¨;\92U\v;]:\b<5\11Ö;ÉV1;j´ç;©@ã;+д:j´ç;¼u\14<b>ì: Ëz»\9fØ\v<\´g; Ëz»a·û;P\87P;\9b\f¶»>\9c\ 5<"\9dÔ:\9b\f¶»ðùu;ÔµC:R^\17<b´g;8ó¿:R^\17<\ 61¨;\92U\v;]:\b<\19\8d²;V\10\8e:]:\b\9a\1d<\90Ëú:uµÿº·u\14<ëùu;uµÿº\9fØ\v<\´g; Ëz»¼u\14<b>ì: Ëz»\99Ëú:"\8bÇ9\1e± <x>ì:ʵC:\1e± <b´g;8ó¿:R^\17<ðùu;ÔµC:R^\17<\e± <eµÿ:ì\86O°M^\17<\97Ëz;ì\86O°·u\14<ëùu;uµÿº§\9a\1d<\90Ëú:uµÿº\ 61¨;\92U\v;]:\b<K^\97;4HJ;]:\b<ɧÀ;jº\80;j´ç;5\11Ö;ÉV1;j´ç;\9fØ\v<\´g; Ëz»i·û;\ 41¨; Ëz»ç\89â;I^\97;\9b\f¶»a·û;P\87P;\9b\f¶»b´g;8ó¿:R^\17<T\87P;\90U\v;R^\17<K^\97;4HJ;]:\b<\ 61¨;\92U\v;]:\b<·u\14<ëùu;uµÿº=\9c\ 5<\14\8d²;uµÿºi·û;\ 41¨; Ëz»\9fØ\v<\´g; Ëz»x>ì:ʵC:\1e± </\9dÔ:O\10\8e:\1e± <T\87P;\90U\v;R^\17<b´g;8ó¿:R^\17<M^\17<\97Ëz;ì\86O°Y:\b<\95\f¶;ì\86O°=\9c\ 5<\14\8d²;uµÿº·u\14<ëùu;uµÿº·u\14<ëùu;iµÿ:=\9c\ 5<\14\8d²;iµÿ:Y:\b<\95\f¶;ì\86O°M^\17<\97Ëz;ì\86O°g´g;9ó¿:P^\17¼Y\87P;\92U\v;P^\17¼=\9dÔ:Y\10\8e:\1d± ¼\89>ì:صC:\1d± ¼\9fØ\v<\´g;\99Ëz;i·û;\ 41¨;\99Ëz;=\9c\ 5<\14\8d²;iµÿ:·u\14<ëùu;iµÿ: 1¨;\93U\v;]:\b¼N^\97;7HJ;]:\b¼Y\87P;\92U\v;P^\17¼g´g;9ó¿:P^\17¼a·û;P\87P;\9b\f¶;ç\89â;I^\97;\9b\f¶;i·û;\ 41¨;\99Ëz;\9fØ\v<\´g;\99Ëz;5\11Ö;ÉV1;j´ç»É§À;jº\80;j´ç»N^\97;7HJ;]:\b¼     1¨;\93U\v;]:\b¼5\11Ö;ÉV1;j´ç;ɧÀ;jº\80;j´ç;ç\89â;I^\97;\9b\f¶;a·û;P\87P;\9b\f¶;a·û;P\87P;\9b\f¶»ç\89â;I^\97;\9b\f¶»É§À;jº\80;j´ç»5\11Ö;ÉV1;j´ç»Y\87P;\92U\v;P^\17¼ÍV1;ÂV1;P^\17¼Bд:)д:\1d± ¼=\9dÔ:Y\10\8e:\1d± ¼i·û;\ 41¨;\99Ëz;7\11Ö;0\11Ö;\99Ëz;\9e@ã;\9e@ã;iµÿ:=\9c\ 5<\14\8d²;iµÿ:N^\97;7HJ;]:\b¼lº\80;hº\80;]:\b¼ÍV1;ÂV1;P^\17¼Y\87P;\92U\v;P^\17¼ç\89â;I^\97;\9b\f¶;Á§À;§À;\9b\f¶;7\11Ö;0\11Ö;\99Ëz;i·û;\ 41¨;\99Ëz;ɧÀ;jº\80;j´ç»\a×£;\ 4×£;j´ç»lº\80;hº\80;]:\b¼N^\97;7HJ;]:\b¼É§À;jº\80;j´ç;\a×£;\ 4×£;j´ç;Á§À;§À;\9b\f¶;ç\89â;I^\97;\9b\f¶;ç\89â;I^\97;\9b\f¶»Á§À;§À;\9b\f¶»\a×£;\ 4×£;j´ç»É§À;jº\80;j´ç»K^\97;4HJ;]:\b<jº\80;fº\80;]:\b<\a×£;\ 4×£;j´ç;ɧÀ;jº\80;j´ç;i·û;\ 41¨; Ëz»7\11Ö;0\11Ö; Ëz»Á§À;§À;\9b\f¶»ç\89â;I^\97;\9b\f¶»T\87P;\90U\v;R^\17<ÊV1;ÀV1;R^\17<jº\80;fº\80;]:\b<K^\97;4HJ;]:\b<=\9c\ 5<\14\8d²;uµÿº\9e@ã;\9e@ã;uµÿº7\11Ö;0\11Ö; Ëz»i·û;\ 41¨; Ëz»/\9dÔ:O\10\8e:\1e± <7д:\1cд:\1e± <ÊV1;ÀV1;R^\17<T\87P;\90U\v;R^\17<Y:\b<\95\f¶;ì\86O°a´ç;b´ç;ì\86\9e@ã;\9e@ã;uµÿº=\9c\ 5<\14\8d²;uµÿº=\9c\ 5<\14\8d²;iµÿ:\9e@ã;\9e@ã;iµÿ:a´ç;b´ç;ì\86O°Y:\b<\95\f¶;ì\86O°7\11Ö;0\11Ö; Ëz» 1¨;b·û; Ëz»G^\97\89â;\9b\f¶»Á§À;§À;\9b\f¶»ÊV1;ÀV1;R^\17<\99U\v;J\87P;R^\17<;HJ;G^\97;]:\b<jº\80;fº\80;]:\b<\9e@ã;\9e@ã;uµÿº\12\8d²;;\9c\ 5<uµÿº     1¨;b·û; Ëz»7\11Ö;0\11Ö; Ëz»7д:\1cд:\1e± <j\10\8e:\14\9dÔ:\1e± <\99U\v;J\87P;R^\17<ÊV1;ÀV1;R^\17<a´ç;b´ç;ì\86\93\f¶;W:\b\86\12\8d²;;\9c\ 5<uµÿº\9e@ã;\9e@ã;uµÿº\9e@ã;\9e@ã;iµÿ:\12\8d²;;\9c\ 5<iµÿ:\93\f¶;W:\b\86O°a´ç;b´ç;ì\86O°ÍV1;ÂV1;P^\17¼\9bU\v;L\87P;P^\17¼s\10\8e:"\9dÔ:\1d± ¼Bд:)д:\1d± ¼7\11Ö;0\11Ö;\99Ëz;     1¨;b·û;\99Ëz;\12\8d²;;\9c\ 5<iµÿ:\9e@ã;\9e@ã;iµÿ:lº\80;hº\80;]:\b¼=HJ;I^\97;]:\b¼\9bU\v;L\87P;P^\17¼ÍV1;ÂV1;P^\17¼Á§À;§À;\9b\f¶;G^\97\89â;\9b\f¶;     1¨;b·û;\99Ëz;7\11Ö;0\11Ö;\99Ëz;\a×£;\ 4×£;j´ç»kº\80;ŧÀ;j´ç»=HJ;I^\97;]:\b¼lº\80;hº\80;]:\b¼\a×£;\ 4×£;j´ç;kº\80;ŧÀ;j´ç;G^\97\89â;\9b\f¶;Á§À;§À;\9b\f¶;Á§À;§À;\9b\f¶»G^\97\89â;\9b\f¶»kº\80;ŧÀ;j´ç»\a×£;\ 4×£;j´ç»jº\80;fº\80;]:\b<;HJ;G^\97;]:\b<kº\80;ŧÀ;j´ç;\a×£;\ 4×£;j´ç; 1¨;b·û;\99Ëz;f´g;\9bØ\v<\99Ëz;ãùu;´u\14<iµÿ:\12\8d²;;\9c\ 5<iµÿ:=HJ;I^\97;]:\b¼\99U\v;\ 21¨;]:\b¼Kó¿:Y´g;P^\17¼\9bU\v;L\87P;P^\17¼G^\97\89â;\9b\f¶;J\87P;]·û;\9b\f¶;f´g;\9bØ\v<\99Ëz;     1¨;b·û;\99Ëz;kº\80;ŧÀ;j´ç»ÊV1;2\11Ö;j´ç»\99U\v;\ 21¨;]:\b¼=HJ;I^\97;]:\b¼kº\80;ŧÀ;j´ç;ÊV1;2\11Ö;j´ç;J\87P;]·û;\9b\f¶;G^\97\89â;\9b\f¶;G^\97\89â;\9b\f¶»J\87P;]·û;\9b\f¶»ÊV1;2\11Ö;j´ç»kº\80;ŧÀ;j´ç»;HJ;G^\97;]:\b<\97U\v;\01¨;]:\b<ÊV1;2\11Ö;j´ç;kº\80;ŧÀ;j´ç;     1¨;b·û; Ëz»f´g;\9bØ\v< Ëz»J\87P;]·û;\9b\f¶»G^\97\89â;\9b\f¶»\99U\v;J\87P;R^\17<Ió¿:W´g;R^\17<\97U\v;\01¨;]:\b<;HJ;G^\97;]:\b<\12\8d²;;\9c\ 5<uµÿºãùu;´u\14<uµÿºf´g;\9bØ\v< Ëz»     1¨;b·û; Ëz»j\10\8e:\14\9dÔ:\1e± <þµC:]>ì:\1e± <Ió¿:W´g;R^\17<\99U\v;J\87P;R^\17<\93\f¶;W:\b\86\8fËz;J^\17\86O°ãùu;´u\14<uµÿº\12\8d²;;\9c\ 5<uµÿº\12\8d²;;\9c\ 5<iµÿ:ãùu;´u\14<iµÿ:\8fËz;J^\17\86\93\f¶;W:\b\86\9bU\v;L\87P;P^\17¼Kó¿:Y´g;P^\17¼
+¶C:n>ì:\1d± ¼s\10\8e:"\9dÔ:\1d± ¼Ió¿:W´g;R^\17<óµC:ãùu;R^\17<`\10\8e:\10\8d²;]:\b<\97U\v;\01¨;]:\b<ãùu;´u\14<uµÿº\80Ëú:¤\9a\1d<uµÿºw>ì:¸u\14< Ëz»f´g;\9bØ\v< Ëz»þµC:]>ì:\1e± <\8a\8bÇ9~Ëú:\1e± <óµC:ãùu;R^\17<Ió¿:W´g;R^\17<\8fËz;J^\17\86O°Uµÿ:\16± <ì\86\80Ëú:¤\9a\1d<uµÿºãùu;´u\14<uµÿºãùu;´u\14<iµÿ:\80Ëú:¤\9a\1d<iµÿ:Uµÿ:\16± <ì\86\8fËz;J^\17\86O°Kó¿:Y´g;P^\17¼õµC:æùu;P^\17¼\92\8bÇ9\8fËú:\1d± ¼
+¶C:n>ì:\1d± ¼f´g;\9bØ\v<\99Ëz;w>ì:¸u\14<\99Ëz;\80Ëú:¤\9a\1d<iµÿ:ãùu;´u\14<iµÿ:\99U\v;\ 21¨;]:\b¼c\10\8e:\13\8d²;]:\b¼õµC:æùu;P^\17¼Kó¿:Y´g;P^\17¼J\87P;]·û;\9b\f¶;\15\9dÔ:;\9c\ 5<\9b\f¶;w>ì:¸u\14<\99Ëz;f´g;\9bØ\v<\99Ëz;ÊV1;2\11Ö;j´ç».д:¢@ã;j´ç»c\10\8e:\13\8d²;]:\b¼\99U\v;\ 21¨;]:\b¼ÊV1;2\11Ö;j´ç;.д:¢@ã;j´ç;\15\9dÔ:;\9c\ 5<\9b\f¶;J\87P;]·û;\9b\f¶;J\87P;]·û;\9b\f¶»\15\9dÔ:;\9c\ 5<\9b\f¶».д:¢@ã;j´ç»ÊV1;2\11Ö;j´ç»\97U\v;\01¨;]:\b<`\10\8e:\10\8d²;]:\b<.д:¢@ã;j´ç;ÊV1;2\11Ö;j´ç;f´g;\9bØ\v< Ëz»w>ì:¸u\14< Ëz»\15\9dÔ:;\9c\ 5<\9b\f¶»J\87P;]·û;\9b\f¶»\15\9dÔ:;\9c\ 5<\9b\f¶;@\v;±T:\b<\9b\f¶;[¼.°K^\17<\99Ëz;w>ì:¸u\14<\99Ëz;.д:¢@ã;j´ç»u?Ò°a´ç;j´ç»ÑÎ\94°\90\f¶;]:\b¼c\10\8e:\13\8d²;]:\b¼.д:¢@ã;j´ç;u?Ò°a´ç;j´ç;@\v;±T:\b<\9b\f¶;\15\9dÔ:;\9c\ 5<\9b\f¶;\15\9dÔ:;\9c\ 5<\9b\f¶»@\v;±T:\b<\9b\f¶»u?Ò°a´ç;j´ç».д:¢@ã;j´ç»`\10\8e:\10\8d²;]:\b<³I©°\8e\f¶;]:\b<u?Ò°a´ç;j´ç;.д:¢@ã;j´ç;w>ì:¸u\14< Ëz»[¼.°K^\17< Ëz»@\v;±T:\b<\9b\f¶»\15\9dÔ:;\9c\ 5<\9b\f¶»óµC:ãùu;R^\17<\1d²W°\8aËz;R^\17<³I©°\8e\f¶;]:\b<`\10\8e:\10\8d²;]:\b<\80Ëú:¤\9a\1d<uµÿºÏÍ0±\13± <uµÿº[¼.°K^\17< Ëz»w>ì:¸u\14< Ëz»\8a\8bÇ9~Ëú:\1e± <\8av&¯Pµÿ:\1e± <\1d²W°\8aËz;R^\17<óµC:ãùu;R^\17<Uµÿ:\16± <ì\86O°@\v\ 1×#<ì\86O°ÏÍ0±\13± <uµÿº\80Ëú:¤\9a\1d<uµÿº\80Ëú:¤\9a\1d<iµÿ:ÏÍ0±\13± <iµÿ:@\v\ 1×#<ì\86O°Uµÿ:\16± <ì\86O°õµC:æùu;P^\17¼\1d²W°\8cËz;P^\17¼\8c\1cίaµÿ:\1d± ¼\92\8bÇ9\8fËú:\1d± ¼w>ì:¸u\14<\99Ëz;[¼.°K^\17<\99Ëz;ÏÍ0±\13± <iµÿ:\80Ëú:¤\9a\1d<iµÿ:c\10\8e:\13\8d²;]:\b¼ÑÎ\94°\90\f¶;]:\b¼\1d²W°\8cËz;P^\17¼õµC:æùu;P^\17¼\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0(¤Cº\0\0\0\80`µÄ<\1eÖ\1d»\0\0\0\80ÍfÖ<PþGº\0\0\0\80\0\0\0\0`´6¹\0\0\0\80\0\0\0\0Ðÿ\92;\0\0\0\80\0\0\0\0\83;\0\0\0\80\0\0\0\0ë\11\19<\0\0\0\80\0\0\0\0\19d<<\0\0\0\80\0\0\0\0\9d¹\0\0\0\80"í¨<\92\18\85»\0\0\0\80`µÄ<\1eÖ\1d»\0\0\0\80\0\0\0\0(¤Cº\0\0\0\80\0\0\0\0,õ×:\0\0\0\80\0\0\0\0\b\80»:\0\0\0\80\0\0\0\0\83;\0\0\0\80\0\0\0\0Ðÿ\92;\0\0\0\80\0\0\0\0\f³\r;\0\0\0\80°\80^<=Ú¢»\0\0\0\80"í¨<\92\18\85»\0\0\0\80\0\0\0\0\9d¹\0\0\0\80\0\0\0\0\80õí8\0\0\0\80\0\0\0\0\0\87\9b8\0\0\0\80\0\0\0\0\b\80»:\0\0\0\80\0\0\0\0,õ×:\0\0\0\80\0\0\0\0\ 3<\0\0\0\80\966\91;ú­-»\0\0\0\80°\80^<=Ú¢»\0\0\0\80\0\0\0\0\f³\r;\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\87\9b8\0\0\0\80\0\0\0\0\80õí8\0\0\0\80\0\0\0\0Rd\9b<\0\0\0\80\0\0\0\0@ E;\0\0\0\80\966\91;ú­-»\0\0\0\80\0\0\0\0\ 3<\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0nW¹<\0\0\0\80\0\0\0\0    ^i<\0\0\0\80\0\0\0\0@ E;\0\0\0\80\0\0\0\0Rd\9b<\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80¾Ný<\0X\ eµ\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\9a[þ<À 8¸\0\0\0\80{äÿ<0\80ÿ¸\0\0\0\80½Ný¼\0Ð\rµ\0\0\0\80\99[þ¼à\1e\0\0\0\80<P\0=\98ÂF¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80B\16\0=¤Vb¹\0\0\0\80kúþ<|ÂF¹\0\0\0\80ÇÒý<\10\80ÿ¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80ãGý<¢ 8¸\0\0\0\80\8aJý<\0L\ eµ\0\0\0\80<Ký<\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80=Ký<\0\0\0\0\0\0\0\80BKý<\0\0\0\0\0\0\0\80CKý<\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80=Ký<\0\0\0\0\0\0\0\80CKý<\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80CKý¼\0\0\0\0\0\0\0\80=Ký¼\0\0\0\0\0\0\0\80BKý¼\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80AKý¼\0\0\0\0\0\0\0\80=Ký¼\0\0\0\0\0\0\0\80<Ký¼\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\8aJý¼\0h\ eµ\0\0\0\80âGý¼Ë 8¸\0\0\0\80ÆÒý¼ø\7fÿ¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80kúþ¼TÂF¹\0\0\0\80A\16\0½\Vb¹\0\0\0\80;P\0½@ÂF¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80zäÿ¼P\7fÿ¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0Ø\15£<\0\0\0\80\0\0\0\0\84<\0\0\0\80\0\0\0\0     ^i<\0\0\0\80\0\0\0\0nW¹<\0\0\0\80\0\0\0\0`´6¹\0\0\0\80ÍfÖ<PþGº\0\0\0\80¾Ný<\0X\ eµ\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\19d<<\0\0\0\80\0\0\0\0ë\11\19<\0\0\0\80\0\0\0\0\84<\0\0\0\80\0\0\0\0Ø\15£<\0\0\0\80\0\0\0\0\84<\0\0\0\80\0\0\0\0Èy@;\0\0\0\80\0\0\0\0\92þ_»\0\0\0\80\0\0\0\0     ^i<\0\0\0\80ÍfÖ<PþGº\0\0\0\808\86ë<>zκ\0\0\0\80\9a[þ<À 8¸\0\0\0\80¾Ný<\0X\ eµ\0\0\0\80\0\0\0\0ë\11\19<\0\0\0\80\0\0\0\0Ê\8f\9b;\0\0\0\80\0\0\0\0Èy@;\0\0\0\80\0\0\0\0\84<\0\0\0\80`µÄ<\1eÖ\1d»\0\0\0\80çdä<×\93\94»\0\0\0\808\86ë<>zκ\0\0\0\80ÍfÖ<PþGº\0\0\0\80\0\0\0\0\83;\0\0\0\80\0\0\0\0`\1e:;\0\0\0\80\0\0\0\0Ê\8f\9b;\0\0\0\80\0\0\0\0ë\11\19<\0\0\0\80"í¨<\92\18\85»\0\0\0\80\ eþ¯<&\9dü»\0\0\0\80çdä<×\93\94»\0\0\0\80`µÄ<\1eÖ\1d»\0\0\0\80\0\0\0\0\b\80»:\0\0\0\80\0\0\0\0À­g:\0\0\0\80\0\0\0\0`\1e:;\0\0\0\80\0\0\0\0\83;\0\0\0\80°\80^<=Ú¢»\0\0\0\80>\aO<Lß!¼\0\0\0\80\ eþ¯<&\9dü»\0\0\0\80"í¨<\92\18\85»\0\0\0\80\0\0\0\0\0\87\9b8\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0À­g:\0\0\0\80\0\0\0\0\b\80»:\0\0\0\80\966\91;ú­-»\0\0\0\806­¶;\ 501¼\90V¸9>\aO<Lß!¼\0\0\0\80°\80^<=Ú¢»\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\87\9b8\0\0\0\80\0\0\0\0@ E;\0\0\0\80\b¾\97:Ø2\v¼\0\0\0\806­¶;\ 501¼\90V¸9\966\91;ú­-»\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0     ^i<\0\0\0\80\0\0\0\0\92þ_»\0\0\0\80\b¾\97:Ø2\v¼\0\0\0\80\0\0\0\0@ E;\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\10\8b\959\0\0\0\80\0\0\0\0À­g:\0\0\0\806­¶;\ 501¼\90V¸9a]¸;\1c\19¶¼\1eH`;\e\94\99;fΤ¼xÔ:;>\aO<Lß!¼\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\b¾\97:Ø2\v¼\0\0\0\80\1fan;\15n\94¼Ûþ\10;a]¸;\1c\19¶¼\1eH`;6­¶;\ 501¼\90V¸9\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\92þ_»\0\0\0\80\0\0\0\0\10)$¼\0\0\0\80\1fan;\15n\94¼Ûþ\10;\b¾\97:Ø2\v¼\0\0\0\80\0\0\0\0Èy@;\0\0\0\80\0\0\0\0D"¤»\0\0\0\80\0\0\0\0\10)$¼\0\0\0\80\0\0\0\0\92þ_»\0\0\0\808\86ë<>zκ\0\0\0\80dÈð<\1f$\1c»\0\0\0\80{äÿ<0\80ÿ¸\0\0\0\80\9a[þ<À 8¸\0\0\0\80\0\0\0\0Ê\8f\9b;\0\0\0\80\0\0\0\0à2û9\0\0\0\80\0\0\0\0D"¤»\0\0\0\80\0\0\0\0Èy@;\0\0\0\80çdä<×\93\94»\0\0\0\80\ 4\9e´<ÇÐØ»\0\0\0\80dÈð<\1f$\1c»\0\0\0\808\86ë<>zκ\0\0\0\80\0\0\0\0`\1e:;\0\0\0\80\0\0\0\0H$·:\0\0\0\80\0\0\0\0à2û9\0\0\0\80\0\0\0\0Ê\8f\9b;\0\0\0\80\ eþ¯<&\9dü»\0\0\0\80þ\90=;\80tp¼\90wñ:\ 4\9e´<ÇÐØ»\0\0\0\80çdä<×\93\94»\0\0\0\80\0\0\0\0À­g:\0\0\0\80\0\0\0\0\10\8b\959\0\0\0\80\0\0\0\0H$·:\0\0\0\80\0\0\0\0`\1e:;\0\0\0\80>\aO<Lß!¼\0\0\0\80\e\94\99;fΤ¼xÔ:;þ\90=;\80tp¼\90wñ:\ eþ¯<&\9dü»\0\0\0\80\0\0\0\0à2û9\0\0\0\80\0\0\0\0ó4.»\0\0\0\80\0\0\0\0h\19ý»\0\0\0\80\0\0\0\0D"¤»\0\0\0\80\ 4\9e´<ÇÐØ»\0\0\0\80\17oF<ôE\ 5¼\0\0\0\80pÛâ<}¦B»\0\0\0\80dÈð<\1f$\1c»\0\0\0\80\0\0\0\0H$·:\0\0\0\80\0\0\0\0Àm\119\0\0\0\80\0\0\0\0ó4.»\0\0\0\80\0\0\0\0à2û9\0\0\0\80þ\90=;\80tp¼\90wñ:¨\88/;³\8a\8b¼ì\89Õ:\17oF<ôE\ 5¼\0\0\0\80\ 4\9e´<ÇÐØ»\0\0\0\80\0\0\0\0\10\8b\959\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0Àm\119\0\0\0\80\0\0\0\0H$·:\0\0\0\80\e\94\99;fΤ¼xÔ:;¼\ 5þ:\16
+­¼°\82\9a\88/;³\8a\8b¼ì\89Õ:þ\90=;\80tp¼\90wñ:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\10\8b\959\0\0\0\80a]¸;\1c\19¶¼\1eH`;à` :èüµ¼$\1aC:¼\ 5þ:\16
+­¼°\82\9a:\e\94\99;fΤ¼xÔ:;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\1fan;\15n\94¼Ûþ\10\93,;\9e\11±¼0ñÑ:à` :èüµ¼$\1aC:a]¸;\1c\19¶¼\1eH`;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\10)$¼\0\0\0\80X×¾:\98\88\v)h:Ê\93,;\9e\11±¼0ñÑ:\1fan;\15n\94¼Ûþ\10;\0\0\0\0D"¤»\0\0\0\80\0\0\0\0h\19ý»\0\0\0\80X×¾:\98\88\v)h:\0\0\0\0\10)$¼\0\0\0\80dÈð<\1f$\1c»\0\0\0\80pÛâ<}¦B»\0\0\0\80<P\0=\98ÂF¹\0\0\0\80{äÿ<0\80ÿ¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Ê\93,;\9e\11±¼0ñÑ:\0:ø7\80¿¨¼Àü\967\0\0\0\0ù?»¼\0\0\0\80à` :èüµ¼$\1aC:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80X×¾:\98\88\v)h:H\9cQ:\8f\1f\83¼Jþþ9\0:ø7\80¿¨¼Àü\96\93,;\9e\11±¼0ñÑ:\0\0\0\0h\19ý»\0\0\0\80\0\0\0\0Åa\ e¼\0\0\0\80H\9cQ:\8f\1f\83¼Jþþ9X×¾:\98\88\v)h:pÛâ<}¦B»\0\0\0\80\1f\8aÌ<ý´P»\0\0\0\80B\16\0=¤Vb¹\0\0\0\80<P\0=\98ÂF¹\0\0\0\80\0\0\0\0ó4.»\0\0\0\80\0\0\0\0     µP»\0\0\0\80\0\0\0\0Åa\ e¼\0\0\0\80\0\0\0\0h\19ý»\0\0\0\80\17oF<ôE\ 5¼\0\0\0\80Ð\12o;üE0¼\\1c\97:\1f\8aÌ<ý´P»\0\0\0\80pÛâ<}¦B»\0\0\0\80\0\0\0\0Àm\119\0\0\0\80\0\0\0\0\80Vb¹\0\0\0\80\0\0\0\0     µP»\0\0\0\80\0\0\0\0ó4.»\0\0\0\80¨\88/;³\8a\8b¼ì\89Õ:ÜÒË:\9dà\89¼ ôw:Ð\12o;üE0¼\\1c\97:\17oF<ôE\ 5¼\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\80Vb¹\0\0\0\80\0\0\0\0Àm\119\0\0\0\80¼\ 5þ:\16
+­¼°\82\9a:\0X!6G\81¨¼\0@Ä5ÜÒË:\9dà\89¼ ôw:¨\88/;³\8a\8b¼ì\89Õ:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80à` :èüµ¼$\1aC:\0\0\0\0ù?»¼\0\0\0\80\0X!6G\81¨¼\0@Ä5¼\ 5þ:\16
+­¼°\82\9a\12o;üE0¼\\1c\97:\b8­;D\89(¼¼9\9d:3À¹<~¦B»\0\0\0\80\1f\8aÌ<ý´P»\0\0\0\80\0\0\0\0\80Vb¹\0\0\0\80\0\0\0\0\0Þò¸\0\0\0\80\0\0\0\0M¯/»\0\0\0\80\0\0\0\0 µP»\0\0\0\80ÜÒË:\9dà\89¼ ôw:\80\13k:¢9w¼\88ü\ e:\b8­;D\89(¼¼9\9d\12o;üE0¼\\1c\97:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0Þò¸\0\0\0\80\0\0\0\0\80Vb¹\0\0\0\80\0X!6G\81¨¼\0@Ä5\0\0\0\0\9b¼\0\0\0\80\80\13k:¢9w¼\88ü\ e:ÜÒË:\9dà\89¼ ôw:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0ù?»¼\0\0\0\80\0\0\0\0e\f«¼\0\0\0\80\0\0\0\0\9b¼\0\0\0\80\0X!6G\81¨¼\0@Ä5\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0:ø7\80¿¨¼Àü\967\0\0\0\0\8b\86\99¼\0\0\0\80\0\0\0\0e\f«¼\0\0\0\80\0\0\0\0ù?»¼\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80H\9cQ:\8f\1f\83¼Jþþ9Àâ\ 29\10»b¼F9\9f8\0\0\0\0\8b\86\99¼\0\0\0\80\0:ø7\80¿¨¼Àü\967\0\0\0\0Åa\ e¼\0\0\0\80\0\0\0\0h\19ý»\0\0\0\80Àâ\ 29\10»b¼F9\9f8H\9cQ:\8f\1f\83¼Jþþ9\1f\8aÌ<ý´P»\0\0\0\803À¹<~¦B»\0\0\0\80kúþ<|ÂF¹\0\0\0\80B\16\0=¤Vb¹\0\0\0\80\0\0\0\0     µP»\0\0\0\80\0\0\0\0M¯/»\0\0\0\80\0\0\0\0h\19ý»\0\0\0\80\0\0\0\0Åa\ e¼\0\0\0\80\0\0\0\0\8b\86\99¼\0\0\0\80\0°-6½Ýg¼\0@Ó5\0\0\0\0¨Ë\83¼\0\0\0\80\0\0\0\0e\f«¼\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Àâ\ 29\10»b¼F9\9f8\0\0\0\0\v)$¼\0\0\0\80\0°-6½Ýg¼\0@Ó5\0\0\0\0\8b\86\99¼\0\0\0\80\0\0\0\0h\19ý»\0\0\0\80\0\0\0\0Ô\11¬»\0\0\0\80\0\0\0\0\v)$¼\0\0\0\80Àâ\ 29\10»b¼F9\9f83À¹<~¦B»\0\0\0\80\7f´¯<\1e$\1c»\0\0\0\80ÇÒý<\10\80ÿ¸\0\0\0\80kúþ<|ÂF¹\0\0\0\80\0\0\0\0M¯/»\0\0\0\80\0\0\0\0»@¿º\0\0\0\80\0\0\0\0Ô\11¬»\0\0\0\80\0\0\0\0h\19ý»\0\0\0\80\b8­;D\89(¼¼9\9d\10±;õì   ¼0\9c\83:\7f´¯<\1e$\1c»\0\0\0\803À¹<~¦B»\0\0\0\80\0\0\0\0\0Þò¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0»@¿º\0\0\0\80\0\0\0\0M¯/»\0\0\0\80\80\13k:¢9w¼\88ü\ e:X\9e<:÷)G¼àtå9¯\10±;õì     ¼0\9c\83:\b8­;D\89(¼¼9\9d:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0Þò¸\0\0\0\80\0\0\0\0\9b¼\0\0\0\80\0Æ\848^ðv¼\80\85!8X\9e<:÷)G¼àtå9\80\13k:¢9w¼\88ü\ e:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0e\f«¼\0\0\0\80\0\0\0\0¨Ë\83¼\0\0\0\80\0Æ\848^ðv¼\80\85!8\0\0\0\0\9b¼\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0J\f\93¹\0\0\0\80\0\0\0\0»@¿º\0\0\0\80X\9e<:÷)G¼àtå90²Ô9\9d\8f\ 5¼ _\819²ÿ\9c\a¼»\18é/:¯\10±;õì     ¼0\9c\83:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0Æ\848^ðv¼\80\85!8\0C´9%\ 5(¼@J[90²Ô9\9d\8f\ 5¼ _\819X\9e<:÷)G¼àtå9\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0¨Ë\83¼\0\0\0\80\0\93\ 69~%)¼ ¶£8\0C´9%\ 5(¼@J[9\0Æ\848^ðv¼\80\85!8\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0°-6½Ýg¼\0@Ó5\0ø\986UI\v¼\0\12:6\0\93\ 69~%)¼ ¶£8\0\0\0\0¨Ë\83¼\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\v)$¼\0\0\0\80\0\0\0\0Ô\1c´»\0\0\0\80\0ø\986UI\v¼\0\12:6\0°-6½Ýg¼\0@Ó5\0\0\0\0Ô\11¬»\0\0\0\80\0\0\0\0\1f\92\19»\0\0\0\80\0\0\0\0Ô\1c´»\0\0\0\80\0\0\0\0\v)$¼\0\0\0\80\7f´¯<\1e$\1c»\0\0\0\80²ì­<4zκ\0\0\0\80ãGý<¢ 8¸\0\0\0\80ÇÒý<\10\80ÿ¸\0\0\0\80\0\0\0\0»@¿º\0\0\0\80\0\0\0\0J\f\93¹\0\0\0\80\0\0\0\0\1f\92\19»\0\0\0\80\0\0\0\0Ô\11¬»\0\0\0\80¯\10±;õì     ¼0\9c\83:²ÿ\9c\a¼»\18é/:²ì­<4zκ\0\0\0\80\7f´¯<\1e$\1c»\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0Ô\1c´»\0\0\0\80\0\0\0\0éoߺ\0\0\0\80\0\0\0\0\90\92\0\0\0\80\0ø\986UI\v¼\0\12:6\0\0\0\0\1f\92\19»\0\0\0\80\0\0\0\0\84\95 ¹\0\0\0\80\0\0\0\0éoߺ\0\0\0\80\0\0\0\0Ô\1c´»\0\0\0\80²ì­<4zκ\0\0\0\80]¬±<6þGº\0\0\0\80\8aJý<\0L\ eµ\0\0\0\80ãGý<¢ 8¸\0\0\0\80\0\0\0\0J\f\93¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\84\95 ¹\0\0\0\80\0\0\0\0\1f\92\19»\0\0\0\80²ÿ\9c\a¼»\18é/:\ e/\ f\ e\0<:8]¬±<6þGº\0\0\0\80²ì­<4zκ\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0J\f\93¹\0\0\0\800²Ô9\9d\8f\ 5¼ _\819hn3:KÐ\8b»\80\9dï8\ e/\ f\ e\0<:8²ÿ\9c\a¼»\18é/:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0C´9%\ 5(¼@J[9\0øå8)Ʀ»\80á\8b8hn3:KÐ\8b»\80\9dï80²Ô9\9d\8f\ 5¼ _\819\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\93\ 69~%)¼ ¶£8\0\88m7\ e¡\9b»\0{\107\0øå8)Ʀ»\80á\8b8\0C´9%\ 5(¼@J[9\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0ø\986UI\v¼\0\12:6\0\0\0\0\90\92\0\0\0\80\0\88m7\ e¡\9b»\0{\107\0\93\ 69~%)¼ ¶£8hn3:KÐ\8b»\80\9dï8\86?v;9Á\r»\0z\0:}\e<<\104Gº\0\0\0\80\ e/\ f\ e\0<:8\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0øå8)Ʀ»\80á\8b8\0À/5\8cË·º\0À©4\86?v;9Á\r»\0z\0:hn3:KÐ\8b»\80\9dï8\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\88m7\ e¡\9b»\0{\107\0\0\0\0\88º\0\0\0\80\0À/5\8cË·º\0À©4\0øå8)Ʀ»\80á\8b8\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\90\92\0\0\0\80\0\0\0\088á¹\0\0\0\80\0\0\0\0\88º\0\0\0\80\0\88m7\ e¡\9b»\0{\107\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0éoߺ\0\0\0\80\0\0\0\0\0ËK·\0\0\0\80\0\0\0\088á¹\0\0\0\80\0\0\0\0\90\92\0\0\0\80\0\0\0\0\84\95 ¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0ËK·\0\0\0\80\0\0\0\0éoߺ\0\0\0\80]¬±<6þGº\0\0\0\80v\9c·<h´6¹\0\0\0\80<Ký<\0\0\0\0\0\0\0\80\8aJý<\0L\ eµ\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\84\95 ¹\0\0\0\80\ e/\ f\ e\0<:8}\e<<\104Gº\0\0\0\80v\9c·<h´6¹\0\0\0\80]¬±<6þGº\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0ËK·\0\0\0\80\07³8\0\97ø`\ 4Z8@èk9\10»\80¹Ð}\ f9\0\0\0\088á¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\07³8\0\97ø`\ 4Z8\0\0\0\0\0ËK·\0\0\0\80v\9c·<h´6¹\0\0\0\80Oaº<\0\0\0\0\0\0\0\80=Ký<\0\0\0\0\0\0\0\80<Ký<\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80}\e<<\104Gº\0\0\0\80ªhU<\0ãv·\0\0\0\80Oaº<\0\0\0\0\0\0\0\80v\9c·<h´6¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\86?v;9Á\r»\0z\0:O×Ð;\18¢îº\94Û\82:ªhU<\0ãv·\0\0\0\80}\e<<\104Gº\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0À/5\8cË·º\0À©4\b\v#:0¯¬¹`¿>9O×Ð;\18¢îº\94Û\82:\86?v;9Á\r»\0z\0:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\88º\0\0\0\80\90 \899\10¨\95¹ Ñ&9\b\v#:0¯¬¹`¿>9\0À/5\8cË·º\0À©4\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\088á¹\0\0\0\80@èk9\10»\80¹Ð}\ f9\90 \899\10¨\95¹ Ñ&9\0\0\0\0\88º\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\b\v#:0¯¬¹`¿>9\ fÛ\ f;(Ê\ 5»h!\95:ãØý;\1c¦S»Ðêë:O×Ð;\18¢îº\94Û\82:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\90 \899\10¨\95¹ Ñ&9^ó ;$\8e\16»\86ѧ:\ fÛ\ f;(Ê\ 5»h!\95:\b\v#:0¯¬¹`¿>9\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80@èk9\10»\80¹Ð}\ f9pÕ\r;\fË\1a»Ò\8a¬:^ó     ;$\8e\16»\86ѧ:\90 \899\10¨\95¹ Ñ&9\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\07³8\0\97ø`\ 4Z8\80\90Ü:°·ðºÖ(\86:pÕ\r;\fË\1a»Ò\8a¬:@èk9\10»\80¹Ð}\ f9\0\0\0\0\0\0\0\0\0\0\0\80Häh:Ð+~ºZ¨\r:\80\90Ü:°·ðºÖ(\86:\07³8\0\97ø`\ 4Z8Oaº<\0\0\0\0\0\0\0\80Oaº<\0\0\0\0\0\0\0\80BKý<\0\0\0\0\0\0\0\80=Ký<\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80à\8bC9ÀiU¹`âí8Häh:Ð+~ºZ¨\r:\0\0\0\0\0\0\0\0\0\0\0\80ªhU<\0ãv·\0\0\0\80©\rV<\0\0\0\0\0\0\0\80Oaº<\0\0\0\0\0\0\0\80Oaº<\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80à\8bC9ÀiU¹`âí8\0\0\0\0\0\0\0\0\0\0\0\80O×Ð;\18¢îº\94Û\82:ãØý;\1c¦S»Ðêë:©\rV<\0\0\0\0\0\0\0\80ªhU<\0ãv·\0\0\0\80Häh:Ð+~ºZ¨\r:]JP;pRc»;cý:?£\9b;¼Û©»»U=;\80\90Ü:°·ðºÖ(\86:Oaº<\0\0\0\0\0\0\0\80Raº<\0\0\0\0\0\0\0\80CKý<\0\0\0\0\0\0\0\80BKý<\0\0\0\0\0\0\0\80à\8bC9ÀiU¹`âí8\80\ 1»:´\17̺\9c~c:]JP;pRc»;cý:Häh:Ð+~ºZ¨\r\rV<\0\0\0\0\0\0\0\80­\rV<\0\0\0\0\0\0\0\80Raº<\0\0\0\0\0\0\0\80Oaº<\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80 ¹Y9À\9dm¹`n\ 49\80\ 1»:´\17̺\9c~c:à\8bC9ÀiU¹`âí8ãØý;\1c¦S»Ðêë:\fhh:\b\83\8b»:\82\e\rV<\0\0\0\0\0\0\0\80©\rV<\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80 ¹Y9À\9dm¹`n\ 49\0\0\0\0\0\0\0\0\0\0\0\80\ fÛ\ f;(Ê\ 5»h!\95:4\14\97;'G\99»uÚ*;\fhh:\b\83\8b»:\82\e;ãØý;\1c¦S»Ðêë:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80^ó ;$\8e\16»\86ѧ: \83©;%\0¹»£6N;4\14\97;'G\99»uÚ*;\ fÛ\ f;(Ê\ 5»h!\95:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\r;\fË\1a»Ò\8a¬:÷Ö´;ô\Å»Rþ[; \83©;%\0¹»£6N;^ó     ;$\8e\16»\86ѧ:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\80\90Ü:°·ðºÖ(\86:?£\9b;¼Û©»»U=;÷Ö´;ô\Å»Rþ[;pÕ\r;\fË\1a»Ò\8a¬:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80 \83©;%\0¹»£6N;>%\r
+\1a¼z´«;â\bê;<Ðó»\8câ\87;4\14\97;'G\99»uÚ*;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80÷Ö´;ô\Å»Rþ[;Ìe\19<çi'¼!\9cº;>%\r
+\1a¼z´«; \83©;%\0¹»£6N;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\9b;¼Û©»»U=;\1a°\b<X-\15¼FH¦;Ìe\19<çi'¼!\9cº;÷Ö´;ô\Å»Rþ[;]JP;pRc»;cý:óHÄ;:8Ö»bÈn;\1a°\b<X-\15¼FH¦;?£\9b;¼Û©»»U=;Raº<\0\0\0\0\0\0\0\80Raº<\0\0\0\0\0\0\0\80=Ký<\0\0\0\0\0\0\0\80CKý<\0\0\0\0\0\0\0\80\80\ 1»:´\17̺\9c~c:3\88Q;T­d»âåþ:óHÄ;:8Ö»bÈn;]JP;pRc»;cý:­\rV<\0\0\0\0\0\0\0\80²\rV<\0\0\0\0\0\0\0\80Raº<\0\0\0\0\0\0\0\80Raº<\0\0\0\0\0\0\0\80 ¹Y9À\9dm¹`n\ 49 Är:Py\84ºè©\13:3\88Q;T­d»âåþ:\80\ 1»:´\17̺\9c~c:\fhh:\b\83\8b»:\82\e;\84l=:&`\91»~\v";²\rV<\0\0\0\0\0\0\0\80­\rV<\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80 Är:Py\84ºè©\13: ¹Y9À\9dm¹`n\ 494\14\97;'G\99»uÚ*;â\bê;<Ðó»\8câ\87;\84l=:&`\91»~\v";\fhh:\b\83\8b»:\82\e;Raº<\0\0\0\0\0\0\0\80Waº<\0\0\0\0\0\0\0\80CKý<\0\0\0\0\0\0\0\80=Ký<\0\0\0\0\0\0\0\803\88Q;T­d»âåþ:þ\19\9a;\8e.¨»Ww;;-\1f\b<-\8f\14¼ø\97¥;óHÄ;:8Ö»bÈn;²\rV<\0\0\0\0\0\0\0\80²\rV<\0\0\0\0\0\0\0\80Waº<\0\0\0\0\0\0\0\80Raº<\0\0\0\0\0\0\0\80 Är:Py\84ºè©\13:\88¥×:°Yëº\f+\83\19\9a;\8e.¨»Ww;;3\88Q;T­d»âåþ:\84l=:&`\91»~\v";¬ö·:D\fr»¦æ\ 6\rV<\0\0\0\0\0\0\0\80²\rV<\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80°\87\9e\ 3­¸\0Ú@8\88¥×:°Yëº\f+\83: Är:Py\84ºè©\13\bê;<Ðó»\8câ\87;à]\13<q\a\e¼$ά;¬ö·:D\fr»¦æ\ 6;\84l=:&`\91»~\v";\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80°\87\9e\ 3­¸\0Ú@8\0\0\0\0\0\0\0\0\0\0\0\80>%\r
+\1a¼z´«;Zó:<J\bL¼lmã;à]\13<q\a\e¼$ά;â\bê;<Ðó»\8câ\87;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Ìe\19<çi'¼!\9cº;©øM<\81Ê`¼û\90ú;Zó:<J\bL¼lmã;>%\r
+\1a¼z´«;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\1a°\b<X-\15¼FH¦;>\869<ÔyJ¼C±á;©øM<\81Ê`¼û\90ú;Ìe\19<çi'¼!\9cº;óHÄ;:8Ö»bÈn;-\1f\b<-\8f\14¼ø\97¥;>\869<ÔyJ¼C±á;\1a°\b<X-\15¼FH¦;Zó:<J\bL¼lmã;\0\0\0\0@é_¼æ\95ù;\0\0\0\0nÂ$¼Á¦·;à]\13<q\a\e¼$ά;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80©øM<\81Ê`¼û\90ú;\0\0\0\0îÙx¼V±
+<\0\0\0\0@é_¼æ\95ù;Zó:<J\bL¼lmã;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80>\869<ÔyJ¼C±á;\0\0\0\0<é_¼â\95ù;\0\0\0\0îÙx¼V±
+<©øM<\81Ê`¼û\90ú;-\1f\b<-\8f\14¼ø\97¥;\0\0\0\0pÂ$¼Â¦·;\0\0\0\0<é_¼â\95ù;>\869<ÔyJ¼C±á;Waº<\0\0\0\0\0\0\0\80\0\0\0\0\80f\16¹\0¥§8\0\0\0\0\0\0\0\0\0\0\0\80CKý<\0\0\0\0\0\0\0\80þ\19\9a;\8e.¨»Ww;;\0\0\0\0\09½»iëR;\0\0\0\0pÂ$¼Â¦·;-\1f\b<-\8f\14¼ø\97¥;²\rV<\0\0\0\0\0\0\0\80\0\0\0\0\f¯
+»ô\95\9a:\0\0\0\0\80f\16¹\0¥§8Waº<\0\0\0\0\0\0\0\80\88¥×:°Yëº\f+\83:\0\0\0\0ü®
+»ä\95\9a:\0\0\0\0\09½»iëR;þ\19\9a;\8e.¨»Ww;;¬ö·:D\fr»¦æ\ 6;\0\0\0\0\09½»iëR;\0\0\0\0\f¯
+»ô\95\9a\rV<\0\0\0\0\0\0\0\80°\87\9e\ 3­¸\0Ú@8\0\0\0\0@f\16¹\0¥§8\0\0\0\0ü®
+»ä\95\9a:\88¥×:°Yëº\f+\83:à]\13<q\a\e¼$ά;\0\0\0\0nÂ$¼Á¦·;\0\0\0\0\09½»iëR;¬ö·:D\fr»¦æ\ 6;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0@f\16¹\0¥§8°\87\9e\ 3­¸\0Ú@8\0\0\0\0\09½»iëR;ü\19\9a»\8e.¨»Ww;;/\1f\b¼.\8f\14¼ø\97¥;\0\0\0\0pÂ$¼Â¦·;\0\0\0\0\f¯
+»ô\95\9a\r\0\0\0\0\0\0\0\80Vaº¼\0\0\0\0\0\0\0\80\0\0\0\0\80f\16¹\0¥§8\0\0\0\0ü®
+»ä\95\9a:\96¥×º¸Yëº\f+\83\19\9a»\8e.¨»Ww;;\0\0\0\0\09½»iëR;\0\0\0\0\09½»iëR;±ö·ºH\fr»¦æ\ 6\r\0\0\0\0\0\0\0\80\0\0\0\0\f¯
+»ô\95\9a:\0\0\0\0@f\16¹\0¥§8\0\88\9e¸@\ 4­¸\0Ú@8\96¥×º¸Yëº\f+\83:\0\0\0\0ü®
+»ä\95\9a:\0\0\0\0nÂ$¼Á¦·;â]\13¼r\a\e¼$ά;±ö·ºH\fr»¦æ\ 6;\0\0\0\0\09½»iëR;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\88\9e¸@\ 4­¸\0Ú@8\0\0\0\0@f\16¹\0¥§8\0\0\0\0@é_¼æ\95ù;Yó:¼J\bL¼lmã;â]\13¼r\a\e¼$ά;\0\0\0\0nÂ$¼Á¦·;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0îÙx¼V±
+<ªøM¼\82Ê`¼û\90ú;Yó:¼J\bL¼lmã;\0\0\0\0@é_¼æ\95ù;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0<é_¼â\95ù;B\869¼ÕyJ¼C±á;ªøM¼\82Ê`¼û\90ú;\0\0\0\0îÙx¼V±
+<\0\0\0\0pÂ$¼Â¦·;/\1f\b¼.\8f\14¼ø\97¥;B\869¼ÕyJ¼C±á;\0\0\0\0<é_¼â\95ù;\0\0\0\0\80f\16¹\0¥§8Vaº¼\0\0\0\0\0\0\0\80CKý¼\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80ªøM¼\82Ê`¼û\90ú;Íe\19¼éi'¼!\9cº;<%\r¼Á
+\1a¼z´«;Yó:¼J\bL¼lmã;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80B\869¼ÕyJ¼C±á;\1c°\b¼Z-\15¼FH¦;Íe\19¼éi'¼!\9cº;ªøM¼\82Ê`¼û\90ú;/\1f\b¼.\8f\14¼ø\97¥;öHÄ»>8Ö»bÈn;\1c°\b¼Z-\15¼FH¦;B\869¼ÕyJ¼C±á;Vaº¼\0\0\0\0\0\0\0\80Qaº¼\0\0\0\0\0\0\0\80=Ký¼\0\0\0\0\0\0\0\80CKý¼\0\0\0\0\0\0\0\80ü\19\9a»\8e.¨»Ww;;,\88Q»T­d»âåþ:öHÄ»>8Ö»bÈn;/\1f\b¼.\8f\14¼ø\97¥;²\r\0\0\0\0\0\0\0\80±\r\0\0\0\0\0\0\0\80Qaº¼\0\0\0\0\0\0\0\80Vaº¼\0\0\0\0\0\0\0\80\96¥×º¸Yëº\f+\83:0Ärºly\84ºè©\13:,\88Q»T­d»âåþ:ü\19\9a»\8e.¨»Ww;;±ö·ºH\fr»¦æ\ 6;\8cl=º(`\91»~\v";±\r\0\0\0\0\0\0\0\80²\r\0\0\0\0\0\0\0\80\0\88\9e¸@\ 4­¸\0Ú@8\0\0\0\0\0\0\0\0\0\0\0\800Ärºly\84ºè©\13:\96¥×º¸Yëº\f+\83:â]\13¼r\a\e¼$ά;ä\bê»CÐó»\8câ\87;\8cl=º(`\91»~\v";±ö·ºH\fr»¦æ\ 6;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\88\9e¸@\ 4­¸\0Ú@8Yó:¼J\bL¼lmã;<%\r¼Á
+\1a¼z´«;ä\bê»CÐó»\8câ\87;â]\13¼r\a\e¼$ά;±\r\0\0\0\0\0\0\0\80«\r\0\0\0\0\0\0\0\80Qaº¼\0\0\0\0\0\0\0\80Qaº¼\0\0\0\0\0\0\0\800Ärºly\84ºè©\13:\0¹Y¹ \9em¹`n\ 49p\ 1»º¸\17̺\9c~c:,\88Q»T­d»âåþ:\8cl=º(`\91»~\v";\ 4hhº
+\83\8b»:\82\e\r\0\0\0\0\0\0\0\80±\r\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0¹Y¹ \9em¹`n\ 490Ärºly\84ºè©\13\bê»CÐó»\8câ\87;3\14\97».G\99»uÚ*;\ 4hhº
+\83\8b»:\82\e;\8cl=º(`\91»~\v";\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80<%\r¼Á
+\1a¼z´«;\1c\83©»&\0¹»£6N;3\14\97».G\99»uÚ*;ä\bê»CÐó»\8câ\87;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Íe\19¼éi'¼!\9cº;øÖ´»ú\Å»Rþ[;\1c\83©»&\0¹»£6N;<%\r¼Á
+\1a¼z´«;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\1c°\b¼Z-\15¼FH¦;A£\9b»ÂÛ©»»U=;øÖ´»ú\Å»Rþ[;Íe\19¼éi'¼!\9cº;öHÄ»>8Ö»bÈn;_JP»|Rc»;cý:A£\9b»ÂÛ©»»U=;\1c°\b¼Z-\15¼FH¦;Qaº¼\0\0\0\0\0\0\0\80Qaº¼\0\0\0\0\0\0\0\80BKý¼\0\0\0\0\0\0\0\80=Ký¼\0\0\0\0\0\0\0\80,\88Q»T­d»âåþ:p\ 1»º¸\17̺\9c~c:_JP»|Rc»;cý:öHÄ»>8Ö»bÈn;øÖ´»ú\Å»Rþ[;lÕ\r»\18Ë\1a»Ò\8a¬:Tó       »(\8e\16»\86ѧ:\1c\83©»&\0¹»£6N;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\9b»ÂÛ©»»U=;\84\90ܺȷðºÖ(\86:lÕ\r»\18Ë\1a»Ò\8a¬:øÖ´»ú\Å»Rþ[;_JP»|Rc»;cý:8ähº\0,~ºZ¨\r:\84\90ܺȷðºÖ(\86:A£\9b»ÂÛ©»»U=;Qaº¼\0\0\0\0\0\0\0\80Paº¼\0\0\0\0\0\0\0\80AKý¼\0\0\0\0\0\0\0\80BKý¼\0\0\0\0\0\0\0\80p\ 1»º¸\17̺\9c~c:@\8b\0jU¹`âí88ähº\0,~ºZ¨\r:_JP»|Rc»;cý:«\r\0\0\0\0\0\0\0\80§\r\0\0\0\0\0\0\0\80Paº¼\0\0\0\0\0\0\0\80Qaº¼\0\0\0\0\0\0\0\80\0¹Y¹ \9em¹`n\ 49\0\0\0\0\0\0\0\0\0\0\0\80@\8b\0jU¹`âí8p\ 1»º¸\17̺\9c~c:\ 4hhº
+\83\8b»:\82\e;äØý»"¦S»Ðêë:§\r\0\0\0\0\0\0\0\80«\r\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0¹Y¹ \9em¹`n\ 493\14\97».G\99»uÚ*;\vÛ\ f»:Ê\ 5»h!\95:äØý»"¦S»Ðêë:\ 4hhº
+\83\8b»:\82\e;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\1c\83©»&\0¹»£6N;Tó  »(\8e\16»\86ѧ:\vÛ\ f»:Ê\ 5»h!\95:3\14\97».G\99»uÚ*;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80@\8b\0jU¹`âí8äØý»"¦S»Ðêë:K×л$¢îº\94Û\82:¨hU¼\0åv·\0\0\0\80§\r\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\vÛ\ f»:Ê\ 5»h!\95
+#º°¯¬¹`¿>9K×л$¢îº\94Û\82:äØý»"¦S»Ðêë:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Tó   »(\8e\16»\86ѧ:P \89¹\10¨\95¹ Ñ&9à
+#º°¯¬¹`¿>9\vÛ\ f»:Ê\ 5»h!\95:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\r»\18Ë\1a»Ò\8a¬:Àçk¹\90»\80¹Ð}\ f9P \89¹\10¨\95¹ Ñ&9Tó   »(\8e\16»\86ѧ:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\84\90ܺȷðºÖ(\86:\806³¸À\98ø`\ 4Z8Àçk¹\90»\80¹Ð}\ f9lÕ\r»\18Ë\1a»Ò\8a¬:8ähº\0,~ºZ¨\r:\0\0\0\0\0\0\0\0\0\0\0\80\806³¸À\98ø`\ 4Z8\84\90ܺȷðºÖ(\86:Paº¼\0\0\0\0\0\0\0\80Naº¼\0\0\0\0\0\0\0\80=Ký¼\0\0\0\0\0\0\0\80AKý¼\0\0\0\0\0\0\0\80@\8b\0jU¹`âí8\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\808ähº\0,~ºZ¨\r\r\0\0\0\0\0\0\0\80¨hU¼\0åv·\0\0\0\80Naº¼\0\0\0\0\0\0\0\80Paº¼\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\806³¸À\98ø`\ 4Z8\0\0\0\0\0ÙK·\0\0\0\80\0\0\0\0¸8á¹\0\0\0\80Àçk¹\90»\80¹Ð}\ f9\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0ÙK·\0\0\0\80\806³¸À\98ø`\ 4Z8Naº¼\0\0\0\0\0\0\0\80t\9c·¼Ð´6¹\0\0\0\80<Ký¼\0\0\0\0\0\0\0\80=Ký¼\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80¨hU¼\0åv·\0\0\0\80z\e\1c4Gº\0\0\0\80t\9c·¼Ð´6¹\0\0\0\80Naº¼\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80K×л$¢îº\94Û\82:~?v»?Á\r»\0z\0:z\e\1c4Gº\0\0\0\80¨hU¼\0åv·\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80à
+#º°¯¬¹`¿>9\0à.µ¬Ë·º\0À©4~?v»?Á\r»\0z\0:K×л$¢îº\94Û\82:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\89¹\10¨\95¹ Ñ&9\0\0\0\0\88º\0\0\0\80\0à.µ¬Ë·º\0À©4à
+#º°¯¬¹`¿>9\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Àçk¹\90»\80¹Ð}\ f9\0\0\0\0¸8á¹\0\0\0\80\0\0\0\0\88º\0\0\0\80\89¹\10¨\95¹ Ñ&9\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0à.µ¬Ë·º\0À©4\80õå¸1Ʀ»\80á\8b8Hn3ºNÐ\8b»\80\9dï8~?v»?Á\r»\0z\0:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\88º\0\0\0\80\0\80\ e¡\9b»\0{\107\80õå¸1Ʀ»\80á\8b8\0à.µ¬Ë·º\0À©4\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0¸8á¹\0\0\0\80\0\0\0\0\9f\92\0\0\0\80\0\80\ e¡\9b»\0{\107\0\0\0\0\88º\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0ÙK·\0\0\0\80\0\0\0\0\ 3pߺ\0\0\0\80\0\0\0\0\9f\92\0\0\0\80\0\0\0\0¸8á¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\96 ¹\0\0\0\80\0\0\0\0\ 3pߺ\0\0\0\80\0\0\0\0\0ÙK·\0\0\0\80t\9c·¼Ð´6¹\0\0\0\80\¬±¼HþGº\0\0\0\80\8aJý¼\0h\ eµ\0\0\0\80<Ký¼\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\96 ¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80z\e\1c4Gº\0\0\0\80\v/\ f¼º\ e\0<:8\¬±¼HþGº\0\0\0\80t\9c·¼Ð´6¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80~?v»?Á\r»\0z\0:Hn3ºNÐ\8b»\80\9dï8\v/\ f¼º\ e\0<:8z\e\1c4Gº\0\0\0\80\0\0\0\0\0\96 ¹\0\0\0\80\0\0\0\0,\92\19»\0\0\0\80\0\0\0\0Ù\1c´»\0\0\0\80\0\0\0\0\ 3pߺ\0\0\0\80\¬±¼HþGº\0\0\0\80±ì­¼7zκ\0\0\0\80âGý¼Ë 8¸\0\0\0\80\8aJý¼\0h\ eµ\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0+\f\93¹\0\0\0\80\0\0\0\0,\92\19»\0\0\0\80\0\0\0\0\0\96 ¹\0\0\0\80\v/\ f¼º\ e\0<:8¬ÿ\9c»þ\a¼»\18é/:±ì­¼7zκ\0\0\0\80\¬±¼HþGº\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0+\f\93¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Hn3ºNÐ\8b»\80\9dï8à±Ô¹\9e\8f\ 5¼ _\819¬ÿ\9c»þ\a¼»\18é/:\v/\ f¼º\ e\0<:8\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\80õå¸1Ʀ»\80á\8b8@B´¹'\ 5(¼@J[9à±Ô¹\9e\8f\ 5¼ _\819Hn3ºNÐ\8b»\80\9dï8\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\80\ e¡\9b»\0{\107\80\92\ 6¹~%)¼ ¶£8@B´¹'\ 5(¼@J[9\80õå¸1Ʀ»\80á\8b8\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\9f\92\0\0\0\80\0Ø\98¶XI\v¼\0\12:6\80\92\ 6¹~%)¼ ¶£8\0\80\ e¡\9b»\0{\107\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\ 3pߺ\0\0\0\80\0\0\0\0Ù\1c´»\0\0\0\80\0Ø\98¶XI\v¼\0\12:6\0\0\0\0\9f\92\0\0\0\80@B´¹'\ 5(¼@J[9\80Â\84¸_ðv¼\80\85!8(\9e<º÷)G¼àtå9à±Ô¹\9e\8f\ 5¼ _\819\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\80\92\ 6¹~%)¼ ¶£8\0\0\0\0¨Ë\83¼\0\0\0\80\80Â\84¸_ðv¼\80\85!8@B´¹'\ 5(¼@J[9\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0Ø\98¶XI\v¼\0\12:6\0@-¶ÀÝg¼\0@Ó5\0\0\0\0¨Ë\83¼\0\0\0\80\80\92\ 6¹~%)¼ ¶£8\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0Ù\1c´»\0\0\0\80\0\0\0\0\r)$¼\0\0\0\80\0@-¶ÀÝg¼\0@Ó5\0Ø\98¶XI\v¼\0\12:6\0\0\0\0,\92\19»\0\0\0\80\0\0\0\0Ø\11¬»\0\0\0\80\0\0\0\0\r)$¼\0\0\0\80\0\0\0\0Ù\1c´»\0\0\0\80±ì­¼7zκ\0\0\0\80~´¯¼\1e$\1c»\0\0\0\80ÆÒý¼ø\7fÿ¸\0\0\0\80âGý¼Ë 8¸\0\0\0\80\0\0\0\0+\f\93¹\0\0\0\80\0\0\0\0¯@¿º\0\0\0\80\0\0\0\0Ø\11¬»\0\0\0\80\0\0\0\0,\92\19»\0\0\0\80¬ÿ\9c»þ\a¼»\18é/:©\10±»õì   ¼0\9c\83:~´¯¼\1e$\1c»\0\0\0\80±ì­¼7zκ\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0¯@¿º\0\0\0\80\0\0\0\0+\f\93¹\0\0\0\80à±Ô¹\9e\8f\ 5¼ _\819(\9e<º÷)G¼àtå9©\10±»õì     ¼0\9c\83:¬ÿ\9c»þ\a¼»\18é/:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0Ø\11¬»\0\0\0\80\0\0\0\0j\19ý»\0\0\0\80\ 2¹\11»b¼F9\9f8\0\0\0\0\r)$¼\0\0\0\80~´¯¼\1e$\1c»\0\0\0\802À¹¼x¦B»\0\0\0\80kúþ¼TÂF¹\0\0\0\80ÆÒý¼ø\7fÿ¸\0\0\0\80\0\0\0\0¯@¿º\0\0\0\80\0\0\0\0E¯/»\0\0\0\80\0\0\0\0j\19ý»\0\0\0\80\0\0\0\0Ø\11¬»\0\0\0\80©\10±»õì ¼0\9c\83:\ 18­»B\89(¼¼9\9d:2À¹¼x¦B»\0\0\0\80~´¯¼\1e$\1c»\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0ÀÝò¸\0\0\0\80\0\0\0\0E¯/»\0\0\0\80\0\0\0\0¯@¿º\0\0\0\80(\9e<º÷)G¼àtå9@\13kº¢9w¼\88ü\ e:\ 18­»B\89(¼¼9\9d\10±»õì     ¼0\9c\83:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0ÀÝò¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\80Â\84¸_ðv¼\80\85!8\0\0\0\0\9b¼\0\0\0\80@\13kº¢9w¼\88ü\ e:(\9e<º÷)G¼àtå9\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0¨Ë\83¼\0\0\0\80\0\0\0\0d\f«¼\0\0\0\80\0\0\0\0\9b¼\0\0\0\80\80Â\84¸_ðv¼\80\85!8\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0@-¶ÀÝg¼\0@Ó5\0\0\0\0\8b\86\99¼\0\0\0\80\0\0\0\0d\f«¼\0\0\0\80\0\0\0\0¨Ë\83¼\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\r)$¼\0\0\0\80\ 2¹\11»b¼F9\9f8\0\0\0\0\8b\86\99¼\0\0\0\80\0@-¶ÀÝg¼\0@Ó5\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0d\f«¼\0\0\0\80\0\0\0\0ø?»¼\0\0\0\80\0Ø ¶F\81¨¼\0@Ä5\0\0\0\0\9b¼\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\8b\86\99¼\0\0\0\80\0,ø·\80¿¨¼Àü\967\0\0\0\0ø?»¼\0\0\0\80\0\0\0\0d\f«¼\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\ 2¹\11»b¼F9\9f\9b\8e\1f\83¼Jþþ9\0,ø·\80¿¨¼Àü\967\0\0\0\0\8b\86\99¼\0\0\0\80\0\0\0\0j\19ý»\0\0\0\80\0\0\0\0Äa\ e¼\0\0\0\80è\9b\8e\1f\83¼Jþþ9@á\ 2¹\11»b¼F9\9f82À¹¼x¦B»\0\0\0\80\1d\8a̼ö´P»\0\0\0\80A\16\0½\Vb¹\0\0\0\80kúþ¼TÂF¹\0\0\0\80\0\0\0\0E¯/»\0\0\0\80\0\0\0\0ÿ´P»\0\0\0\80\0\0\0\0Äa\ e¼\0\0\0\80\0\0\0\0j\19ý»\0\0\0\80\ 18­»B\89(¼¼9\9d\12o»úE0¼\\1c\97:\1d\8a̼ö´P»\0\0\0\802À¹¼x¦B»\0\0\0\80\0\0\0\0ÀÝò¸\0\0\0\80\0\0\0\0\0Vb¹\0\0\0\80\0\0\0\0ÿ´P»\0\0\0\80\0\0\0\0E¯/»\0\0\0\80@\13kº¢9w¼\88ü\ e:ÀÒ˺\9cà\89¼ ôw:À\12o»úE0¼\\1c\97:\ 18­»B\89(¼¼9\9d:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0Vb¹\0\0\0\80\0\0\0\0ÀÝò¸\0\0\0\80\0\0\0\0\9b¼\0\0\0\80\0Ø ¶F\81¨¼\0@Ä5ÀÒ˺\9cà\89¼ ôw:@\13kº¢9w¼\88ü\ e:\1d\8a̼ö´P»\0\0\0\80nÛâ¼q¦B»\0\0\0\80;P\0½@ÂF¹\0\0\0\80A\16\0½\Vb¹\0\0\0\80\0\0\0\0ÿ´P»\0\0\0\80\0\0\0\0å4.»\0\0\0\80\0\0\0\0`\19ý»\0\0\0\80\0\0\0\0Äa\ e¼\0\0\0\80À\12o»úE0¼\\1c\97:\13oF¼òE\ 5¼\0\0\0\80nÛâ¼q¦B»\0\0\0\80\1d\8a̼ö´P»\0\0\0\80\0\0\0\0\0Vb¹\0\0\0\80\0\0\0\0 n\119\0\0\0\80\0\0\0\0å4.»\0\0\0\80\0\0\0\0ÿ´P»\0\0\0\80ÀÒ˺\9cà\89¼ ôw:\9a\88/»²\8a\8b¼ì\89Õ:\13oF¼òE\ 5¼\0\0\0\80À\12o»úE0¼\\1c\97:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0 n\119\0\0\0\80\0\0\0\0\0Vb¹\0\0\0\80\0Ø ¶F\81¨¼\0@Ä5\80\ 5þº\14
+­¼°\82\9a:\9a\88/»²\8a\8b¼ì\89Õ:ÀÒ˺\9cà\89¼ ôw:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0ø?»¼\0\0\0\80Ì` ºéüµ¼$\1aC:\80\ 5þº\14
+­¼°\82\9a:\0Ø ¶F\81¨¼\0@Ä5\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0,ø·\80¿¨¼Àü\96\93\9c\11±¼0ñÑ:Ì` ºéüµ¼$\1aC:\0\0\0\0ø?»¼\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80è\9b\8e\1f\83¼Jþþ9 ×¾º\97\88\v)h:ª\93\9c\11±¼0ñÑ:\0,ø·\80¿¨¼Àü\967\0\0\0\0Äa\ e¼\0\0\0\80\0\0\0\0`\19ý»\0\0\0\80 ×¾º\97\88\v)h:è\9b\8e\1f\83¼Jþþ9Ì` ºéüµ¼$\1aC:\]¸»\e\19¶¼\1eH`;\ e\94\99»dΤ¼xÔ:;\80\ 5þº\14
+­¼°\82\9a:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80ª\93\9c\11±¼0ñÑ:\ 3an»\12n\94¼Ûþ\10;\]¸»\e\19¶¼\1eH`;Ì` ºéüµ¼$\1aC:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80 ×¾º\97\88\v)h:\0\0\0\0\f)$¼\0\0\0\80\ 3an»\12n\94¼Ûþ\10\93\9c\11±¼0ñÑ:\0\0\0\0`\19ý»\0\0\0\80\0\0\0\08"¤»\0\0\0\80\0\0\0\0\f)$¼\0\0\0\80 ×¾º\97\88\v)h:nÛâ¼q¦B»\0\0\0\80bÈð¼\ f$\1c»\0\0\0\80zäÿ¼P\7fÿ¸\0\0\0\80;P\0½@ÂF¹\0\0\0\80\0\0\0\0å4.»\0\0\0\80\0\0\0\0`3û9\0\0\0\80\0\0\0\08"¤»\0\0\0\80\0\0\0\0`\19ý»\0\0\0\80\13oF¼òE\ 5¼\0\0\0\80\ 3\9e´¼ÁÐØ»\0\0\0\80bÈð¼\ f$\1c»\0\0\0\80nÛâ¼q¦B»\0\0\0\80\0\0\0\0 n\119\0\0\0\80\0\0\0\0p$·:\0\0\0\80\0\0\0\0`3û9\0\0\0\80\0\0\0\0å4.»\0\0\0\80\9a\88/»²\8a\8b¼ì\89Õ:î\90=»~tp¼\90wñ:\ 3\9e´¼ÁÐØ»\0\0\0\80\13oF¼òE\ 5¼\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0`\8b\959\0\0\0\80\0\0\0\0p$·:\0\0\0\80\0\0\0\0 n\119\0\0\0\80\80\ 5þº\14
+­¼°\82\9a:\ e\94\99»dΤ¼xÔ:;î\90=»~tp¼\90wñ:\9a\88/»²\8a\8b¼ì\89Õ:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0`\8b\959\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0`3û9\0\0\0\80\0\0\0\0Ò\8f\9b;\0\0\0\80\0\0\0\0èy@;\0\0\0\80\0\0\0\08"¤»\0\0\0\80\ 3\9e´¼ÁÐØ»\0\0\0\80ådä¼Ï\93\94»\0\0\0\807\86ë¼\1azκ\0\0\0\80bÈð¼\ f$\1c»\0\0\0\80\0\0\0\0p$·:\0\0\0\80\0\0\0\0z\1e:;\0\0\0\80\0\0\0\0Ò\8f\9b;\0\0\0\80\0\0\0\0`3û9\0\0\0\80î\90=»~tp¼\90wñ:\fþ¯¼!\9dü»\0\0\0\80ådä¼Ï\93\94»\0\0\0\80\ 3\9e´¼ÁÐØ»\0\0\0\80\0\0\0\0`\8b\959\0\0\0\80\0\0\0\0è­g:\0\0\0\80\0\0\0\0z\1e:;\0\0\0\80\0\0\0\0p$·:\0\0\0\80\ e\94\99»dΤ¼xÔ:;8\aO¼Fß!¼\0\0\0\80\fþ¯¼!\9dü»\0\0\0\80î\90=»~tp¼\90wñ:\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0è­g:\0\0\0\80\0\0\0\0`\8b\959\0\0\0\80\]¸»\e\19¶¼\1eH`;0­¶»\ 101¼\90V¸98\aO¼Fß!¼\0\0\0\80\ e\94\99»dΤ¼xÔ:;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\ 3an»\12n\94¼Ûþ\10;Ô½\97ºÐ2\v¼\0\0\0\800­¶»\ 101¼\90V¸9\]¸»\e\19¶¼\1eH`;\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\f)$¼\0\0\0\80\0\0\0\0zþ_»\0\0\0\80Ô½\97ºÐ2\v¼\0\0\0\80\ 3an»\12n\94¼Ûþ\10;\0\0\0\08"¤»\0\0\0\80\0\0\0\0èy@;\0\0\0\80\0\0\0\0zþ_»\0\0\0\80\0\0\0\0\f)$¼\0\0\0\80bÈð¼\ f$\1c»\0\0\0\807\86ë¼\1azκ\0\0\0\80\99[þ¼à\1e\0\0\0\80zäÿ¼P\7fÿ¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\8a\9b8\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Ô½\97ºÐ2\v¼\0\0\0\80\0\0\0\0d E;\0\0\0\80\936\91»æ­-»\0\0\0\800­¶»\ 101¼\90V¸9\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0zþ_»\0\0\0\80\0\0\0\0\10^i<\0\0\0\80\0\0\0\0d E;\0\0\0\80Ô½\97ºÐ2\v¼\0\0\0\80\0\0\0\0èy@;\0\0\0\80\0\0\0\0\84<\0\0\0\80\0\0\0\0\10^i<\0\0\0\80\0\0\0\0zþ_»\0\0\0\807\86ë¼\1azκ\0\0\0\80ÌfÖ¼\ 4þGº\0\0\0\80½Ný¼\0Ð\rµ\0\0\0\80\99[þ¼à\1e\0\0\0\80\0\0\0\0Ò\8f\9b;\0\0\0\80\0\0\0\0ð\11\19<\0\0\0\80\0\0\0\0\84<\0\0\0\80\0\0\0\0èy@;\0\0\0\80ådä¼Ï\93\94»\0\0\0\80_µÄ¼
\1d»\0\0\0\80ÌfÖ¼\ 4þGº\0\0\0\807\86ë¼\1azκ\0\0\0\80\0\0\0\0z\1e:;\0\0\0\80\0\0\0\0\83;\0\0\0\80\0\0\0\0ð\11\19<\0\0\0\80\0\0\0\0Ò\8f\9b;\0\0\0\80\fþ¯¼!\9dü»\0\0\0\80!í¨¼\8a\18\85»\0\0\0\80_µÄ¼
\1d»\0\0\0\80ådä¼Ï\93\94»\0\0\0\80\0\0\0\0è­g:\0\0\0\80\0\0\0\0(\80»:\0\0\0\80\0\0\0\0\83;\0\0\0\80\0\0\0\0z\1e:;\0\0\0\808\aO¼Fß!¼\0\0\0\80¬\80^¼-Ú¢»\0\0\0\80!í¨¼\8a\18\85»\0\0\0\80\fþ¯¼!\9dü»\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\8a\9b8\0\0\0\80\0\0\0\0(\80»:\0\0\0\80\0\0\0\0è­g:\0\0\0\800­¶»\ 101¼\90V¸9\936\91»æ­-»\0\0\0\80¬\80^¼-Ú¢»\0\0\0\808\aO¼Fß!¼\0\0\0\80\0\0\0\0\83;\0\0\0\80\0\0\0\0Ðÿ\92;\0\0\0\80\0\0\0\0\19d<<\0\0\0\80\0\0\0\0ð\11\19<\0\0\0\80!í¨¼\8a\18\85»\0\0\0\80\0\0\0\0\9d¹\0\0\0\80\0\0\0\0(¤Cº\0\0\0\80_µÄ¼
\1d»\0\0\0\80\0\0\0\0(\80»:\0\0\0\80\0\0\0\0,õ×:\0\0\0\80\0\0\0\0Ðÿ\92;\0\0\0\80\0\0\0\0\83;\0\0\0\80¬\80^¼-Ú¢»\0\0\0\80\0\0\0\0\f³\r;\0\0\0\80\0\0\0\0\9d¹\0\0\0\80!í¨¼\8a\18\85»\0\0\0\80\0\0\0\0\0\8a\9b8\0\0\0\80\0\0\0\0\80õí8\0\0\0\80\0\0\0\0,õ×:\0\0\0\80\0\0\0\0(\80»:\0\0\0\80\936\91»æ­-»\0\0\0\80\0\0\0\0\ 3<\0\0\0\80\0\0\0\0\f³\r;\0\0\0\80¬\80^¼-Ú¢»\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\80õí8\0\0\0\80\0\0\0\0\0\8a\9b8\0\0\0\80\0\0\0\0d E;\0\0\0\80\0\0\0\0Rd\9b<\0\0\0\80\0\0\0\0\ 3<\0\0\0\80\936\91»æ­-»\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\10^i<\0\0\0\80\0\0\0\0nW¹<\0\0\0\80\0\0\0\0Rd\9b<\0\0\0\80\0\0\0\0d E;\0\0\0\80\0\0\0\0\84<\0\0\0\80\0\0\0\0Ø\15£<\0\0\0\80\0\0\0\0nW¹<\0\0\0\80\0\0\0\0\10^i<\0\0\0\80ÌfÖ¼\ 4þGº\0\0\0\80\0\0\0\0`´6¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80½Ný¼\0Ð\rµ\0\0\0\80\0\0\0\0ð\11\19<\0\0\0\80\0\0\0\0\19d<<\0\0\0\80\0\0\0\0Ø\15£<\0\0\0\80\0\0\0\0\84<\0\0\0\80_µÄ¼
\1d»\0\0\0\80\0\0\0\0(¤Cº\0\0\0\80\0\0\0\0`´6¹\0\0\0\80ÌfÖ¼\ 4þGº\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0 \9a\98¹\0\0\0\80Ôä\ 6» ób¹\0\0\0\80Øô\13»XéÕ¹\0\0\0\80\0\0\0\0\0à\ 4º\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0À°ß¸\0\0\0\80'¸õº\80\1c\89¸\0\0\0\80Ôä\ 6» ób¹\0\0\0\80\0\0\0\0 \9a\98¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\805íµº\0\80ªµ\0\0\0\80'¸õº\80\1c\89¸\0\0\0\80\0\0\0\0À°ß¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\10
+Eº\0\0\0\0\0\0\0\805íµº\0\80ªµ\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80(\ 6\0\0\0\0\0\0\0\80\10
+Eº\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80(\ 6\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0Z\94\0\0\0\80¦ïA»F\86\rº\0\0\0\80\0\0\0\0~±Hº\0\0\0\80yRj»4®ø¹\0\0\0\80à\9e\8a»dJÕ¹\0\0\0\80©ïA;$\86\rº\0\0\0\80~Rj;ø­ø¹\0\0\0\80\88®\9f»Pá³¹\0\0\0\80\0\0\0\0~±Hº\0\0\0\80}²»Fý\96¹\0\0\0\80\9c\9e»Ò1\80¹\0\0\0\80n\84λT\13\0\0\0\80\0\0\0\0~±Hº\0\0\0\80Ï-Ö»)®L¹\0\0\0\80LóÙ»ô«D¹\0\0\0\80w'Ú»\f%G¹\0\0\0\80\0\0\0\0~±Hº\0\0\0\80\1a\80Ö»8+U¹\0\0\0\80âÇλð+p¹\0\0\0\80¤\ 6ûL®\8c¹\0\0\0\80\0\0\0\0~±Hº\0\0\0\80,ʳ»|E¨¹\0\0\0\80Om¢»\10]ɹ\0\0\0\80\0\0\0\0d\94\0\0\0\80\0\0\0\0~±Hº\0\0\0\80Om¢;\1c]ɹ\0\0\0\80-ʳ;\8cE¨¹\0\0\0\80¤\ 6Ã;`®\8c¹\0\0\0\80\0\0\0\0~±Hº\0\0\0\80æÇÎ; ,p¹\0\0\0\80\1c\80Ö;h+U¹\0\0\0\80z'Ú;4%G¹\0\0\0\80\0\0\0\0~±Hº\0\0\0\80OóÙ;\10¬D¹\0\0\0\80Ò-Ö;3®L¹\0\0\0\80q\84Î;H\13\0\0\0\80\0\0\0\0~±Hº\0\0\0\80 \9eÂ;¾1\80¹\0\0\0\80\81²;"ý\96¹\0\0\0\80\8c®\9f;$á³¹\0\0\0\80\0\0\0\0~±Hº\0\0\0\80ã\9e\8a;,JÕ¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80À\93\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80H\96\0\0\0\0\0\0\0\80|\96¡¹\0\0\0\0\0\0\0\80\ 2º\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80¦¤7º\0\0\0\0\0\0\0\80\82üjº\0\0\0\0\0\0\0\80\8aº\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\7fä\98º\0\0\0\0\0\0\0\80íÑ\9dº\0\0\0\0\0\0\0\80\7fä\98º\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\8aº\0\0\0\0\0\0\0\80\81üjº\0\0\0\0\0\0\0\80¬¤7º\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80®¤7:\0\0\0\0\0\0\0\80\86üj:\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\8a:\0\0\0\0\0\0\0\80\84ä\98:\0\0\0\0\0\0\0\80ôÑ\9d:\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\88ä\98:\0\0\0\0\0\0\0\80\8a:\0\0\0\0\0\0\0\80\98üj:\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80º¤7:\0\0\0\0\0\0\0\80\ 2:\0\0\0\0\0\0\0\80¤\96¡9\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\90\96#9\0\0\0\0\0\0\0\80À\94^8\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0à\ 4º\0\0\0\80Øô\13»XéÕ¹\0\0\0\80¦ïA»F\86\rº\0\0\0\80\0\0\0\0Z\94\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80 IѸ\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Øô\13»XéÕ¹\0\0\0\80+\V»pú¦¹\0\0\0\80yRj»4®ø¹\0\0\0\80¦ïA»F\86\rº\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Ôä\ 6» ób¹\0\0\0\80¹YK»àÛ\13¹\0\0\0\80+\V»pú¦¹\0\0\0\80Øô\13»XéÕ¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80'¸õº\80\1c\89¸\0\0\0\80I8?»\0#µ·\0\0\0\80¹YK»àÛ\13¹\0\0\0\80Ôä\ 6» ób¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\805íµº\0\80ªµ\0\0\0\80j[\14»\0\0\0\0\0\0\0\80I8?»\0#µ·\0\0\0\80'¸õº\80\1c\89¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\10
+Eº\0\0\0\0\0\0\0\80TÙ¿º\0\0\0\0\0\0\0\80j[\14»\0\0\0\0\0\0\0\805íµº\0\80ªµ\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80(\ 6\0\0\0\0\0\0\0\80\fÂ)º\0\0\0\0\0\0\0\80TÙ¿º\0\0\0\0\0\0\0\80\10
+Eº\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80 IѸ\0\0\0\0\0\0\0\80\fÂ)º\0\0\0\0\0\0\0\80(\ 6\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80TÙ¿º\0\0\0\0\0\0\0\80ħ\10»\0\0\0\0\0\0\0\80h\1f\0\0\0\0\0\0\0\80j[\14»\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\fÂ)º\0\0\0\0\0\0\0\80\96º\0\0\0\0\0\0\0\80ħ\10»\0\0\0\0\0\0\0\80TÙ¿º\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80À\93\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80 IѸ\0\0\0\0\0\0\0\80À\15¬¹\0\0\0\0\0\0\0\80\96º\0\0\0\0\0\0\0\80\fÂ)º\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80À\15¬¹\0\0\0\0\0\0\0\80 IѸ\0\0\0\0\0\0\0\80+\V»pú¦¹\0\0\0\80Ð\86\91»Àqh¹\0\0\0\80à\9e\8a»dJÕ¹\0\0\0\80yRj»4®ø¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80¹YK»àÛ\13¹\0\0\0\80\90»À\ 6\8b¸\0\0\0\80Ð\86\91»Àqh¹\0\0\0\80+\V»pú¦¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80I8?»\0#µ·\0\0\0\80S\9b\8b»\0\0\0\0\0\80\90»À\ 6\8b¸\0\0\0\80¹YK»àÛ\13¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80j[\14»\0\0\0\0\0\0\0\80h\1f\0\0\0\0\0\0\0\80S\9b\8b»\0\0\0\0\0\80I8?»\0#µ·\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\93Õ·\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\90»À\ 6\8b¸\0\0\0\80ôa½»\0©\83·\0\0\0\80ª\8f·»\80ø\ e¹\0\0\0\80Ð\86\91»Àqh¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80S\9b\8b»\0\0\0\0\0\80\90X´»\0\0\0\0\0\0\0\80ôa½»\0©\83·\0\0\0\80\90»À\ 6\8b¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80h\1f\0\0\0\0\0\0\0\80è|\95»\0\0\0\0\0\0\0\80\90X´»\0\0\0\0\0\0\0\80S\9b\8b»\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80À\ 5\96¸\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80ħ\10»\0\0\0\0\0\0\0\80ü(G»\0\0\0\0\0\0\0\80è|\95»\0\0\0\0\0\0\0\80h\1f\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\90}\1e¹\0\0\0\0\0\0\0\80À\ 5\96¸\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\96º\0\0\0\0\0\0\0\80X~̺\0\0\0\0\0\0\0\80ü(G»\0\0\0\0\0\0\0\80ħ\10»\0\0\0\0\0\0\0\80À\93\0\0\0\0\0\0\0\80H\96\0\0\0\0\0\0\0\80\90}\1e¹\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80À\15¬¹\0\0\0\0\0\0\0\80èu\ eº\0\0\0\0\0\0\0\80X~̺\0\0\0\0\0\0\0\80\96º\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\93Õ·\0\0\0\0\0\0\0\80èu\ eº\0\0\0\0\0\0\0\80À\15¬¹\0\0\0\0\0\0\0\80Ð\86\91»Àqh¹\0\0\0\80ª\8f·»\80ø\ e¹\0\0\0\80\88®\9f»Pá³¹\0\0\0\80à\9e\8a»dJÕ¹\0\0\0\80\90}\1e¹\0\0\0\0\0\0\0\80Ì/\ 2º\0\0\0\0\0\0\0\80¸\8f   º\0\0\0\0\0\0\0\80À\ 5\96¸\0\0\0\0\0\0\0\80X~̺\0\0\0\0\0\0\0\80xäúº\0\0\0\0\0\0\0\80\92ëq»\0\0\0\0\0\0\0\80ü(G»\0\0\0\0\0\0\0\80H\96\0\0\0\0\0\0\0\80|\96¡¹\0\0\0\0\0\0\0\80Ì/\ 2º\0\0\0\0\0\0\0\80\90}\1e¹\0\0\0\0\0\0\0\80èu\ eº\0\0\0\0\0\0\0\80\90~&º\0\0\0\0\0\0\0\80xäúº\0\0\0\0\0\0\0\80X~̺\0\0\0\0\0\0\0\80\0\93Õ·\0\0\0\0\0\0\0\80\0÷@¸\0\0\0\0\0\0\0\80\90~&º\0\0\0\0\0\0\0\80èu\ eº\0\0\0\0\0\0\0\80ª\8f·»\80ø\ e¹\0\0\0\80\8a\1aØ» B\9e¸\0\0\0\80}²»Fý\96¹\0\0\0\80\88®\9f»Pá³¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0÷@¸\0\0\0\0\0\0\0\80\0\93Õ·\0\0\0\0\0\0\0\80ôa½»\0©\83·\0\0\0\80ØÚá»\0\0\0\0\0\80\8a\1aØ» B\9e¸\0\0\0\80ª\8f·»\80ø\ e¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80À\87\15¹\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\90X´»\0\0\0\0\0\0\0\80p{Ó»\0\0\0\0\0\0\0\80ØÚá»\0\0\0\0\0\80ôa½»\0©\83·\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80àëĹ\0\0\0\0\0\0\0\80À\87\15¹\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80è|\95»\0\0\0\0\0\0\0\80ïj¯»\0\0\0\0\0\0\0\80p{Ó»\0\0\0\0\0\0\0\80\90X´»\0\0\0\0\0\0\0\80À\ 5\96¸\0\0\0\0\0\0\0\80¸\8f     º\0\0\0\0\0\0\0\80àëĹ\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80ü(G»\0\0\0\0\0\0\0\80\92ëq»\0\0\0\0\0\0\0\80ïj¯»\0\0\0\0\0\0\0\80è|\95»\0\0\0\0\0\0\0\80ØÚá»\0\0\0\0\0\80Þ\91ø»\0\0\0\0\0\0\0\80\9eëï» .)¸\0\0\0\80\8a\1aØ» B\9e¸\0\0\0\80À\87\15¹\0\0\0\0\0\0\0\80\87º\0\0\0\0\0\0\0\80\10í
\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80p{Ó»\0\0\0\0\0\0\0\80Û_ä»\0\0\0\0\0\0\0\80Þ\91ø»\0\0\0\0\0\0\0\80ØÚá»\0\0\0\0\0\80àëĹ\0\0\0\0\0\0\0\80\ 4@²º\0\0\0\0\0\0\0\80\87º\0\0\0\0\0\0\0\80À\87\15¹\0\0\0\0\0\0\0\80ïj¯»\0\0\0\0\0\0\0\80\18ôº»\0\0\0\0\0\0\0\80Û_ä»\0\0\0\0\0\0\0\80p{Ó»\0\0\0\0\0\0\0\80¸\8f        º\0\0\0\0\0\0\0\80<´°º\0\0\0\0\0\0\0\80\ 4@²º\0\0\0\0\0\0\0\80àëĹ\0\0\0\0\0\0\0\80\92ëq»\0\0\0\0\0\0\0\80\1eu\84»\0\0\0\0\0\0\0\80\18ôº»\0\0\0\0\0\0\0\80ïj¯»\0\0\0\0\0\0\0\80Ì/\ 2º\0\0\0\0\0\0\0\80 è\83º\0\0\0\0\0\0\0\80<´°º\0\0\0\0\0\0\0\80¸\8f     º\0\0\0\0\0\0\0\80xäúº\0\0\0\0\0\0\0\80ØA\ f»\0\0\0\0\0\0\0\80\1eu\84»\0\0\0\0\0\0\0\80\92ëq»\0\0\0\0\0\0\0\80|\96¡¹\0\0\0\0\0\0\0\80\ 2º\0\0\0\0\0\0\0\80 è\83º\0\0\0\0\0\0\0\80Ì/\ 2º\0\0\0\0\0\0\0\80\90~&º\0\0\0\0\0\0\0\80ÐÆ,º\0\0\0\0\0\0\0\80ØA\ f»\0\0\0\0\0\0\0\80xäúº\0\0\0\0\0\0\0\80\0÷@¸\0\0\0\0\0\0\0\80\0º\ 4¹\0\0\0\0\0\0\0\80ÐÆ,º\0\0\0\0\0\0\0\80\90~&º\0\0\0\0\0\0\0\80\8a\1aØ» B\9e¸\0\0\0\80\9eëï» .)¸\0\0\0\80\9c\9e»Ò1\80¹\0\0\0\80}²»Fý\96¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\10í
\0\0\0\0\0\0\0\80\0º\ 4¹\0\0\0\0\0\0\0\80\0÷@¸\0\0\0\0\0\0\0\80ØA\ f»\0\0\0\0\0\0\0\80Hò>»\0\0\0\0\0\0\0\80Ì\ 3\96»\0\0\0\0\0\0\0\80\1eu\84»\0\0\0\0\0\0\0\80\ 2º\0\0\0\0\0\0\0\80¦¤7º\0\0\0\0\0\0\0\808êÓº\0\0\0\0\0\0\0\80 è\83º\0\0\0\0\0\0\0\80ÐÆ,º\0\0\0\0\0\0\0\80 Vº\0\0\0\0\0\0\0\80Hò>»\0\0\0\0\0\0\0\80ØA\ f»\0\0\0\0\0\0\0\80\0º\ 4¹\0\0\0\0\0\0\0\80È˲º\0\0\0\0\0\0\0\80 Vº\0\0\0\0\0\0\0\80ÐÆ,º\0\0\0\0\0\0\0\80\9eëï» .)¸\0\0\0\80\88\8cÿ»àM½·\0\0\0\80n\84λT\13\0\0\0\80\9c\9e»Ò1\80¹\0\0\0\80\10í
\0\0\0\0\0\0\0\80x\88ÿº\0\0\0\0\0\0\0\80È˲º\0\0\0\0\0\0\0\80\0º\ 4¹\0\0\0\0\0\0\0\80Þ\91ø»\0\0\0\0\0\0\0\80_1\ 3¼\0\0\0\0\0\0\0\80\88\8cÿ»àM½·\0\0\0\80\9eëï» .)¸\0\0\0\80\87º\0\0\0\0\0\0\0\80ü2*»\0\0\0\0\0\0\0\80x\88ÿº\0\0\0\0\0\0\0\80\10í
\0\0\0\0\0\0\0\80Û_ä»\0\0\0\0\0\0\0\80\81
+ð»\0\0\0\0\0\0\0\80_1\ 3¼\0\0\0\0\0\0\0\80Þ\91ø»\0\0\0\0\0\0\0\80\ 4@²º\0\0\0\0\0\0\0\80Þ58»\0\0\0\0\0\0\0\80ü2*»\0\0\0\0\0\0\0\80\87º\0\0\0\0\0\0\0\80\18ôº»\0\0\0\0\0\0\0\80Z\16È»\0\0\0\0\0\0\0\80\81
+ð»\0\0\0\0\0\0\0\80Û_ä»\0\0\0\0\0\0\0\80<´°º\0\0\0\0\0\0\0\80.`\1f»\0\0\0\0\0\0\0\80Þ58»\0\0\0\0\0\0\0\80\ 4@²º\0\0\0\0\0\0\0\80\1eu\84»\0\0\0\0\0\0\0\80Ì\ 3\96»\0\0\0\0\0\0\0\80Z\16È»\0\0\0\0\0\0\0\80\18ôº»\0\0\0\0\0\0\0\80 è\83º\0\0\0\0\0\0\0\808êÓº\0\0\0\0\0\0\0\80.`\1f»\0\0\0\0\0\0\0\80<´°º\0\0\0\0\0\0\0\80ü2*»\0\0\0\0\0\0\0\80\16-\95»\0\0\0\0\0\0\0\80ÞÛ\84»\0\0\0\0\0\0\0\80x\88ÿº\0\0\0\0\0\0\0\80\81
+ð»\0\0\0\0\0\0\0\80°Çú»\0\0\0\0\0\0\0\80êý\a¼\0\0\0\0\0\0\0\80_1\ 3¼\0\0\0\0\0\0\0\80Þ58»\0\0\0\0\0\0\0\80\1a]\93»\0\0\0\0\0\0\0\80\16-\95»\0\0\0\0\0\0\0\80ü2*»\0\0\0\0\0\0\0\80Z\16È»\0\0\0\0\0\0\0\80\ 6<Ø»\0\0\0\0\0\0\0\80°Çú»\0\0\0\0\0\0\0\80\81
+ð»\0\0\0\0\0\0\0\80.`\1f»\0\0\0\0\0\0\0\80¼¶l»\0\0\0\0\0\0\0\80\1a]\93»\0\0\0\0\0\0\0\80Þ58»\0\0\0\0\0\0\0\80Ì\ 3\96»\0\0\0\0\0\0\0\804+®»\0\0\0\0\0\0\0\80\ 6<Ø»\0\0\0\0\0\0\0\80Z\16È»\0\0\0\0\0\0\0\808êÓº\0\0\0\0\0\0\0\80\12»\0\0\0\0\0\0\0\80¼¶l»\0\0\0\0\0\0\0\80.`\1f»\0\0\0\0\0\0\0\80Hò>»\0\0\0\0\0\0\0\80bx\82»\0\0\0\0\0\0\0\804+®»\0\0\0\0\0\0\0\80Ì\ 3\96»\0\0\0\0\0\0\0\80¦¤7º\0\0\0\0\0\0\0\80\82üjº\0\0\0\0\0\0\0\80\12»\0\0\0\0\0\0\0\808êÓº\0\0\0\0\0\0\0\80 Vº\0\0\0\0\0\0\0\808N[»\0\0\0\0\0\0\0\80bx\82»\0\0\0\0\0\0\0\80Hò>»\0\0\0\0\0\0\0\80È˲º\0\0\0\0\0\0\0\80¼\8a\0\0\0\0\0\0\0\808N[»\0\0\0\0\0\0\0\80 Vº\0\0\0\0\0\0\0\80\88\8cÿ»àM½·\0\0\0\80ì\10\ 5¼¬©Z·\0\0\0\80Ï-Ö»)®L¹\0\0\0\80n\84λT\13\0\0\0\80x\88ÿº\0\0\0\0\0\0\0\80ÞÛ\84»\0\0\0\0\0\0\0\80¼\8a\0\0\0\0\0\0\0\80È˲º\0\0\0\0\0\0\0\80_1\ 3¼\0\0\0\0\0\0\0\80êý\a¼\0\0\0\0\0\0\0\80ì\10\ 5¼¬©Z·\0\0\0\80\88\8cÿ»àM½·\0\0\0\80\82üjº\0\0\0\0\0\0\0\80\8aº\0\0\0\0\0\0\0\80Ù|6»\0\0\0\0\0\0\0\80\12»\0\0\0\0\0\0\0\808N[»\0\0\0\0\0\0\0\80²ù°»\0\0\0\0\0\0\0\80ªÞ®»\0\0\0\0\0\0\0\80bx\82»\0\0\0\0\0\0\0\80¼\8a\0\0\0\0\0\0\0\80¸n·»\0\0\0\0\0\0\0\80²ù°»\0\0\0\0\0\0\0\808N[»\0\0\0\0\0\0\0\80ì\10\ 5¼¬©Z·\0\0\0\80ã>\b¼@}
\0\0\0\80LóÙ»ô«D¹\0\0\0\80Ï-Ö»)®L¹\0\0\0\80ÞÛ\84»\0\0\0\0\0\0\0\80HçÅ»\0\0\0\0\0\0\0\80¸n·»\0\0\0\0\0\0\0\80¼\8a\0\0\0\0\0\0\0\80êý\a¼\0\0\0\0\0\0\0\80ïS\v¼\0\0\0\0\0\0\0\80ã>\b¼@}
\0\0\0\80ì\10\ 5¼¬©Z·\0\0\0\80\16-\95»\0\0\0\0\0\0\0\80úlÕ»\0\0\0\0\0\0\0\80HçÅ»\0\0\0\0\0\0\0\80ÞÛ\84»\0\0\0\0\0\0\0\80°Çú»\0\0\0\0\0\0\0\80Ø*\ 2¼\0\0\0\0\0\0\0\80ïS\v¼\0\0\0\0\0\0\0\80êý\a¼\0\0\0\0\0\0\0\80\1a]\93»\0\0\0\0\0\0\0\80rjÈ»\0\0\0\0\0\0\0\80úlÕ»\0\0\0\0\0\0\0\80\16-\95»\0\0\0\0\0\0\0\80\ 6<Ø»\0\0\0\0\0\0\0\80\ 6éç»\0\0\0\0\0\0\0\80Ø*\ 2¼\0\0\0\0\0\0\0\80°Çú»\0\0\0\0\0\0\0\80¼¶l»\0\0\0\0\0\0\0\80Ö\92\99»\0\0\0\0\0\0\0\80rjÈ»\0\0\0\0\0\0\0\80\1a]\93»\0\0\0\0\0\0\0\804+®»\0\0\0\0\0\0\0\80\90\9aÆ»\0\0\0\0\0\0\0\80\ 6éç»\0\0\0\0\0\0\0\80\ 6<Ø»\0\0\0\0\0\0\0\80\12»\0\0\0\0\0\0\0\80Ù|6»\0\0\0\0\0\0\0\80Ö\92\99»\0\0\0\0\0\0\0\80¼¶l»\0\0\0\0\0\0\0\80bx\82»\0\0\0\0\0\0\0\80ªÞ®»\0\0\0\0\0\0\0\80\90\9aÆ»\0\0\0\0\0\0\0\804+®»\0\0\0\0\0\0\0\80Ø*\ 2¼\0\0\0\0\0\0\0\80\ 3\95\ 6¼\0\0\0\0\0\0\0\80ä÷\r¼\0\0\0\0\0\0\0\80ïS\v¼\0\0\0\0\0\0\0\80rjÈ»\0\0\0\0\0\0\0\80:âï»\0\0\0\0\0\0\0\80h\96\ 3¼\0\0\0\0\0\0\0\80úlÕ»\0\0\0\0\0\0\0\80\ 6éç»\0\0\0\0\0\0\0\80úÍõ»\0\0\0\0\0\0\0\80\ 3\95\ 6¼\0\0\0\0\0\0\0\80Ø*\ 2¼\0\0\0\0\0\0\0\80Ö\92\99»\0\0\0\0\0\0\0\80!w²»\0\0\0\0\0\0\0\80:âï»\0\0\0\0\0\0\0\80rjÈ»\0\0\0\0\0\0\0\80\90\9aÆ»\0\0\0\0\0\0\0\80\90\9aÛ»\0\0\0\0\0\0\0\80úÍõ»\0\0\0\0\0\0\0\80\ 6éç»\0\0\0\0\0\0\0\80Ù|6»\0\0\0\0\0\0\0\80\16ºN»\0\0\0\0\0\0\0\80!w²»\0\0\0\0\0\0\0\80Ö\92\99»\0\0\0\0\0\0\0\80ªÞ®»\0\0\0\0\0\0\0\80D\ 3Ú»\0\0\0\0\0\0\0\80\90\9aÛ»\0\0\0\0\0\0\0\80\90\9aÆ»\0\0\0\0\0\0\0\80\8aº\0\0\0\0\0\0\0\80\7fä\98º\0\0\0\0\0\0\0\80\16ºN»\0\0\0\0\0\0\0\80Ù|6»\0\0\0\0\0\0\0\80²ù°»\0\0\0\0\0\0\0\80Hùè»\0\0\0\0\0\0\0\80D\ 3Ú»\0\0\0\0\0\0\0\80ªÞ®»\0\0\0\0\0\0\0\80¸n·»\0\0\0\0\0\0\0\80\84?è»\0\0\0\0\0\0\0\80Hùè»\0\0\0\0\0\0\0\80²ù°»\0\0\0\0\0\0\0\80ã>\b¼@}
\0\0\0\806\9b        ¼\0&ܶ\0\0\0\80w'Ú»\f%G¹\0\0\0\80LóÙ»ô«D¹\0\0\0\80HçÅ»\0\0\0\0\0\0\0\80juö»\0\0\0\0\0\0\0\80\84?è»\0\0\0\0\0\0\0\80¸n·»\0\0\0\0\0\0\0\80ïS\v¼\0\0\0\0\0\0\0\80ä÷\r¼\0\0\0\0\0\0\0\806\9b     ¼\0&ܶ\0\0\0\80ã>\b¼@}
\0\0\0\80úlÕ»\0\0\0\0\0\0\0\80h\96\ 3¼\0\0\0\0\0\0\0\80juö»\0\0\0\0\0\0\0\80HçÅ»\0\0\0\0\0\0\0\80Hùè»\0\0\0\0\0\0\0\80\ 2.\ 4¼\0\0\0\0\0\0\0\80Pûò»\0\0\0\0\0\0\0\80D\ 3Ú»\0\0\0\0\0\0\0\80\84?è»\0\0\0\0\0\0\0\80î\97\ 3¼\0\0\0\0\0\0\0\80\ 2.\ 4¼\0\0\0\0\0\0\0\80Hùè»\0\0\0\0\0\0\0\806\9b        ¼\0&ܶ\0\0\0\80\98ÿ\b¼\0\92\ 6·\0\0\0\80\1a\80Ö»8+U¹\0\0\0\80w'Ú»\f%G¹\0\0\0\80juö»\0\0\0\0\0\0\0\80O\v\b¼\0\0\0\0\0\0\0\80î\97\ 3¼\0\0\0\0\0\0\0\80\84?è»\0\0\0\0\0\0\0\80ä÷\r¼\0\0\0\0\0\0\0\80\91Þ\ f¼\0\0\0\0\0\0\0\80\98ÿ\b¼\0\92\ 6·\0\0\0\806\9b     ¼\0&ܶ\0\0\0\80h\96\ 3¼\0\0\0\0\0\0\0\80ø-\ e¼\0\0\0\0\0\0\0\80O\v\b¼\0\0\0\0\0\0\0\80juö»\0\0\0\0\0\0\0\80\ 3\95\ 6¼\0\0\0\0\0\0\0\80\10{
\0\0\0\0\0\0\0\80\91Þ\ f¼\0\0\0\0\0\0\0\80ä÷\r¼\0\0\0\0\0\0\0\80:âï»\0\0\0\0\0\0\0\80iÑþ»\0\0\0\0\0\0\0\80ø-\ e¼\0\0\0\0\0\0\0\80h\96\ 3¼\0\0\0\0\0\0\0\80úÍõ»\0\0\0\0\0\0\0\80°`\0¼\0\0\0\0\0\0\0\80\10{
\0\0\0\0\0\0\0\80\ 3\95\ 6¼\0\0\0\0\0\0\0\80!w²»\0\0\0\0\0\0\0\80z~»»\0\0\0\0\0\0\0\80iÑþ»\0\0\0\0\0\0\0\80:âï»\0\0\0\0\0\0\0\80\90\9aÛ»\0\0\0\0\0\0\0\80¶ìé»\0\0\0\0\0\0\0\80°`\0¼\0\0\0\0\0\0\0\80úÍõ»\0\0\0\0\0\0\0\80\16ºN»\0\0\0\0\0\0\0\80*UW»\0\0\0\0\0\0\0\80z~»»\0\0\0\0\0\0\0\80!w²»\0\0\0\0\0\0\0\80D\ 3Ú»\0\0\0\0\0\0\0\80Pûò»\0\0\0\0\0\0\0\80¶ìé»\0\0\0\0\0\0\0\80\90\9aÛ»\0\0\0\0\0\0\0\80\7fä\98º\0\0\0\0\0\0\0\80íÑ\9dº\0\0\0\0\0\0\0\80*UW»\0\0\0\0\0\0\0\80\16ºN»\0\0\0\0\0\0\0\80iÑþ»\0\0\0\0\0\0\0\80§ßñ»\0\0\0\0\0\0\0\80\18\9a\b¼\0\0\0\0\0\0\0\80ø-\ e¼\0\0\0\0\0\0\0\80°`\0¼\0\0\0\0\0\0\0\80æè\ 1¼\0\0\0\0\0\0\0\80Z-\f¼\0\0\0\0\0\0\0\80\10{
\0\0\0\0\0\0\0\80z~»»\0\0\0\0\0\0\0\80"w²»\0\0\0\0\0\0\0\80§ßñ»\0\0\0\0\0\0\0\80iÑþ»\0\0\0\0\0\0\0\80¶ìé»\0\0\0\0\0\0\0\80¨¡ã»\0\0\0\0\0\0\0\80æè\ 1¼\0\0\0\0\0\0\0\80°`\0¼\0\0\0\0\0\0\0\80*UW»\0\0\0\0\0\0\0\80\17ºN»\0\0\0\0\0\0\0\80"w²»\0\0\0\0\0\0\0\80z~»»\0\0\0\0\0\0\0\80Pûò»\0\0\0\0\0\0\0\80\aÐâ»\0\0\0\0\0\0\0\80¨¡ã»\0\0\0\0\0\0\0\80¶ìé»\0\0\0\0\0\0\0\80íÑ\9dº\0\0\0\0\0\0\0\80\7fä\98º\0\0\0\0\0\0\0\80\17ºN»\0\0\0\0\0\0\0\80*UW»\0\0\0\0\0\0\0\80\ 2.\ 4¼\0\0\0\0\0\0\0\80\86Ñ\0¼\0\0\0\0\0\0\0\80\aÐâ»\0\0\0\0\0\0\0\80Pûò»\0\0\0\0\0\0\0\80î\97\ 3¼\0\0\0\0\0\0\0\80Êí\a¼\0\0\0\0\0\0\0\80\86Ñ\0¼\0\0\0\0\0\0\0\80\ 2.\ 4¼\0\0\0\0\0\0\0\80\98ÿ\b¼\0\92\ 6·\0\0\0\80\7f\96\ 5¼\0¼\82·\0\0\0\80âÇλð+p¹\0\0\0\80\1a\80Ö»8+U¹\0\0\0\80O\v\b¼\0\0\0\0\0\0\0\80ö\e        ¼\0\0\0\0\0\0\0\80Êí\a¼\0\0\0\0\0\0\0\80î\97\ 3¼\0\0\0\0\0\0\0\80\91Þ\ f¼\0\0\0\0\0\0\0\80\ f¼\0\0\0\0\0\0\0\80\7f\96\ 5¼\0¼\82·\0\0\0\80\98ÿ\b¼\0\92\ 6·\0\0\0\80ø-\ e¼\0\0\0\0\0\0\0\80\18\9a\b¼\0\0\0\0\0\0\0\80ö\e     ¼\0\0\0\0\0\0\0\80O\v\b¼\0\0\0\0\0\0\0\80\10{
\0\0\0\0\0\0\0\80Z-\f¼\0\0\0\0\0\0\0\80\ f¼\0\0\0\0\0\0\0\80\91Þ\ f¼\0\0\0\0\0\0\0\80Êí\a¼\0\0\0\0\0\0\0\80}2é»\0\0\0\0\0\0\0\80\ 4YÁ»\0\0\0\0\0\0\0\80\86Ñ\0¼\0\0\0\0\0\0\0\80\7f\96\ 5¼\0¼\82·\0\0\0\80\80Ðû»\80ý\1e¸\0\0\0\80¤\ 6ûL®\8c¹\0\0\0\80âÇλð+p¹\0\0\0\80ö\e        ¼\0\0\0\0\0\0\0\80<Võ»\0\0\0\0\0\0\0\80}2é»\0\0\0\0\0\0\0\80Êí\a¼\0\0\0\0\0\0\0\80\ f¼\0\0\0\0\0\0\0\80©^
\0\0\0\0\0\0\0\80\80Ðû»\80ý\1e¸\0\0\0\80\7f\96\ 5¼\0¼\82·\0\0\0\80\18\9a\b¼\0\0\0\0\0\0\0\80øIí»\0\0\0\0\0\0\0\80<Võ»\0\0\0\0\0\0\0\80ö\e        ¼\0\0\0\0\0\0\0\80Z-\f¼\0\0\0\0\0\0\0\80\7fº\a¼\0\0\0\0\0\0\0\80©^
\0\0\0\0\0\0\0\80\ f¼\0\0\0\0\0\0\0\80§ßñ»\0\0\0\0\0\0\0\80\ 2bÍ»\0\0\0\0\0\0\0\80øIí»\0\0\0\0\0\0\0\80\18\9a\b¼\0\0\0\0\0\0\0\80æè\ 1¼\0\0\0\0\0\0\0\80`\1cò»\0\0\0\0\0\0\0\80\7fº\a¼\0\0\0\0\0\0\0\80Z-\f¼\0\0\0\0\0\0\0\80"w²»\0\0\0\0\0\0\0\80Ñ\92\99»\0\0\0\0\0\0\0\80\ 2bÍ»\0\0\0\0\0\0\0\80§ßñ»\0\0\0\0\0\0\0\80¨¡ã»\0\0\0\0\0\0\0\80ºß¿»\0\0\0\0\0\0\0\80`\1cò»\0\0\0\0\0\0\0\80æè\ 1¼\0\0\0\0\0\0\0\80\17ºN»\0\0\0\0\0\0\0\80Ú|6»\0\0\0\0\0\0\0\80Ñ\92\99»\0\0\0\0\0\0\0\80"w²»\0\0\0\0\0\0\0\80\aÐâ»\0\0\0\0\0\0\0\80/Q¨»\0\0\0\0\0\0\0\80ºß¿»\0\0\0\0\0\0\0\80¨¡ã»\0\0\0\0\0\0\0\80\7fä\98º\0\0\0\0\0\0\0\80\8aº\0\0\0\0\0\0\0\80Ú|6»\0\0\0\0\0\0\0\80\17ºN»\0\0\0\0\0\0\0\80\86Ñ\0¼\0\0\0\0\0\0\0\80\ 4YÁ»\0\0\0\0\0\0\0\80/Q¨»\0\0\0\0\0\0\0\80\aÐâ»\0\0\0\0\0\0\0\80Ñ\92\99»\0\0\0\0\0\0\0\80½¶l»\0\0\0\0\0\0\0\80}'\95»\0\0\0\0\0\0\0\80\ 2bÍ»\0\0\0\0\0\0\0\80ºß¿»\0\0\0\0\0\0\0\80e1\87»\0\0\0\0\0\0\0\80*\9aû\0\0\0\0\0\0\0\80`\1cò»\0\0\0\0\0\0\0\80Ú|6»\0\0\0\0\0\0\0\80\12»\0\0\0\0\0\0\0\80½¶l»\0\0\0\0\0\0\0\80Ñ\92\99»\0\0\0\0\0\0\0\80/Q¨»\0\0\0\0\0\0\0\80\89\7f\0\0\0\0\0\0\0\80e1\87»\0\0\0\0\0\0\0\80ºß¿»\0\0\0\0\0\0\0\80\8aº\0\0\0\0\0\0\0\80\81üjº\0\0\0\0\0\0\0\80\12»\0\0\0\0\0\0\0\80Ú|6»\0\0\0\0\0\0\0\80\ 4YÁ»\0\0\0\0\0\0\0\80\86$4»\0\0\0\0\0\0\0\80\89\7f\0\0\0\0\0\0\0\80/Q¨»\0\0\0\0\0\0\0\80}2é»\0\0\0\0\0\0\0\80®¨\86»\0\0\0\0\0\0\0\80\86$4»\0\0\0\0\0\0\0\80\ 4YÁ»\0\0\0\0\0\0\0\80\80Ðû»\80ý\1e¸\0\0\0\805"á»\80}¼¸\0\0\0\80,ʳ»|E¨¹\0\0\0\80¤\ 6ûL®\8c¹\0\0\0\80<Võ»\0\0\0\0\0\0\0\80µÎ£»\0\0\0\0\0\0\0\80®¨\86»\0\0\0\0\0\0\0\80}2é»\0\0\0\0\0\0\0\80©^
\0\0\0\0\0\0\0\80\ 3\ e÷»\0\0\0\0\0\0\0\805"á»\80}¼¸\0\0\0\80\80Ðû»\80ý\1e¸\0\0\0\80øIí»\0\0\0\0\0\0\0\80\1aQ§»\0\0\0\0\0\0\0\80µÎ£»\0\0\0\0\0\0\0\80<Võ»\0\0\0\0\0\0\0\80\7fº\a¼\0\0\0\0\0\0\0\80þ\1dî»\0\0\0\0\0\0\0\80\ 3\ e÷»\0\0\0\0\0\0\0\80©^
\0\0\0\0\0\0\0\80\ 2bÍ»\0\0\0\0\0\0\0\80}'\95»\0\0\0\0\0\0\0\80\1aQ§»\0\0\0\0\0\0\0\80øIí»\0\0\0\0\0\0\0\80`\1cò»\0\0\0\0\0\0\0\80*\9aû\0\0\0\0\0\0\0\80þ\1dî»\0\0\0\0\0\0\0\80\7fº\a¼\0\0\0\0\0\0\0\805"á»\80}¼¸\0\0\0\80`×»»ð6?¹\0\0\0\80Om¢»\10]ɹ\0\0\0\80,ʳ»|E¨¹\0\0\0\80µÎ£»\0\0\0\0\0\0\0\80Òº\ 3»\0\0\0\0\0\0\0\80ûO\95º\0\0\0\0\0\0\0\80®¨\86»\0\0\0\0\0\0\0\80\ 3\ e÷»\0\0\0\0\0\0\0\80¼òû\80\9f\0\0\0\80`×»»ð6?¹\0\0\0\805"á»\80}¼¸\0\0\0\80\1aQ§»\0\0\0\0\0\0\0\80T?+»\0\0\0\0\0\0\0\80Òº\ 3»\0\0\0\0\0\0\0\80µÎ£»\0\0\0\0\0\0\0\80þ\1dî»\0\0\0\0\0\0\0\80Dð±»\0\0\0\0\0\0\0\80¼òû\80\9f\0\0\0\80\ 3\ e÷»\0\0\0\0\0\0\0\80}'\95»\0\0\0\0\0\0\0\80â58»\0\0\0\0\0\0\0\80T?+»\0\0\0\0\0\0\0\80\1aQ§»\0\0\0\0\0\0\0\80*\9aû\0\0\0\0\0\0\0\805L\83»\0\0\0\0\0\0\0\80Dð±»\0\0\0\0\0\0\0\80þ\1dî»\0\0\0\0\0\0\0\80½¶l»\0\0\0\0\0\0\0\80,`\1f»\0\0\0\0\0\0\0\80â58»\0\0\0\0\0\0\0\80}'\95»\0\0\0\0\0\0\0\80e1\87»\0\0\0\0\0\0\0\80®>\16»\0\0\0\0\0\0\0\805L\83»\0\0\0\0\0\0\0\80*\9aû\0\0\0\0\0\0\0\80\12»\0\0\0\0\0\0\0\80:êÓº\0\0\0\0\0\0\0\80,`\1f»\0\0\0\0\0\0\0\80½¶l»\0\0\0\0\0\0\0\80\89\7f\0\0\0\0\0\0\0\802\11\0\0\0\0\0\0\0\80®>\16»\0\0\0\0\0\0\0\80e1\87»\0\0\0\0\0\0\0\80\81üjº\0\0\0\0\0\0\0\80¬¤7º\0\0\0\0\0\0\0\80:êÓº\0\0\0\0\0\0\0\80\12»\0\0\0\0\0\0\0\80\86$4»\0\0\0\0\0\0\0\80\b\r͹\0\0\0\0\0\0\0\802\11\0\0\0\0\0\0\0\80\89\7f\0\0\0\0\0\0\0\80®¨\86»\0\0\0\0\0\0\0\80ûO\95º\0\0\0\0\0\0\0\80\b\r͹\0\0\0\0\0\0\0\80\86$4»\0\0\0\0\0\0\0\80®>\16»\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\805L\83»\0\0\0\0\0\0\0\80:êÓº\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80,`\1f»\0\0\0\0\0\0\0\802\11\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80®>\16»\0\0\0\0\0\0\0\80¬¤7º\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80:êÓº\0\0\0\0\0\0\0\80\b\r͹\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\802\11\0\0\0\0\0\0\0\80ûO\95º\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\b\r͹\0\0\0\0\0\0\0\80`×»»ð6?¹\0\0\0\80\0\0\0\0\0à\ 4º\0\0\0\80\0\0\0\0d\94\0\0\0\80Om¢»\10]ɹ\0\0\0\80Òº\ 3»\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80ûO\95º\0\0\0\0\0\0\0\80¼òû\80\9f\0\0\0\80\0\0\0\0 \9a\98¹\0\0\0\80\0\0\0\0\0à\ 4º\0\0\0\80`×»»ð6?¹\0\0\0\80T?+»\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Òº\ 3»\0\0\0\0\0\0\0\80Dð±»\0\0\0\0\0\0\0\80\0\0\0\0\0±ß¸\0\0\0\80\0\0\0\0 \9a\98¹\0\0\0\80¼òû\80\9f\0\0\0\80â58»\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80T?+»\0\0\0\0\0\0\0\805L\83»\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0±ß¸\0\0\0\80Dð±»\0\0\0\0\0\0\0\80,`\1f»\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80â58»\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Öº\ 3;\0\0\0\0\0\0\0\80ïO\95:\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0 \9a\98¹\0\0\0\80¾òÃ;\80\9f\0\0\0\80b×»;\107?¹\0\0\0\80\0\0\0\0\0à\ 4º\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80L?+;\0\0\0\0\0\0\0\80Öº\ 3;\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0±ß¸\0\0\0\80Bð±;\0\0\0\0\0\0\0\80¾òÃ;\80\9f\0\0\0\80\0\0\0\0 \9a\98¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80ß58;\0\0\0\0\0\0\0\80L?+;\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\802L\83;\0\0\0\0\0\0\0\80Bð±;\0\0\0\0\0\0\0\80\0\0\0\0\0±ß¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80+`\1f;\0\0\0\0\0\0\0\80ß58;\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80´>\16;\0\0\0\0\0\0\0\802L\83;\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80>êÓ:\0\0\0\0\0\0\0\80+`\1f;\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\1a\11U:\0\0\0\0\0\0\0\80´>\16;\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80®¤7:\0\0\0\0\0\0\0\80>êÓ:\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Ä\fÍ9\0\0\0\0\0\0\0\80\1a\11U:\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80ïO\95:\0\0\0\0\0\0\0\80Ä\fÍ9\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0à\ 4º\0\0\0\80b×»;\107?¹\0\0\0\80Om¢;\1c]ɹ\0\0\0\80\0\0\0\0d\94\0\0\0\80>êÓ:\0\0\0\0\0\0\0\80\12;\0\0\0\0\0\0\0\80½¶l;\0\0\0\0\0\0\0\80+`\1f;\0\0\0\0\0\0\0\80\1a\11U:\0\0\0\0\0\0\0\80\84\7f.;\0\0\0\0\0\0\0\80h1\87;\0\0\0\0\0\0\0\80´>\16;\0\0\0\0\0\0\0\80®¤7:\0\0\0\0\0\0\0\80\86üj:\0\0\0\0\0\0\0\80\12;\0\0\0\0\0\0\0\80>êÓ:\0\0\0\0\0\0\0\80Ä\fÍ9\0\0\0\0\0\0\0\80~$4;\0\0\0\0\0\0\0\80\84\7f.;\0\0\0\0\0\0\0\80\1a\11U:\0\0\0\0\0\0\0\80ïO\95:\0\0\0\0\0\0\0\80¬¨\86;\0\0\0\0\0\0\0\80~$4;\0\0\0\0\0\0\0\80Ä\fÍ9\0\0\0\0\0\0\0\80b×»;\107?¹\0\0\0\807"á;\0~¼¸\0\0\0\80-ʳ;\8cE¨¹\0\0\0\80Om¢;\1c]ɹ\0\0\0\80Öº\ 3;\0\0\0\0\0\0\0\80¸Î£;\0\0\0\0\0\0\0\80¬¨\86;\0\0\0\0\0\0\0\80ïO\95:\0\0\0\0\0\0\0\80¾òÃ;\80\9f\0\0\0\80\ 2\ e÷;\0\0\0\0\0\0\0\807"á;\0~¼¸\0\0\0\80b×»;\107?¹\0\0\0\80L?+;\0\0\0\0\0\0\0\80\16Q§;\0\0\0\0\0\0\0\80¸Î£;\0\0\0\0\0\0\0\80Öº\ 3;\0\0\0\0\0\0\0\80Bð±;\0\0\0\0\0\0\0\80ý\1dî;\0\0\0\0\0\0\0\80\ 2\ e÷;\0\0\0\0\0\0\0\80¾òÃ;\80\9f\0\0\0\80ß58;\0\0\0\0\0\0\0\80|'\95;\0\0\0\0\0\0\0\80\16Q§;\0\0\0\0\0\0\0\80L?+;\0\0\0\0\0\0\0\802L\83;\0\0\0\0\0\0\0\80(\9aÃ;\0\0\0\0\0\0\0\80ý\1dî;\0\0\0\0\0\0\0\80Bð±;\0\0\0\0\0\0\0\80+`\1f;\0\0\0\0\0\0\0\80½¶l;\0\0\0\0\0\0\0\80|'\95;\0\0\0\0\0\0\0\80ß58;\0\0\0\0\0\0\0\80´>\16;\0\0\0\0\0\0\0\80h1\87;\0\0\0\0\0\0\0\80(\9aÃ;\0\0\0\0\0\0\0\802L\83;\0\0\0\0\0\0\0\80\ 2\ e÷;\0\0\0\0\0\0\0\80ª^
+<\0\0\0\0\0\0\0\80\82Ðû;Àþ\1e¸\0\0\0\807"á;\0~¼¸\0\0\0\80\16Q§;\0\0\0\0\0\0\0\80ùIí;\0\0\0\0\0\0\0\80@Võ;\0\0\0\0\0\0\0\80¸Î£;\0\0\0\0\0\0\0\80ý\1dî;\0\0\0\0\0\0\0\80\80º\a<\0\0\0\0\0\0\0\80ª^
+<\0\0\0\0\0\0\0\80\ 2\ e÷;\0\0\0\0\0\0\0\80|'\95;\0\0\0\0\0\0\0\80\ 3bÍ;\0\0\0\0\0\0\0\80ùIí;\0\0\0\0\0\0\0\80\16Q§;\0\0\0\0\0\0\0\80(\9aÃ;\0\0\0\0\0\0\0\80a\1cò;\0\0\0\0\0\0\0\80\80º\a<\0\0\0\0\0\0\0\80ý\1dî;\0\0\0\0\0\0\0\80½¶l;\0\0\0\0\0\0\0\80Ò\92\99;\0\0\0\0\0\0\0\80\ 3bÍ;\0\0\0\0\0\0\0\80|'\95;\0\0\0\0\0\0\0\80h1\87;\0\0\0\0\0\0\0\80¾ß¿;\0\0\0\0\0\0\0\80a\1cò;\0\0\0\0\0\0\0\80(\9aÃ;\0\0\0\0\0\0\0\80\12;\0\0\0\0\0\0\0\80Ý|6;\0\0\0\0\0\0\0\80Ò\92\99;\0\0\0\0\0\0\0\80½¶l;\0\0\0\0\0\0\0\80\84\7f.;\0\0\0\0\0\0\0\80.Q¨;\0\0\0\0\0\0\0\80¾ß¿;\0\0\0\0\0\0\0\80h1\87;\0\0\0\0\0\0\0\80\86üj:\0\0\0\0\0\0\0\80\8a:\0\0\0\0\0\0\0\80Ý|6;\0\0\0\0\0\0\0\80\12;\0\0\0\0\0\0\0\80~$4;\0\0\0\0\0\0\0\80\ 2YÁ;\0\0\0\0\0\0\0\80.Q¨;\0\0\0\0\0\0\0\80\84\7f.;\0\0\0\0\0\0\0\80¬¨\86;\0\0\0\0\0\0\0\80|2é;\0\0\0\0\0\0\0\80\ 2YÁ;\0\0\0\0\0\0\0\80~$4;\0\0\0\0\0\0\0\807"á;\0~¼¸\0\0\0\80\82Ðû;Àþ\1e¸\0\0\0\80¤\ 6Ã;`®\8c¹\0\0\0\80-ʳ;\8cE¨¹\0\0\0\80¸Î£;\0\0\0\0\0\0\0\80@Võ;\0\0\0\0\0\0\0\80|2é;\0\0\0\0\0\0\0\80¬¨\86;\0\0\0\0\0\0\0\80.Q¨;\0\0\0\0\0\0\0\80      Ðâ;\0\0\0\0\0\0\0\80­¡ã;\0\0\0\0\0\0\0\80¾ß¿;\0\0\0\0\0\0\0\80\8a:\0\0\0\0\0\0\0\80\84ä\98:\0\0\0\0\0\0\0\80\eºN;\0\0\0\0\0\0\0\80Ý|6;\0\0\0\0\0\0\0\80\ 2YÁ;\0\0\0\0\0\0\0\80\85Ñ\0<\0\0\0\0\0\0\0\80     Ðâ;\0\0\0\0\0\0\0\80.Q¨;\0\0\0\0\0\0\0\80|2é;\0\0\0\0\0\0\0\80Êí\a<\0\0\0\0\0\0\0\80\85Ñ\0<\0\0\0\0\0\0\0\80\ 2YÁ;\0\0\0\0\0\0\0\80\82Ðû;Àþ\1e¸\0\0\0\80\80\96\ 5<\80¿\82·\0\0\0\80æÇÎ; ,p¹\0\0\0\80¤\ 6Ã;`®\8c¹\0\0\0\80@Võ;\0\0\0\0\0\0\0\80ø\e   <\0\0\0\0\0\0\0\80Êí\a<\0\0\0\0\0\0\0\80|2é;\0\0\0\0\0\0\0\80ª^
+<\0\0\0\0\0\0\0\80\ f<\0\0\0\0\0\0\0\80\80\96\ 5<\80¿\82·\0\0\0\80\82Ðû;Àþ\1e¸\0\0\0\80ùIí;\0\0\0\0\0\0\0\80\18\9a\b<\0\0\0\0\0\0\0\80ø\e        <\0\0\0\0\0\0\0\80@Võ;\0\0\0\0\0\0\0\80\80º\a<\0\0\0\0\0\0\0\80[-\f<\0\0\0\0\0\0\0\80\ f<\0\0\0\0\0\0\0\80ª^
+<\0\0\0\0\0\0\0\80\ 3bÍ;\0\0\0\0\0\0\0\80¨ßñ;\0\0\0\0\0\0\0\80\18\9a\b<\0\0\0\0\0\0\0\80ùIí;\0\0\0\0\0\0\0\80a\1cò;\0\0\0\0\0\0\0\80æè\ 1<\0\0\0\0\0\0\0\80[-\f<\0\0\0\0\0\0\0\80\80º\a<\0\0\0\0\0\0\0\80Ò\92\99;\0\0\0\0\0\0\0\80$w²;\0\0\0\0\0\0\0\80¨ßñ;\0\0\0\0\0\0\0\80\ 3bÍ;\0\0\0\0\0\0\0\80¾ß¿;\0\0\0\0\0\0\0\80­¡ã;\0\0\0\0\0\0\0\80æè\ 1<\0\0\0\0\0\0\0\80a\1cò;\0\0\0\0\0\0\0\80Ý|6;\0\0\0\0\0\0\0\80\eºN;\0\0\0\0\0\0\0\80$w²;\0\0\0\0\0\0\0\80Ò\92\99;\0\0\0\0\0\0\0\80\18\9a\b<\0\0\0\0\0\0\0\80û-\ e<\0\0\0\0\0\0\0\80Q\v\b<\0\0\0\0\0\0\0\80ø\e        <\0\0\0\0\0\0\0\80[-\f<\0\0\0\0\0\0\0\80\12{
+<\0\0\0\0\0\0\0\80\92Þ\ f<\0\0\0\0\0\0\0\80\ f<\0\0\0\0\0\0\0\80¨ßñ;\0\0\0\0\0\0\0\80mÑþ;\0\0\0\0\0\0\0\80û-\ e<\0\0\0\0\0\0\0\80\18\9a\b<\0\0\0\0\0\0\0\80æè\ 1<\0\0\0\0\0\0\0\80²`\0<\0\0\0\0\0\0\0\80\12{
+<\0\0\0\0\0\0\0\80[-\f<\0\0\0\0\0\0\0\80$w²;\0\0\0\0\0\0\0\80}~»;\0\0\0\0\0\0\0\80mÑþ;\0\0\0\0\0\0\0\80¨ßñ;\0\0\0\0\0\0\0\80­¡ã;\0\0\0\0\0\0\0\80ºìé;\0\0\0\0\0\0\0\80²`\0<\0\0\0\0\0\0\0\80æè\ 1<\0\0\0\0\0\0\0\80\eºN;\0\0\0\0\0\0\0\801UW;\0\0\0\0\0\0\0\80}~»;\0\0\0\0\0\0\0\80$w²;\0\0\0\0\0\0\0\80  Ðâ;\0\0\0\0\0\0\0\80Tûò;\0\0\0\0\0\0\0\80ºìé;\0\0\0\0\0\0\0\80­¡ã;\0\0\0\0\0\0\0\80\84ä\98:\0\0\0\0\0\0\0\80ôÑ\9d:\0\0\0\0\0\0\0\801UW;\0\0\0\0\0\0\0\80\eºN;\0\0\0\0\0\0\0\80\85Ñ\0<\0\0\0\0\0\0\0\80\ 3.\ 4<\0\0\0\0\0\0\0\80Tûò;\0\0\0\0\0\0\0\80     Ðâ;\0\0\0\0\0\0\0\80Êí\a<\0\0\0\0\0\0\0\80ð\97\ 3<\0\0\0\0\0\0\0\80\ 3.\ 4<\0\0\0\0\0\0\0\80\85Ñ\0<\0\0\0\0\0\0\0\80\80\96\ 5<\80¿\82·\0\0\0\80\9bÿ\b<\0\99\ 6·\0\0\0\80\1c\80Ö;h+U¹\0\0\0\80æÇÎ; ,p¹\0\0\0\80ø\e   <\0\0\0\0\0\0\0\80Q\v\b<\0\0\0\0\0\0\0\80ð\97\ 3<\0\0\0\0\0\0\0\80Êí\a<\0\0\0\0\0\0\0\80\ f<\0\0\0\0\0\0\0\80\92Þ\ f<\0\0\0\0\0\0\0\80\9bÿ\b<\0\99\ 6·\0\0\0\80\80\96\ 5<\80¿\82·\0\0\0\80ôÑ\9d:\0\0\0\0\0\0\0\80\88ä\98:\0\0\0\0\0\0\0\80\1fºN;\0\0\0\0\0\0\0\801UW;\0\0\0\0\0\0\0\80\ 3.\ 4<\0\0\0\0\0\0\0\80Lùè;\0\0\0\0\0\0\0\80H\ 3Ú;\0\0\0\0\0\0\0\80Tûò;\0\0\0\0\0\0\0\80ð\97\ 3<\0\0\0\0\0\0\0\80\88?è;\0\0\0\0\0\0\0\80Lùè;\0\0\0\0\0\0\0\80\ 3.\ 4<\0\0\0\0\0\0\0\80\9bÿ\b<\0\99\ 6·\0\0\0\809\9b     <\03ܶ\0\0\0\80z'Ú;4%G¹\0\0\0\80\1c\80Ö;h+U¹\0\0\0\80Q\v\b<\0\0\0\0\0\0\0\80nuö;\0\0\0\0\0\0\0\80\88?è;\0\0\0\0\0\0\0\80ð\97\ 3<\0\0\0\0\0\0\0\80\92Þ\ f<\0\0\0\0\0\0\0\80æ÷\r<\0\0\0\0\0\0\0\809\9b     <\03ܶ\0\0\0\80\9bÿ\b<\0\99\ 6·\0\0\0\80û-\ e<\0\0\0\0\0\0\0\80l\96\ 3<\0\0\0\0\0\0\0\80nuö;\0\0\0\0\0\0\0\80Q\v\b<\0\0\0\0\0\0\0\80\12{
+<\0\0\0\0\0\0\0\80\ 5\95\ 6<\0\0\0\0\0\0\0\80æ÷\r<\0\0\0\0\0\0\0\80\92Þ\ f<\0\0\0\0\0\0\0\80mÑþ;\0\0\0\0\0\0\0\80>âï;\0\0\0\0\0\0\0\80l\96\ 3<\0\0\0\0\0\0\0\80û-\ e<\0\0\0\0\0\0\0\80²`\0<\0\0\0\0\0\0\0\80\ 1Îõ;\0\0\0\0\0\0\0\80\ 5\95\ 6<\0\0\0\0\0\0\0\80\12{
+<\0\0\0\0\0\0\0\80}~»;\0\0\0\0\0\0\0\80%w²;\0\0\0\0\0\0\0\80>âï;\0\0\0\0\0\0\0\80mÑþ;\0\0\0\0\0\0\0\80ºìé;\0\0\0\0\0\0\0\80\94\9aÛ;\0\0\0\0\0\0\0\80\ 1Îõ;\0\0\0\0\0\0\0\80²`\0<\0\0\0\0\0\0\0\801UW;\0\0\0\0\0\0\0\80\1fºN;\0\0\0\0\0\0\0\80%w²;\0\0\0\0\0\0\0\80}~»;\0\0\0\0\0\0\0\80Tûò;\0\0\0\0\0\0\0\80H\ 3Ú;\0\0\0\0\0\0\0\80\94\9aÛ;\0\0\0\0\0\0\0\80ºìé;\0\0\0\0\0\0\0\80>âï;\0\0\0\0\0\0\0\80vjÈ;\0\0\0\0\0\0\0\80\ 4mÕ;\0\0\0\0\0\0\0\80l\96\ 3<\0\0\0\0\0\0\0\80\ 1Îõ;\0\0\0\0\0\0\0\80\10éç;\0\0\0\0\0\0\0\80Ú*\ 2<\0\0\0\0\0\0\0\80\ 5\95\ 6<\0\0\0\0\0\0\0\80%w²;\0\0\0\0\0\0\0\80Û\92\99;\0\0\0\0\0\0\0\80vjÈ;\0\0\0\0\0\0\0\80>âï;\0\0\0\0\0\0\0\80\94\9aÛ;\0\0\0\0\0\0\0\80\94\9aÆ;\0\0\0\0\0\0\0\80\10éç;\0\0\0\0\0\0\0\80\ 1Îõ;\0\0\0\0\0\0\0\80\1fºN;\0\0\0\0\0\0\0\80ä|6;\0\0\0\0\0\0\0\80Û\92\99;\0\0\0\0\0\0\0\80%w²;\0\0\0\0\0\0\0\80H\ 3Ú;\0\0\0\0\0\0\0\80²Þ®;\0\0\0\0\0\0\0\80\94\9aÆ;\0\0\0\0\0\0\0\80\94\9aÛ;\0\0\0\0\0\0\0\80\88ä\98:\0\0\0\0\0\0\0\80\8a:\0\0\0\0\0\0\0\80ä|6;\0\0\0\0\0\0\0\80\1fºN;\0\0\0\0\0\0\0\80Lùè;\0\0\0\0\0\0\0\80¸ù°;\0\0\0\0\0\0\0\80²Þ®;\0\0\0\0\0\0\0\80H\ 3Ú;\0\0\0\0\0\0\0\80\88?è;\0\0\0\0\0\0\0\80Àn·;\0\0\0\0\0\0\0\80¸ù°;\0\0\0\0\0\0\0\80Lùè;\0\0\0\0\0\0\0\809\9b    <\03ܶ\0\0\0\80ç>\b\81
\0\0\0\80OóÙ;\10¬D¹\0\0\0\80z'Ú;4%G¹\0\0\0\80nuö;\0\0\0\0\0\0\0\80LçÅ;\0\0\0\0\0\0\0\80Àn·;\0\0\0\0\0\0\0\80\88?è;\0\0\0\0\0\0\0\80æ÷\r<\0\0\0\0\0\0\0\80òS\v<\0\0\0\0\0\0\0\80ç>\b\81
\0\0\0\809\9b        <\03ܶ\0\0\0\80l\96\ 3<\0\0\0\0\0\0\0\80\ 4mÕ;\0\0\0\0\0\0\0\80LçÅ;\0\0\0\0\0\0\0\80nuö;\0\0\0\0\0\0\0\80\ 5\95\ 6<\0\0\0\0\0\0\0\80Ú*\ 2<\0\0\0\0\0\0\0\80òS\v<\0\0\0\0\0\0\0\80æ÷\r<\0\0\0\0\0\0\0\80Àn·;\0\0\0\0\0\0\0\80Ì\8ah;\0\0\0\0\0\0\0\80LN[;\0\0\0\0\0\0\0\80¸ù°;\0\0\0\0\0\0\0\80ç>\b\81
\0\0\0\80ð\10\ 5<E«Z·\0\0\0\80Ò-Ö;3®L¹\0\0\0\80OóÙ;\10¬D¹\0\0\0\80LçÅ;\0\0\0\0\0\0\0\80âÛ\84;\0\0\0\0\0\0\0\80Ì\8ah;\0\0\0\0\0\0\0\80Àn·;\0\0\0\0\0\0\0\80òS\v<\0\0\0\0\0\0\0\80îý\a<\0\0\0\0\0\0\0\80ð\10\ 5<E«Z·\0\0\0\80ç>\b\81
\0\0\0\80\ 4mÕ;\0\0\0\0\0\0\0\80"-\95;\0\0\0\0\0\0\0\80âÛ\84;\0\0\0\0\0\0\0\80LçÅ;\0\0\0\0\0\0\0\80Ú*\ 2<\0\0\0\0\0\0\0\80µÇú;\0\0\0\0\0\0\0\80îý\a<\0\0\0\0\0\0\0\80òS\v<\0\0\0\0\0\0\0\80vjÈ;\0\0\0\0\0\0\0\80\1f]\93;\0\0\0\0\0\0\0\80"-\95;\0\0\0\0\0\0\0\80\ 4mÕ;\0\0\0\0\0\0\0\80\10éç;\0\0\0\0\0\0\0\80\12<Ø;\0\0\0\0\0\0\0\80µÇú;\0\0\0\0\0\0\0\80Ú*\ 2<\0\0\0\0\0\0\0\80Û\92\99;\0\0\0\0\0\0\0\80ȶl;\0\0\0\0\0\0\0\80\1f]\93;\0\0\0\0\0\0\0\80vjÈ;\0\0\0\0\0\0\0\80\94\9aÆ;\0\0\0\0\0\0\0\808+®;\0\0\0\0\0\0\0\80\12<Ø;\0\0\0\0\0\0\0\80\10éç;\0\0\0\0\0\0\0\80ä|6;\0\0\0\0\0\0\0\80\12;\0\0\0\0\0\0\0\80ȶl;\0\0\0\0\0\0\0\80Û\92\99;\0\0\0\0\0\0\0\80²Þ®;\0\0\0\0\0\0\0\80jx\82;\0\0\0\0\0\0\0\808+®;\0\0\0\0\0\0\0\80\94\9aÆ;\0\0\0\0\0\0\0\80\8a:\0\0\0\0\0\0\0\80\98üj:\0\0\0\0\0\0\0\80\12;\0\0\0\0\0\0\0\80ä|6;\0\0\0\0\0\0\0\80¸ù°;\0\0\0\0\0\0\0\80LN[;\0\0\0\0\0\0\0\80jx\82;\0\0\0\0\0\0\0\80²Þ®;\0\0\0\0\0\0\0\80\12<Ø;\0\0\0\0\0\0\0\80h\16È;\0\0\0\0\0\0\0\80\87
+ð;\0\0\0\0\0\0\0\80µÇú;\0\0\0\0\0\0\0\80ȶl;\0\0\0\0\0\0\0\80>`\1f;\0\0\0\0\0\0\0\80ê58;\0\0\0\0\0\0\0\80\1f]\93;\0\0\0\0\0\0\0\808+®;\0\0\0\0\0\0\0\80Ò\ 3\96;\0\0\0\0\0\0\0\80h\16È;\0\0\0\0\0\0\0\80\12<Ø;\0\0\0\0\0\0\0\80\12;\0\0\0\0\0\0\0\80TêÓ:\0\0\0\0\0\0\0\80>`\1f;\0\0\0\0\0\0\0\80ȶl;\0\0\0\0\0\0\0\80jx\82;\0\0\0\0\0\0\0\80dò>;\0\0\0\0\0\0\0\80Ò\ 3\96;\0\0\0\0\0\0\0\808+®;\0\0\0\0\0\0\0\80\98üj:\0\0\0\0\0\0\0\80º¤7:\0\0\0\0\0\0\0\80TêÓ:\0\0\0\0\0\0\0\80\12;\0\0\0\0\0\0\0\80LN[;\0\0\0\0\0\0\0\80HVÂ:\0\0\0\0\0\0\0\80dò>;\0\0\0\0\0\0\0\80jx\82;\0\0\0\0\0\0\0\80Ì\8ah;\0\0\0\0\0\0\0\80\0̲:\0\0\0\0\0\0\0\80HVÂ:\0\0\0\0\0\0\0\80LN[;\0\0\0\0\0\0\0\80ð\10\ 5<E«Z·\0\0\0\80\90\8cÿ; M½·\0\0\0\80q\84Î;H\13\0\0\0\80Ò-Ö;3®L¹\0\0\0\80âÛ\84;\0\0\0\0\0\0\0\80\90\88ÿ:\0\0\0\0\0\0\0\80\0̲:\0\0\0\0\0\0\0\80Ì\8ah;\0\0\0\0\0\0\0\80îý\a<\0\0\0\0\0\0\0\80b1\ 3<\0\0\0\0\0\0\0\80\90\8cÿ; M½·\0\0\0\80ð\10\ 5<E«Z·\0\0\0\80"-\95;\0\0\0\0\0\0\0\80\183*;\0\0\0\0\0\0\0\80\90\88ÿ:\0\0\0\0\0\0\0\80âÛ\84;\0\0\0\0\0\0\0\80µÇú;\0\0\0\0\0\0\0\80\87
+ð;\0\0\0\0\0\0\0\80b1\ 3<\0\0\0\0\0\0\0\80îý\a<\0\0\0\0\0\0\0\80\1f]\93;\0\0\0\0\0\0\0\80ê58;\0\0\0\0\0\0\0\80\183*;\0\0\0\0\0\0\0\80"-\95;\0\0\0\0\0\0\0\80\0̲:\0\0\0\0\0\0\0\80À»\ 49\0\0\0\0\0\0\0\800Ç,:\0\0\0\0\0\0\0\80HVÂ:\0\0\0\0\0\0\0\80\90\8cÿ; M½·\0\0\0\80¦ëï;@-)¸\0\0\0\80 \9eÂ;¾1\80¹\0\0\0\80q\84Î;H\13\0\0\0\80\90\88ÿ:\0\0\0\0\0\0\0\80
+:\0\0\0\0\0\0\0\80À»\ 49\0\0\0\0\0\0\0\80\0̲:\0\0\0\0\0\0\0\80b1\ 3<\0\0\0\0\0\0\0\80å\91ø;\0\0\0\0\0\0\0\80¦ëï;@-)¸\0\0\0\80\90\8cÿ; M½·\0\0\0\80\183*;\0\0\0\0\0\0\0\80\87:\0\0\0\0\0\0\0\80
+:\0\0\0\0\0\0\0\80\90\88ÿ:\0\0\0\0\0\0\0\80\87
+ð;\0\0\0\0\0\0\0\80ã_ä;\0\0\0\0\0\0\0\80å\91ø;\0\0\0\0\0\0\0\80b1\ 3<\0\0\0\0\0\0\0\80ê58;\0\0\0\0\0\0\0\80$@²:\0\0\0\0\0\0\0\80\87:\0\0\0\0\0\0\0\80\183*;\0\0\0\0\0\0\0\80h\16È;\0\0\0\0\0\0\0\80'ôº;\0\0\0\0\0\0\0\80ã_ä;\0\0\0\0\0\0\0\80\87
+ð;\0\0\0\0\0\0\0\80>`\1f;\0\0\0\0\0\0\0\80`´°:\0\0\0\0\0\0\0\80$@²:\0\0\0\0\0\0\0\80ê58;\0\0\0\0\0\0\0\80Ò\ 3\96;\0\0\0\0\0\0\0\80"u\84;\0\0\0\0\0\0\0\80'ôº;\0\0\0\0\0\0\0\80h\16È;\0\0\0\0\0\0\0\80TêÓ:\0\0\0\0\0\0\0\80¼è\83:\0\0\0\0\0\0\0\80`´°:\0\0\0\0\0\0\0\80>`\1f;\0\0\0\0\0\0\0\80dò>;\0\0\0\0\0\0\0\80ôA\ f;\0\0\0\0\0\0\0\80"u\84;\0\0\0\0\0\0\0\80Ò\ 3\96;\0\0\0\0\0\0\0\80º¤7:\0\0\0\0\0\0\0\80\ 2:\0\0\0\0\0\0\0\80¼è\83:\0\0\0\0\0\0\0\80TêÓ:\0\0\0\0\0\0\0\80HVÂ:\0\0\0\0\0\0\0\800Ç,:\0\0\0\0\0\0\0\80ôA\ f;\0\0\0\0\0\0\0\80dò>;\0\0\0\0\0\0\0\80`´°:\0\0\0\0\0\0\0\80\0\90       :\0\0\0\0\0\0\0\80PìÄ9\0\0\0\0\0\0\0\80$@²:\0\0\0\0\0\0\0\80"u\84;\0\0\0\0\0\0\0\80\9eëq;\0\0\0\0\0\0\0\80ÿj¯;\0\0\0\0\0\0\0\80'ôº;\0\0\0\0\0\0\0\80¼è\83:\0\0\0\0\0\0\0\80\f0\ 2:\0\0\0\0\0\0\0\80\0\90     :\0\0\0\0\0\0\0\80`´°:\0\0\0\0\0\0\0\80ôA\ f;\0\0\0\0\0\0\0\80°äú:\0\0\0\0\0\0\0\80\9eëq;\0\0\0\0\0\0\0\80"u\84;\0\0\0\0\0\0\0\80\ 2:\0\0\0\0\0\0\0\80¤\96¡9\0\0\0\0\0\0\0\80\f0\ 2:\0\0\0\0\0\0\0\80¼è\83:\0\0\0\0\0\0\0\800Ç,:\0\0\0\0\0\0\0\80ð~&:\0\0\0\0\0\0\0\80°äú:\0\0\0\0\0\0\0\80ôA\ f;\0\0\0\0\0\0\0\80À»\ 49\0\0\0\0\0\0\0\80\0þ@8\0\0\0\0\0\0\0\80ð~&:\0\0\0\0\0\0\0\800Ç,:\0\0\0\0\0\0\0\80¦ëï;@-)¸\0\0\0\80\92\1aØ; A\9e¸\0\0\0\80\81²;"ý\96¹\0\0\0\80 \9eÂ;¾1\80¹\0\0\0\80
+:\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0þ@8\0\0\0\0\0\0\0\80À»\ 49\0\0\0\0\0\0\0\80å\91ø;\0\0\0\0\0\0\0\80àÚá;\0\0\0\0\0\80\92\1aØ; A\9e¸\0\0\0\80¦ëï;@-)¸\0\0\0\80\87:\0\0\0\0\0\0\0\80À\89\159\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80
+:\0\0\0\0\0\0\0\80ã_ä;\0\0\0\0\0\0\0\80w{Ó;\0\0\0\0\0\0\0\80àÚá;\0\0\0\0\0\80å\91ø;\0\0\0\0\0\0\0\80$@²:\0\0\0\0\0\0\0\80PìÄ9\0\0\0\0\0\0\0\80À\89\159\0\0\0\0\0\0\0\80\87:\0\0\0\0\0\0\0\80'ôº;\0\0\0\0\0\0\0\80ÿj¯;\0\0\0\0\0\0\0\80w{Ó;\0\0\0\0\0\0\0\80ã_ä;\0\0\0\0\0\0\0\80\92\1aØ; A\9e¸\0\0\0\80²\8f·;À÷\ e¹\0\0\0\80\8c®\9f;$á³¹\0\0\0\80\81²;"ý\96¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0£Õ7\0\0\0\0\0\0\0\80\0þ@8\0\0\0\0\0\0\0\80àÚá;\0\0\0\0\0\80üa½;\0¤\83·\0\0\0\80²\8f·;À÷\ e¹\0\0\0\80\92\1aØ; A\9e¸\0\0\0\80À\89\159\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80w{Ó;\0\0\0\0\0\0\0\80\97X´;\0\0\0\0\0\0\0\80üa½;\0¤\83·\0\0\0\80àÚá;\0\0\0\0\0\80PìÄ9\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80À\89\159\0\0\0\0\0\0\0\80ÿj¯;\0\0\0\0\0\0\0\80÷|\95;\0\0\0\0\0\0\0\80\97X´;\0\0\0\0\0\0\0\80w{Ó;\0\0\0\0\0\0\0\80\0\90    :\0\0\0\0\0\0\0\80\0\b\968\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80PìÄ9\0\0\0\0\0\0\0\80\9eëq;\0\0\0\0\0\0\0\80\ 6)G;\0\0\0\0\0\0\0\80÷|\95;\0\0\0\0\0\0\0\80ÿj¯;\0\0\0\0\0\0\0\80\f0\ 2:\0\0\0\0\0\0\0\80`~\1e9\0\0\0\0\0\0\0\80\0\b\968\0\0\0\0\0\0\0\80\0\90     :\0\0\0\0\0\0\0\80°äú:\0\0\0\0\0\0\0\80\98~Ì:\0\0\0\0\0\0\0\80\ 6)G;\0\0\0\0\0\0\0\80\9eëq;\0\0\0\0\0\0\0\80¤\96¡9\0\0\0\0\0\0\0\80\90\96#9\0\0\0\0\0\0\0\80`~\1e9\0\0\0\0\0\0\0\80\f0\ 2:\0\0\0\0\0\0\0\80ð~&:\0\0\0\0\0\0\0\80Xv\ e:\0\0\0\0\0\0\0\80\98~Ì:\0\0\0\0\0\0\0\80°äú:\0\0\0\0\0\0\0\80\0þ@8\0\0\0\0\0\0\0\80\0£Õ7\0\0\0\0\0\0\0\80Xv\ e:\0\0\0\0\0\0\0\80ð~&:\0\0\0\0\0\0\0\80\ 6)G;\0\0\0\0\0\0\0\80Χ\10;\0\0\0\0\0\0\0\80\82\1f_;\0\0\0\0\0\0\0\80÷|\95;\0\0\0\0\0\0\0\80`~\1e9\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\b\968\0\0\0\0\0\0\0\80\98~Ì:\0\0\0\0\0\0\0\80\96:\0\0\0\0\0\0\0\80Χ\10;\0\0\0\0\0\0\0\80\ 6)G;\0\0\0\0\0\0\0\80\90\96#9\0\0\0\0\0\0\0\80À\94^8\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80`~\1e9\0\0\0\0\0\0\0\80Xv\ e:\0\0\0\0\0\0\0\80\90\16¬9\0\0\0\0\0\0\0\80\96:\0\0\0\0\0\0\0\80\98~Ì:\0\0\0\0\0\0\0\80\0£Õ7\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\90\16¬9\0\0\0\0\0\0\0\80Xv\ e:\0\0\0\0\0\0\0\80²\8f·;À÷\ e¹\0\0\0\80Ö\86\91;Àph¹\0\0\0\80ã\9e\8a;,JÕ¹\0\0\0\80\8c®\9f;$á³¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0£Õ7\0\0\0\0\0\0\0\80üa½;\0¤\83·\0\0\0\80\90;@\ 5\8b¸\0\0\0\80Ö\86\91;Àph¹\0\0\0\80²\8f·;À÷\ e¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\97X´;\0\0\0\0\0\0\0\80[\9b\8b;\0\0 ³\0\0\0\80\90;@\ 5\8b¸\0\0\0\80üa½;\0¤\83·\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80÷|\95;\0\0\0\0\0\0\0\80\82\1f_;\0\0\0\0\0\0\0\80[\9b\8b;\0\0 ³\0\0\0\80\97X´;\0\0\0\0\0\0\0\80\0\b\968\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\90;@\ 5\8b¸\0\0\0\80ÅYK;àÚ\13¹\0\0\0\806\V;àù¦¹\0\0\0\80Ö\86\91;Àph¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80[\9b\8b;\0\0 ³\0\0\0\80V8?;\0\1eµ·\0\0\0\80ÅYK;àÚ\13¹\0\0\0\80\90;@\ 5\8b¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\82\1f_;\0\0\0\0\0\0\0\80\82[\14;\0\0\0\0\0\0\0\80V8?;\0\1eµ·\0\0\0\80[\9b\8b;\0\0 ³\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Χ\10;\0\0\0\0\0\0\0\80dÙ¿:\0\0\0\0\0\0\0\80\82[\14;\0\0\0\0\0\0\0\80\82\1f_;\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\96:\0\0\0\0\0\0\0\80tÂ):\0\0\0\0\0\0\0\80dÙ¿:\0\0\0\0\0\0\0\80Χ\10;\0\0\0\0\0\0\0\80À\94^8\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\90\16¬9\0\0\0\0\0\0\0\80àKÑ8\0\0\0\0\0\0\0\80tÂ):\0\0\0\0\0\0\0\80\96:\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80àKÑ8\0\0\0\0\0\0\0\80\90\16¬9\0\0\0\0\0\0\0\80Ö\86\91;Àph¹\0\0\0\806\V;àù¦¹\0\0\0\80~Rj;ø­ø¹\0\0\0\80ã\9e\8a;,JÕ¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80tÂ):\0\0\0\0\0\0\0\80P\a^9\0\0\0\0\0\0\0\80&
+E:\0\0\0\0\0\0\0\80dÙ¿:\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80àKÑ8\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80P\a^9\0\0\0\0\0\0\0\80tÂ):\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80àKÑ8\0\0\0\0\0\0\0\806\V;àù¦¹\0\0\0\80àô\13;ÀèÕ¹\0\0\0\80©ïA;$\86\rº\0\0\0\80~Rj;ø­ø¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80ÅYK;àÚ\13¹\0\0\0\80Þä\ 6;`òb¹\0\0\0\80àô\13;ÀèÕ¹\0\0\0\806\V;àù¦¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80V8?;\0\1eµ·\0\0\0\80:¸õ:\80\1a\89¸\0\0\0\80Þä\ 6;`òb¹\0\0\0\80ÅYK;àÚ\13¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\82[\14;\0\0\0\0\0\0\0\80[íµ:\0\80©µ\0\0\0\80:¸õ:\80\1a\89¸\0\0\0\80V8?;\0\1eµ·\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80dÙ¿:\0\0\0\0\0\0\0\80&
+E:\0\0\0\0\0\0\0\80[íµ:\0\80©µ\0\0\0\80\82[\14;\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80:¸õ:\80\1a\89¸\0\0\0\80\0\0\0\0À°ß¸\0\0\0\80\0\0\0\0 \9a\98¹\0\0\0\80Þä\ 6;`òb¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80[íµ:\0\80©µ\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0À°ß¸\0\0\0\80:¸õ:\80\1a\89¸\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80&
+E:\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80[íµ:\0\80©µ\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80P\a^9\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80&
+E:\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80P\a^9\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80àô\13;ÀèÕ¹\0\0\0\80\0\0\0\0\0à\ 4º\0\0\0\80\0\0\0\0Z\94\0\0\0\80©ïA;$\86\rº\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80Þä\ 6;`òb¹\0\0\0\80\0\0\0\0 \9a\98¹\0\0\0\80\0\0\0\0\0à\ 4º\0\0\0\80àô\13;ÀèÕ¹\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\0\0\0\0\0\0\0\0\0\0\0\80\ 2\0\ 1\0\0\0\ 3\0\ 2\0\0\0\ 6\0\ 5\0\ 4\0\a\0\ 6\0\ 4\0
+\0      \0\b\0\v\0
+\0\b\0\ e\0\r\0\f\0\ f\0\ e\0\f\0\12\0\11\0\10\0\13\0\12\0\10\0\16\0\15\0\14\0\17\0\16\0\14\0\1a\0\19\0\18\0\e\0\1a\0\18\0\1e\0\1d\0\1c\0\1f\0\1e\0\1c\0"\0!\0 \0#\0"\0 \0&\0%\0$\0'\0&\0$\0*\0)\0(\0+\0*\0(\0.\0-\0,\0.\0/\0-\0.\00\0/\0.\0,\01\0.\01\02\04\03\00\04\05\03\04\06\05\08\07\06\08\09\07\08\0:\09\0<\0;\0:\0<\0=\0;\0<\0>\0=\0@\0?\0>\0@\0A\0?\0@\0B\0A\0D\0C\0B\0D\0E\0C\0D\0F\0E\0H\0G\0F\0H\0I\0G\0H\0J\0I\0L\0K\0J\0L\0M\0K\0L\0N\0M\0P\0O\0N\0P\0Q\0O\0P\0R\0Q\0T\0S\0R\0T\0U\0S\0T\02\0U\0X\0W\0V\0W\0Y\0V\0Y\0Z\0V\0[\0X\0V\0\\0[\0V\0Z\0^\0]\0^\0_\0]\0_\0`\0]\0`\0b\0a\0b\0c\0a\0c\0d\0a\0d\0f\0e\0f\0g\0e\0g\0h\0e\0h\0j\0i\0j\0k\0i\0k\0l\0i\0l\0n\0m\0n\0o\0m\0o\0p\0m\0p\0r\0q\0r\0s\0q\0s\0t\0q\0t\0v\0u\0v\0w\0u\0w\0x\0u\0x\0z\0y\0z\0{\0y\0{\0|\0y\0|\0~\0}\0~\0\7f\0}\0\7f\0\\0}\0\82\0\81\0\80\0\83\0\82\0\80\0\86\0\85\0\84\0\87\0\86\0\84\0\8a\0\89\0\88\0\8b\0\8a\0\88\0\8e\0\8d\0\8c\0\8f\0\8e\0\8c\0\92\0\91\0\90\0\93\0\92\0\90\0\96\0\95\0\94\0\97\0\96\0\94\0\9a\0\99\0\98\0\9b\0\9a\0\98\0\9e\0\9d\0\9c\0\9f\0\9e\0\9c\0¢\0¡\0 \0£\0¢\0 \0¦\0¥\0¤\0§\0¦\0¤\0ª\0©\0¨\0«\0ª\0¨\0®\0­\0¬\0¯\0®\0¬\0²\0±\0°\0³\0²\0°\0\0µ\0´\0·\0\0´\0º\0¹\0¸\0»\0º\0¸\0¾\0½\0¼\0¿\0¾\0¼\0Â\0Á\0À\0Ã\0Â\0À\0Æ\0Å\0Ä\0Ç\0Æ\0Ä\0Ê\0É\0È\0Ë\0Ê\0È\0Î\0Í\0Ì\0Ï\0Î\0Ì\0Ò\0Ñ\0Ð\0Ó\0Ò\0Ð\0Ö\0Õ\0Ô\0×\0Ö\0Ô\0Ú\0Ù\0Ø\0Û\0Ú\0Ø\0Þ\0Ý\0Ü\0ß\0Þ\0Ü\0â\0á\0à\0ã\0â\0à\0æ\0å\0ä\0ç\0æ\0ä\0ê\0é\0è\0ë\0ê\0è\0î\0í\0ì\0ï\0î\0ì\0ò\0ñ\0ð\0ó\0ò\0ð\0ö\0õ\0ô\0÷\0ö\0ô\0ú\0ù\0ø\0û\0ú\0ø\0þ\0ý\0ü\0ÿ\0þ\0ü\0\ 2\ 1\ 1\ 1\0\ 1\ 3\ 1\ 2\ 1\0\ 1\ 6\ 1\ 5\ 1\ 4\ 1\a\ 1\ 6\ 1\ 4\ 1
+\ 1      \ 1\b\ 1\v\ 1
+\ 1\b\ 1\ e\ 1\r\ 1\f\ 1\ f\ 1\ e\ 1\f\ 1\12\ 1\11\ 1\10\ 1\13\ 1\12\ 1\10\ 1\16\ 1\15\ 1\14\ 1\17\ 1\16\ 1\14\ 1\1a\ 1\19\ 1\18\ 1\e\ 1\1a\ 1\18\ 1\1e\ 1\1d\ 1\1c\ 1\1f\ 1\1e\ 1\1c\ 1"\ 1!\ 1 \ 1#\ 1"\ 1 \ 1&\ 1%\ 1$\ 1'\ 1&\ 1$\ 1*\ 1)\ 1(\ 1+\ 1*\ 1(\ 1.\ 1-\ 1,\ 1/\ 1.\ 1,\ 12\ 11\ 10\ 13\ 12\ 10\ 16\ 15\ 14\ 17\ 16\ 14\ 1:\ 19\ 18\ 1;\ 1:\ 18\ 1>\ 1=\ 1<\ 1?\ 1>\ 1<\ 1B\ 1A\ 1@\ 1C\ 1B\ 1@\ 1F\ 1E\ 1D\ 1G\ 1F\ 1D\ 1J\ 1I\ 1H\ 1K\ 1J\ 1H\ 1N\ 1M\ 1L\ 1O\ 1N\ 1L\ 1R\ 1Q\ 1P\ 1S\ 1R\ 1P\ 1V\ 1U\ 1T\ 1W\ 1V\ 1T\ 1Z\ 1Y\ 1X\ 1[\ 1Z\ 1X\ 1^\ 1]\ 1\\ 1_\ 1^\ 1\\ 1b\ 1a\ 1`\ 1c\ 1b\ 1`\ 1f\ 1e\ 1d\ 1g\ 1f\ 1d\ 1j\ 1i\ 1h\ 1k\ 1j\ 1h\ 1n\ 1m\ 1l\ 1o\ 1n\ 1l\ 1r\ 1q\ 1p\ 1s\ 1r\ 1p\ 1v\ 1u\ 1t\ 1w\ 1v\ 1t\ 1z\ 1y\ 1x\ 1{\ 1z\ 1x\ 1~\ 1}\ 1|\ 1\7f\ 1~\ 1|\ 1\82\ 1\81\ 1\80\ 1\83\ 1\82\ 1\80\ 1\86\ 1\85\ 1\84\ 1\87\ 1\86\ 1\84\ 1\8a\ 1\89\ 1\88\ 1\8b\ 1\8a\ 1\88\ 1\8e\ 1\8d\ 1\8c\ 1\8f\ 1\8e\ 1\8c\ 1\92\ 1\91\ 1\90\ 1\93\ 1\92\ 1\90\ 1\96\ 1\95\ 1\94\ 1\97\ 1\96\ 1\94\ 1\9a\ 1\99\ 1\98\ 1\9b\ 1\9a\ 1\98\ 1\9e\ 1\9d\ 1\9c\ 1\9f\ 1\9e\ 1\9c\ 1¢\ 1¡\ 1 \ 1£\ 1¢\ 1 \ 1¦\ 1¥\ 1¤\ 1§\ 1¦\ 1¤\ 1ª\ 1©\ 1¨\ 1«\ 1ª\ 1¨\ 1®\ 1­\ 1¬\ 1¯\ 1®\ 1¬\ 1²\ 1±\ 1°\ 1³\ 1²\ 1°\ 1\ 1µ\ 1´\ 1·\ 1\ 1´\ 1º\ 1¹\ 1¸\ 1»\ 1º\ 1¸\ 1¾\ 1½\ 1¼\ 1¿\ 1¾\ 1¼\ 1Â\ 1Á\ 1À\ 1Ã\ 1Â\ 1À\ 1Æ\ 1Å\ 1Ä\ 1Ç\ 1Æ\ 1Ä\ 1Ê\ 1É\ 1È\ 1Ë\ 1Ê\ 1È\ 1Î\ 1Í\ 1Ì\ 1Ï\ 1Î\ 1Ì\ 1Ò\ 1Ñ\ 1Ð\ 1Ó\ 1Ò\ 1Ð\ 1Ö\ 1Õ\ 1Ô\ 1×\ 1Ö\ 1Ô\ 1Ú\ 1Ù\ 1Ø\ 1Û\ 1Ú\ 1Ø\ 1Þ\ 1Ý\ 1Ü\ 1ß\ 1Þ\ 1Ü\ 1â\ 1á\ 1à\ 1ã\ 1â\ 1à\ 1æ\ 1å\ 1ä\ 1ç\ 1æ\ 1ä\ 1ê\ 1é\ 1è\ 1ë\ 1ê\ 1è\ 1î\ 1í\ 1ì\ 1ï\ 1î\ 1ì\ 1ò\ 1ñ\ 1ð\ 1ó\ 1ò\ 1ð\ 1ö\ 1õ\ 1ô\ 1÷\ 1ö\ 1ô\ 1ú\ 1ù\ 1ø\ 1û\ 1ú\ 1ø\ 1þ\ 1ý\ 1ü\ 1ÿ\ 1þ\ 1ü\ 1\ 2\ 2\ 1\ 2\0\ 2\ 3\ 2\ 2\ 2\0\ 2\ 6\ 2\ 5\ 2\ 4\ 2\a\ 2\ 6\ 2\ 4\ 2
+\ 2      \ 2\b\ 2\v\ 2
+\ 2\b\ 2\ e\ 2\r\ 2\f\ 2\ f\ 2\ e\ 2\f\ 2\12\ 2\11\ 2\10\ 2\13\ 2\12\ 2\10\ 2\16\ 2\15\ 2\14\ 2\17\ 2\16\ 2\14\ 2\1a\ 2\19\ 2\18\ 2\e\ 2\1a\ 2\18\ 2\1e\ 2\1d\ 2\1c\ 2\1f\ 2\1e\ 2\1c\ 2"\ 2!\ 2 \ 2#\ 2"\ 2 \ 2&\ 2%\ 2$\ 2'\ 2&\ 2$\ 2*\ 2)\ 2(\ 2+\ 2*\ 2(\ 2.\ 2-\ 2,\ 2/\ 2.\ 2,\ 22\ 21\ 20\ 23\ 22\ 20\ 26\ 25\ 24\ 27\ 26\ 24\ 2:\ 29\ 28\ 2;\ 2:\ 28\ 2>\ 2=\ 2<\ 2?\ 2>\ 2<\ 2B\ 2A\ 2@\ 2C\ 2B\ 2@\ 2F\ 2E\ 2D\ 2G\ 2F\ 2D\ 2J\ 2I\ 2H\ 2K\ 2J\ 2H\ 2N\ 2M\ 2L\ 2O\ 2N\ 2L\ 2R\ 2Q\ 2P\ 2S\ 2R\ 2P\ 2V\ 2U\ 2T\ 2W\ 2V\ 2T\ 2Z\ 2Y\ 2X\ 2[\ 2Z\ 2X\ 2^\ 2]\ 2\\ 2_\ 2^\ 2\\ 2b\ 2a\ 2`\ 2c\ 2b\ 2`\ 2f\ 2e\ 2d\ 2g\ 2f\ 2d\ 2j\ 2i\ 2h\ 2k\ 2j\ 2h\ 2n\ 2m\ 2l\ 2o\ 2n\ 2l\ 2r\ 2q\ 2p\ 2s\ 2r\ 2p\ 2v\ 2u\ 2t\ 2w\ 2v\ 2t\ 2z\ 2y\ 2x\ 2{\ 2z\ 2x\ 2~\ 2}\ 2|\ 2\7f\ 2~\ 2|\ 2\82\ 2\81\ 2\80\ 2\83\ 2\82\ 2\80\ 2\86\ 2\85\ 2\84\ 2\87\ 2\86\ 2\84\ 2\8a\ 2\89\ 2\88\ 2\8b\ 2\8a\ 2\88\ 2\8e\ 2\8d\ 2\8c\ 2\8f\ 2\8e\ 2\8c\ 2\92\ 2\91\ 2\90\ 2\93\ 2\92\ 2\90\ 2\96\ 2\95\ 2\94\ 2\97\ 2\96\ 2\94\ 2\9a\ 2\99\ 2\98\ 2\9b\ 2\9a\ 2\98\ 2\9e\ 2\9d\ 2\9c\ 2\9f\ 2\9e\ 2\9c\ 2¢\ 2¡\ 2 \ 2£\ 2¢\ 2 \ 2¦\ 2¥\ 2¤\ 2§\ 2¦\ 2¤\ 2ª\ 2©\ 2¨\ 2«\ 2ª\ 2¨\ 2®\ 2­\ 2¬\ 2¯\ 2®\ 2¬\ 2²\ 2±\ 2°\ 2³\ 2²\ 2°\ 2\ 2µ\ 2´\ 2·\ 2\ 2´\ 2º\ 2¹\ 2¸\ 2»\ 2º\ 2¸\ 2¾\ 2½\ 2¼\ 2¿\ 2¾\ 2¼\ 2Â\ 2Á\ 2À\ 2Ã\ 2Â\ 2À\ 2Æ\ 2Å\ 2Ä\ 2Ç\ 2Æ\ 2Ä\ 2Ê\ 2É\ 2È\ 2Ë\ 2Ê\ 2È\ 2Î\ 2Í\ 2Ì\ 2Ï\ 2Î\ 2Ì\ 2Ò\ 2Ñ\ 2Ð\ 2Ó\ 2Ò\ 2Ð\ 2Ö\ 2Õ\ 2Ô\ 2×\ 2Ö\ 2Ô\ 2Ú\ 2Ù\ 2Ø\ 2Û\ 2Ú\ 2Ø\ 2Þ\ 2Ý\ 2Ü\ 2ß\ 2Þ\ 2Ü\ 2â\ 2á\ 2à\ 2ã\ 2â\ 2à\ 2æ\ 2å\ 2ä\ 2ç\ 2æ\ 2ä\ 2ê\ 2é\ 2è\ 2ë\ 2ê\ 2è\ 2î\ 2í\ 2ì\ 2ï\ 2î\ 2ì\ 2ò\ 2ñ\ 2ð\ 2ó\ 2ò\ 2ð\ 2ö\ 2õ\ 2ô\ 2÷\ 2ö\ 2ô\ 2ú\ 2ù\ 2ø\ 2û\ 2ú\ 2ø\ 2þ\ 2ý\ 2ü\ 2ÿ\ 2þ\ 2ü\ 2\ 2\ 3\ 1\ 3\0\ 3\ 3\ 3\ 2\ 3\0\ 3\ 6\ 3\ 5\ 3\ 4\ 3\a\ 3\ 6\ 3\ 4\ 3
+\ 3      \ 3\b\ 3\v\ 3
+\ 3\b\ 3\ e\ 3\r\ 3\f\ 3\ f\ 3\ e\ 3\f\ 3\12\ 3\11\ 3\10\ 3\13\ 3\12\ 3\10\ 3\16\ 3\15\ 3\14\ 3\17\ 3\16\ 3\14\ 3\1a\ 3\19\ 3\18\ 3\e\ 3\1a\ 3\18\ 3\1e\ 3\1d\ 3\1c\ 3\1f\ 3\1e\ 3\1c\ 3"\ 3!\ 3 \ 3#\ 3"\ 3 \ 3&\ 3%\ 3$\ 3'\ 3&\ 3$\ 3*\ 3)\ 3(\ 3+\ 3*\ 3(\ 3.\ 3-\ 3,\ 3/\ 3.\ 3,\ 32\ 31\ 30\ 33\ 32\ 30\ 36\ 35\ 34\ 37\ 36\ 34\ 3:\ 39\ 38\ 3;\ 3:\ 38\ 3>\ 3=\ 3<\ 3?\ 3>\ 3<\ 3B\ 3A\ 3@\ 3C\ 3B\ 3@\ 3F\ 3E\ 3D\ 3G\ 3F\ 3D\ 3J\ 3I\ 3H\ 3K\ 3J\ 3H\ 3N\ 3M\ 3L\ 3O\ 3N\ 3L\ 3R\ 3Q\ 3P\ 3S\ 3R\ 3P\ 3V\ 3U\ 3T\ 3W\ 3V\ 3T\ 3Z\ 3Y\ 3X\ 3[\ 3Z\ 3X\ 3^\ 3]\ 3\\ 3_\ 3^\ 3\\ 3b\ 3a\ 3`\ 3c\ 3b\ 3`\ 3f\ 3e\ 3d\ 3g\ 3f\ 3d\ 3j\ 3i\ 3h\ 3k\ 3j\ 3h\ 3n\ 3m\ 3l\ 3o\ 3n\ 3l\ 3r\ 3q\ 3p\ 3s\ 3r\ 3p\ 3v\ 3u\ 3t\ 3w\ 3v\ 3t\ 3z\ 3y\ 3x\ 3{\ 3z\ 3x\ 3~\ 3}\ 3|\ 3\7f\ 3~\ 3|\ 3\82\ 3\81\ 3\80\ 3\83\ 3\82\ 3\80\ 3\86\ 3\85\ 3\84\ 3\87\ 3\86\ 3\84\ 3\8a\ 3\89\ 3\88\ 3\8b\ 3\8a\ 3\88\ 3\8e\ 3\8d\ 3\8c\ 3\8f\ 3\8e\ 3\8c\ 3\92\ 3\91\ 3\90\ 3\93\ 3\92\ 3\90\ 3\96\ 3\95\ 3\94\ 3\97\ 3\96\ 3\94\ 3\9a\ 3\99\ 3\98\ 3\9b\ 3\9a\ 3\98\ 3\9e\ 3\9d\ 3\9c\ 3\9f\ 3\9e\ 3\9c\ 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µ\ 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Ë\ 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Ü\ 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õ\ 3ô\ 3÷\ 3ö\ 3ô\ 3ú\ 3ù\ 3ø\ 3û\ 3ú\ 3ø\ 3þ\ 3ý\ 3ü\ 3ÿ\ 3þ\ 3ü\ 3\ 2\ 4\ 1\ 4\0\ 4\ 3\ 4\ 2\ 4\0\ 4\ 6\ 4\ 5\ 4\ 4\ 4\a\ 4\ 6\ 4\ 4\ 4
+\ 4      \ 4\b\ 4\v\ 4
+\ 4\b\ 4\ e\ 4\r\ 4\f\ 4\ f\ 4\ e\ 4\f\ 4\12\ 4\11\ 4\10\ 4\13\ 4\12\ 4\10\ 4\16\ 4\15\ 4\14\ 4\17\ 4\16\ 4\14\ 4\1a\ 4\19\ 4\18\ 4\e\ 4\1a\ 4\18\ 4\1e\ 4\1d\ 4\1c\ 4\1f\ 4\1e\ 4\1c\ 4"\ 4!\ 4 \ 4#\ 4"\ 4 \ 4&\ 4%\ 4$\ 4'\ 4&\ 4$\ 4*\ 4)\ 4(\ 4+\ 4*\ 4(\ 4.\ 4-\ 4,\ 4/\ 4.\ 4,\ 42\ 41\ 40\ 43\ 42\ 40\ 46\ 45\ 44\ 47\ 46\ 44\ 4:\ 49\ 48\ 4;\ 4:\ 48\ 4>\ 4=\ 4<\ 4?\ 4>\ 4<\ 4B\ 4A\ 4@\ 4C\ 4B\ 4@\ 4F\ 4E\ 4D\ 4G\ 4F\ 4D\ 4J\ 4I\ 4H\ 4K\ 4J\ 4H\ 4N\ 4M\ 4L\ 4O\ 4N\ 4L\ 4R\ 4Q\ 4P\ 4S\ 4R\ 4P\ 4V\ 4U\ 4T\ 4W\ 4V\ 4T\ 4Z\ 4Y\ 4X\ 4[\ 4Z\ 4X\ 4^\ 4]\ 4\\ 4_\ 4^\ 4\\ 4b\ 4a\ 4`\ 4c\ 4b\ 4`\ 4f\ 4e\ 4d\ 4g\ 4f\ 4d\ 4j\ 4i\ 4h\ 4k\ 4j\ 4h\ 4n\ 4m\ 4l\ 4o\ 4n\ 4l\ 4r\ 4q\ 4p\ 4s\ 4r\ 4p\ 4v\ 4u\ 4t\ 4w\ 4v\ 4t\ 4z\ 4y\ 4x\ 4{\ 4z\ 4x\ 4~\ 4}\ 4|\ 4\7f\ 4~\ 4|\ 4\82\ 4\81\ 4\80\ 4\83\ 4\82\ 4\80\ 4\86\ 4\85\ 4\84\ 4\87\ 4\86\ 4\84\ 4\8a\ 4\89\ 4\88\ 4\8b\ 4\8a\ 4\88\ 4\8e\ 4\8d\ 4\8c\ 4\8f\ 4\8e\ 4\8c\ 4\92\ 4\91\ 4\90\ 4\93\ 4\92\ 4\90\ 4\96\ 4\95\ 4\94\ 4\97\ 4\96\ 4\94\ 4\9a\ 4\99\ 4\98\ 4\9b\ 4\9a\ 4\98\ 4\9e\ 4\9d\ 4\9c\ 4\9f\ 4\9e\ 4\9c\ 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Ë\ 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õ\ 4ô\ 4÷\ 4ö\ 4ô\ 4ú\ 4ù\ 4ø\ 4û\ 4ú\ 4ø\ 4þ\ 4ý\ 4ü\ 4ÿ\ 4þ\ 4ü\ 4\ 2\ 5\ 1\ 5\0\ 5\ 3\ 5\ 2\ 5\0\ 5\ 6\ 5\ 5\ 5\ 4\ 5\a\ 5\ 6\ 5\ 4\ 5
+\ 5      \ 5\b\ 5\v\ 5
+\ 5\b\ 5\ e\ 5\r\ 5\f\ 5\ f\ 5\ e\ 5\f\ 5\12\ 5\11\ 5\10\ 5\13\ 5\12\ 5\10\ 5\16\ 5\15\ 5\14\ 5\17\ 5\16\ 5\14\ 5\1a\ 5\19\ 5\18\ 5\e\ 5\1a\ 5\18\ 5\1e\ 5\1d\ 5\1c\ 5\1f\ 5\1e\ 5\1c\ 5"\ 5!\ 5 \ 5#\ 5"\ 5 \ 5&\ 5%\ 5$\ 5'\ 5&\ 5$\ 5*\ 5)\ 5(\ 5+\ 5*\ 5(\ 5.\ 5-\ 5,\ 5/\ 5.\ 5,\ 52\ 51\ 50\ 53\ 52\ 50\ 56\ 55\ 54\ 57\ 56\ 54\ 5:\ 59\ 58\ 5;\ 5:\ 58\ 5>\ 5=\ 5<\ 5?\ 5>\ 5<\ 5B\ 5A\ 5@\ 5C\ 5B\ 5@\ 5F\ 5E\ 5D\ 5G\ 5F\ 5D\ 5J\ 5I\ 5H\ 5K\ 5J\ 5H\ 5N\ 5M\ 5L\ 5O\ 5N\ 5L\ 5R\ 5Q\ 5P\ 5S\ 5R\ 5P\ 5V\ 5U\ 5T\ 5W\ 5V\ 5T\ 5Z\ 5Y\ 5X\ 5[\ 5Z\ 5X\ 5^\ 5]\ 5\\ 5_\ 5^\ 5\\ 5b\ 5a\ 5`\ 5c\ 5b\ 5`\ 5f\ 5e\ 5d\ 5g\ 5f\ 5d\ 5j\ 5i\ 5h\ 5k\ 5j\ 5h\ 5n\ 5m\ 5l\ 5o\ 5n\ 5l\ 5r\ 5q\ 5p\ 5s\ 5r\ 5p\ 5v\ 5u\ 5t\ 5w\ 5v\ 5t\ 5z\ 5y\ 5x\ 5{\ 5z\ 5x\ 5~\ 5}\ 5|\ 5\7f\ 5~\ 5|\ 5\82\ 5\81\ 5\80\ 5\83\ 5\82\ 5\80\ 5\86\ 5\85\ 5\84\ 5\87\ 5\86\ 5\84\ 5\8a\ 5\89\ 5\88\ 5\8b\ 5\8a\ 5\88\ 5\8e\ 5\8d\ 5\8c\ 5\8f\ 5\8e\ 5\8c\ 5\92\ 5\91\ 5\90\ 5\93\ 5\92\ 5\90\ 5\96\ 5\95\ 5\94\ 5\97\ 5\96\ 5\94\ 5\9a\ 5\99\ 5\98\ 5\9b\ 5\9a\ 5\98\ 5\9e\ 5\9d\ 5\9c\ 5\9f\ 5\9e\ 5\9c\ 5¢\ 5¡\ 5 \ 5£\ 5¢\ 5 \ 5¦\ 5¥\ 5¤\ 5§\ 5¦\ 5¤\ 5ª\ 5©\ 5¨\ 5«\ 5ª\ 5¨\ 5®\ 5­\ 5¬\ 5¯\ 5®\ 5¬\ 5²\ 5±\ 5°\ 5³\ 5²\ 5°\ 5\ 5µ\ 5´\ 5·\ 5\ 5´\ 5º\ 5¹\ 5¸\ 5»\ 5º\ 5¸\ 5¾\ 5½\ 5¼\ 5¿\ 5¾\ 5¼\ 5Â\ 5Á\ 5À\ 5Ã\ 5Â\ 5À\ 5Æ\ 5Å\ 5Ä\ 5Ç\ 5Æ\ 5Ä\ 5Ê\ 5É\ 5È\ 5Ë\ 5Ê\ 5È\ 5Î\ 5Í\ 5Ì\ 5Ï\ 5Î\ 5Ì\ 5Ò\ 5Ñ\ 5Ð\ 5Ó\ 5Ò\ 5Ð\ 5Ö\ 5Õ\ 5Ô\ 5×\ 5Ö\ 5Ô\ 5Ú\ 5Ù\ 5Ø\ 5Û\ 5Ú\ 5Ø\ 5Þ\ 5Ý\ 5Ü\ 5ß\ 5Þ\ 5Ü\ 5â\ 5á\ 5à\ 5ã\ 5â\ 5à\ 5æ\ 5å\ 5ä\ 5ç\ 5æ\ 5ä\ 5ê\ 5é\ 5è\ 5ë\ 5ê\ 5è\ 5î\ 5í\ 5ì\ 5ï\ 5î\ 5ì\ 5ò\ 5ñ\ 5ð\ 5ó\ 5ò\ 5ð\ 5ö\ 5õ\ 5ô\ 5÷\ 5ö\ 5ô\ 5ú\ 5ù\ 5ø\ 5û\ 5ú\ 5ø\ 5þ\ 5ý\ 5ü\ 5ÿ\ 5þ\ 5ü\ 5\ 2\ 6\ 1\ 6\0\ 6\ 3\ 6\ 2\ 6\0\ 6\ 6\ 6\ 5\ 6\ 4\ 6\a\ 6\ 6\ 6\ 4\ 6
+\ 6      \ 6\b\ 6\v\ 6
+\ 6\b\ 6\ e\ 6\r\ 6\f\ 6\ f\ 6\ e\ 6\f\ 6\12\ 6\11\ 6\10\ 6\13\ 6\12\ 6\10\ 6\16\ 6\15\ 6\14\ 6\17\ 6\16\ 6\14\ 6\1a\ 6\19\ 6\18\ 6\e\ 6\1a\ 6\18\ 6\1e\ 6\1d\ 6\1c\ 6\1f\ 6\1e\ 6\1c\ 6"\ 6!\ 6 \ 6#\ 6"\ 6 \ 6&\ 6%\ 6$\ 6'\ 6&\ 6$\ 6*\ 6)\ 6(\ 6+\ 6*\ 6(\ 6.\ 6-\ 6,\ 6/\ 6.\ 6,\ 62\ 61\ 60\ 63\ 62\ 60\ 66\ 65\ 64\ 67\ 66\ 64\ 6:\ 69\ 68\ 6;\ 6:\ 68\ 6>\ 6=\ 6<\ 6?\ 6>\ 6<\ 6B\ 6A\ 6@\ 6C\ 6B\ 6@\ 6F\ 6E\ 6D\ 6G\ 6F\ 6D\ 6J\ 6I\ 6H\ 6K\ 6J\ 6H\ 6N\ 6M\ 6L\ 6O\ 6N\ 6L\ 6R\ 6Q\ 6P\ 6S\ 6R\ 6P\ 6V\ 6U\ 6T\ 6W\ 6V\ 6T\ 6Z\ 6Y\ 6X\ 6[\ 6Z\ 6X\ 6^\ 6]\ 6\\ 6_\ 6^\ 6\\ 6b\ 6a\ 6`\ 6c\ 6b\ 6`\ 6f\ 6e\ 6d\ 6g\ 6f\ 6d\ 6j\ 6i\ 6h\ 6k\ 6j\ 6h\ 6n\ 6m\ 6l\ 6o\ 6n\ 6l\ 6r\ 6q\ 6p\ 6s\ 6r\ 6p\ 6v\ 6u\ 6t\ 6w\ 6v\ 6t\ 6z\ 6y\ 6x\ 6{\ 6z\ 6x\ 6~\ 6}\ 6|\ 6\7f\ 6~\ 6|\ 6\82\ 6\81\ 6\80\ 6\83\ 6\82\ 6\80\ 6\86\ 6\85\ 6\84\ 6\87\ 6\86\ 6\84\ 6\8a\ 6\89\ 6\88\ 6\8b\ 6\8a\ 6\88\ 6\8e\ 6\8d\ 6\8c\ 6\8f\ 6\8e\ 6\8c\ 6\92\ 6\91\ 6\90\ 6\93\ 6\92\ 6\90\ 6\96\ 6\95\ 6\94\ 6\97\ 6\96\ 6\94\ 6\9a\ 6\99\ 6\98\ 6\9b\ 6\9a\ 6\98\ 6\9e\ 6\9d\ 6\9c\ 6\9f\ 6\9e\ 6\9c\ 6¢\ 6¡\ 6 \ 6£\ 6¢\ 6 \ 6¦\ 6¥\ 6¤\ 6§\ 6¦\ 6¤\ 6ª\ 6©\ 6¨\ 6«\ 6ª\ 6¨\ 6®\ 6­\ 6¬\ 6¯\ 6®\ 6¬\ 6²\ 6±\ 6°\ 6³\ 6²\ 6°\ 6\ 6µ\ 6´\ 6·\ 6\ 6´\ 6º\ 6¹\ 6¸\ 6»\ 6º\ 6¸\ 6¾\ 6½\ 6¼\ 6¿\ 6¾\ 6¼\ 6Â\ 6Á\ 6À\ 6Ã\ 6Â\ 6À\ 6Æ\ 6Å\ 6Ä\ 6Ç\ 6Æ\ 6Ä\ 6Ê\ 6É\ 6È\ 6Ë\ 6Ê\ 6È\ 6Î\ 6Í\ 6Ì\ 6Ï\ 6Î\ 6Ì\ 6Ò\ 6Ñ\ 6Ð\ 6Ó\ 6Ò\ 6Ð\ 6Ö\ 6Õ\ 6Ô\ 6×\ 6Ö\ 6Ô\ 6Ú\ 6Ù\ 6Ø\ 6Û\ 6Ú\ 6Ø\ 6Þ\ 6Ý\ 6Ü\ 6ß\ 6Þ\ 6Ü\ 6â\ 6á\ 6à\ 6ã\ 6â\ 6à\ 6æ\ 6å\ 6ä\ 6ç\ 6æ\ 6ä\ 6ê\ 6é\ 6è\ 6ë\ 6ê\ 6è\ 6î\ 6í\ 6ì\ 6ï\ 6î\ 6ì\ 6ò\ 6ñ\ 6ð\ 6ó\ 6ò\ 6ð\ 6ö\ 6õ\ 6ô\ 6÷\ 6ö\ 6ô\ 6ú\ 6ù\ 6ø\ 6û\ 6ú\ 6ø\ 6þ\ 6ý\ 6ü\ 6ÿ\ 6þ\ 6ü\ 6\ 2\a\ 1\a\0\a\ 3\a\ 2\a\0\a\ 6\a\ 5\a\ 4\a\a\a\ 6\a\ 4\a
+\a      \a\b\a\v\a
+\a\b\a\ e\a\r\a\f\a\ f\a\ e\a\f\a\12\a\11\a\10\a\13\a\12\a\10\a\16\a\15\a\14\a\17\a\16\a\14\a\1a\a\19\a\18\a\e\a\1a\a\18\a\1e\a\1d\a\1c\a\1f\a\1e\a\1c\a"\a!\a \a#\a"\a \a&\a%\a$\a'\a&\a$\a*\a)\a(\a+\a*\a(\a.\a-\a,\a/\a.\a,\a2\a1\a0\a3\a2\a0\a6\a5\a4\a7\a6\a4\a:\a9\a8\a;\a:\a8\a>\a=\a<\a?\a>\a<\aB\aA\a@\aC\aB\a@\aF\aE\aD\aG\aF\aD\aJ\aI\aH\aK\aJ\aH\aN\aM\aL\aO\aN\aL\aR\aQ\aP\aS\aR\aP\a\0\0\0\0\89\88\b=\89\88\88=ÎÌÌ=\89\88\b>«ª*>ÍÌL>ïîn>\89\88\88>\9a\99\99>«ªª>¼»»>ÍÌÌ>ÞÝÝ>ïîî>\0\0\0?\89\88\b?\12\11\11?\9b\99\19?$""?­ª*?633?¿»;?HDD?ÑÌL?ZUU?ãÝ]?lff?õîn?~ww?\ 3\0\80?GD\84?\8b\88\88?ÏÌ\8c?\13\11\91?WU\95?\9b\99\99?ßÝ\9d?#"¢?gf¦?«ªª?ïî®?33³?ww·?»»»?ÿÿ¿?CDÄ?\87\88È?ËÌÌ?\ f\11Ñ?SUÕ?\97\99Ù?ÛÝÝ?\1f"â?cfæ?§ªê?ëîî?/3ó?sw÷?·»û?ûÿÿ? "\ 2@BD\ 4@df\ 6@\86\88\b@¨ª
+@ÊÌ\f@ìî\ e@\ e\11\11@03\13@RU\15@tw\17@\96\99\19@¸»\e@ÚÝ\1d@üÿ\1f@\1e""@@D$@bf&@\84\88(@¦ª*@ÈÌ,@êî.@\f\111@.33@PU5@rw7@\94\999@¶»;@ØÝ=@úÿ?@\1c"B@>DD@`fF@\82\88H@¤ªJ@ÆÌL@èîN@
+\11Q@,3S@NUU@pwW@\92\99Y@´»[@ÖÝ]@øÿ_@\1a"b@<Dd@^ff@\80\88h@¢ªj@ÄÌl@æîn@\b\11q@*3s@LUu@nww@\90\99y@²»{@ÔÝ}@öÿ\7f@\f\11\81@\1d"\82@.3\83@?D\84@PU\85@af\86@rw\87@\83\88\88@\94\99\89@¥ª\8a@¶»\8b@ÇÌ\8c@ØÝ\8d@éî\8e@úÿ\8f@\v\11\91@\1c"\92@-3\93@>D\94@OU\95@`f\96@qw\97@\82\88\98@\93\99\99@¤ª\9a@µ»\9b@ÆÌ\9c@×Ý\9d@èî\9e@ùÿ\9f@
+\11¡@\e"¢@,3£@=D¤@NU¥@_f¦@pw§@\81\88¨@\92\99©@£ªª@´»«@Å̬@ÖÝ­@çî®@øÿ¯@    \11±@\1a"²@+3³@<D´@MUµ@^f¶@ow·@\80\88¸@\91\99¹@¢ªº@³»»@Ä̼@Õݽ@æî¾@÷ÿ¿@\b\11Á@\19"Â@*3Ã@;DÄ@LUÅ@]fÆ@nwÇ@\7f\88È@\90\99É@¡ªÊ@²»Ë@ÃÌÌ@ÔÝÍ@åîÎ@öÿÏ@\a\11Ñ@\18"Ò@)3Ó@:DÔ@KUÕ@\fÖ@mw×@~\88Ø@\8f\99Ù@ ªÚ@±»Û@ÂÌÜ@ÓÝÝ@äîÞ@õÿß@\ 6\11á@\17"â@(3ã@9Dä@JUå@[fæ@\0\0\0\0\0\0\0\0\0\0\0\0½\1ak:\0\0\0\0¼Xh;\0\0\0\0Ê$\ 1<\0\0\0\0»Ôb<\0\0\0\0¡\ e¯<\0\0\0\0Ñúø<\0\0\0\0EU'=\0\0\0\0ºÌW=\0\0\0\0ÈÐ\86=\0\0\0\0ÏH¤=\0\0\0\0W-Ä=\0\0\0\0L]æ=\0\0\0\0È[\ 5>\0\0\0\0\88\8d\18>\0\0\0\0Z³,>\0\0\0\0²¼A>\0\0\0\0\ 3\99W>\0\0\0\0Á7n>\0\0\0\0\82>\0\0\0\0\8e>\0\0\0\0\8bþ\9a>\0\0\0\0\b\80§>\0\0\0\0_9´>\0\0\0\0F"Á>\0\0\0\0}2Î>\0\0\0\0ºaÛ>\0\0\0\0¶§è>\0\0\0\0-üõ>\0\0\0\0\ 1?\0\0\0\0¸W\b?\0\0\0\0Ùþ\ e?\0\0\0\0ª\9c\15?\0\0\0\0
+-\1c?\0\0\0\0Õ«"?\0\0\0\0è\14)?\0\0\0\0 d/?\0\0\0\0\\955?\0\0\0\0v¤;?\0\0\0\0M\8dA?\0\0\0\0»KG?\0\0\0\0¡ÛL?\0\0\0\0Ú8R?\0\0\0\0D_W?\0\0\0\0ºJ\?\0\0\0\0\e÷`?\0\0\0\0C`e?\0\0\0\0\ e\82i?\0\0\0\0[Xm?\0\0\0\0\bßp?\0\0\0\0î\11t?\0\0\0\0íìv?\0\0\0\0áky?\0\0\0\0¦\8a{?\0\0\0\0\1cE}?\0\0\0\0\1f\97~?\0\0\0\0\8a|\7f?\0\0\0\0\7f?\12þ\169\91ö\7f?óJ¨:Û«\7f?
+\8cg;t\18\7f?3Äà;w>~?±ÿ7<\ 1 }?}\1a\88<,¿{?\8f=¼<\13\1ez?£%ø<Ó>x?¤Ç\1d=\85#v?\8a\eC=HÎs?Ïìk=3Aq?Ý\f\8c=d~n?LÀ£=÷\87k?Øÿ¼=\ 5`h?¥º×=¬\be?Úßó=\ 5\84a?O¯\b>-Ô]?\a\13\18>?ûY?ª\12(>VûU?Í¥8>\8dÖQ?ýÃI>\0\8fM?Ñd[>Ì&I?Ú\7fm>
+ D?T\ 6\80>Öü??h\81\89>L?;?ó,\93>\86i6?¼\ 4\9d>¡}1?\8f\ 4§>¸},?3(±>æk'?rk»>GJ"?\13ÊÅ>ö\1a\1d?ä?Ð>\ eà\17?¬ÈÚ>ª\9b\12?2`å>çO\r?A\ 2ð>ßþ\a?£ªú>¯ª\ 2?\8fª\ 2?áªú>Àþ\a?\80\ 2ð>ÈO\r?q`å>\8c\9b\12?èÈÚ>ïß\17?!@Ð>Ø\1a\1d?QÊÅ>*J"?­k»>Ék'?n(±>\9b},?Ê\ 4§>\84}1?ø\ 4\9d>ji6?,-\93>/?;?¡\81\89>ºü??\8d\ 6\80\9fD?C\80m>²&I?8e[>è\8eM?aÄI>tÖQ?.¦8>=ûU?
+\13(>(ûY?a\13\18>\17Ô]?¤¯\b\83a?{àó=\97\be?H»×=ó_h?f\0½=æ\87k?ÍÀ£=T~n?\\r\8c=$Aq?Ãík=:Îs?f\1cC={#v?RÈ\1d=Ê>x?¸&ø<
+\1ez?¸>¼<%¿{?\\e\88\1f}?\9a\ 18<s>~?fÆà;q\18\7f?\\8fg;Û«\7f?=J¨:\91ö\7f?Ãõ\169vù\7f?\0\0\0\0\91Å\7f?\0\0\0\0ò^\7f?\0\0\0\0ÏÆ~?\0\0\0\0_þ}?\0\0\0\0Ú\ 6}?\0\0\0\0tá{?\0\0\0\0i\8fz?\0\0\0\0ì\11y?\0\0\0\06jw?\0\0\0\0}\99u?\0\0\0\0ø s?\0\0\0\0à\81q?\0\0\0\0j=o?\0\0\0\0ÎÔl?\0\0\0\0CIj?\0\0\0\0ÿ\9bg?\0\0\0\0;Îd?\0\0\0\0,áa?\0\0\0\0
+Ö^?\0\0\0\0\ e®[?\0\0\0\0ljX?\0\0\0\0\\fU?\0\0\0\0\16\95Q?\0\0\0\0Ï\ 5N?\0\0\0\0Á_J?\0\0\0\0!¤F?\0\0\0\0(ÔB?\0\0\0\0
+ñ>?\0\0\0\0\0ü:?\0\0\0\0Aö6?\0\0\0\0\ 4á2?\0\0\0\0\80½.?\0\0\0\0ì\8c*?\0\0\0\0\7fP&?\0\0\0\0o       "?\0\0\0\0ö¸\1d?\0\0\0\0H`\19?\0\0\0\0\9d\0\15?\0\0\0\0.\9b\10?\0\0\0\001\f?\0\0\0\0ÚÃ\a?\0\0\0\0dT\ 3?\0\0\0\0
+Èý>\0\0\0\0èçô>\0\0\0\0Ì
+ì>\0\0\0\0,3ã>\0\0\0\0ncÚ>\0\0\0\0\ 5\9eÑ>\0\0\0\0]åÈ>\0\0\0\0é;À>\0\0\0\0\12¤·>\0\0\0\0H ¯>\0\0\0\0ö²¦>\0\0\0\0\92^\9e>\0\0\0\0}%\96>\0\0\0\03
+\8e>\0\0\0\0\1a\ f\86>\0\0\0\0Hm|>\0\0\0\0v\ 6m>\0\0\0\0\9fî]>\0\0\0\0\9f*O>\0\0\0\0\¿@>\0\0\0\0\9f±2>\0\0\0\0R\ 6%>\0\0\0\0\17>\0\0\0\0
+>\0\0\0\0\14\aý=\0\0\0\0\14%å=\0\0\0\0\858Î=\0\0\0\0\1fK¸=\0\0\0\0\9af£=\0\0\0\0¸\94\8f=\0\0\0\0)¾y=\0\0\0\0H\9fV=\0\0\0\0×ß5=\0\0\0\0H\93\17=\0\0\0\0f\9a÷<\0\0\0\0ìAÅ<\0\0\0\0\0D\98<\0\0\0\0®\8fa<\0\0\0\0\1d<\0\0\0\0¸¾Ë;\0\0\0\0=
+g;\0\0\0\0áúÎ:\0\0\0\0\9a\99Ð9\0\0\0\0
+×#´\0\0\0\0
\ No newline at end of file
diff --git a/automated-tests/resources/AnimatedMorphSphere.gltf b/automated-tests/resources/AnimatedMorphSphere.gltf
new file mode 100644 (file)
index 0000000..95c2751
--- /dev/null
@@ -0,0 +1,276 @@
+{\r
+  "accessors": [\r
+    {\r
+      "bufferView": 0,\r
+      "componentType": 5126,\r
+      "count": 1876,\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 1,\r
+      "componentType": 5126,\r
+      "count": 1876,\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 2,\r
+      "componentType": 5126,\r
+      "count": 1876,\r
+      "type": "VEC3",\r
+      "max": [\r
+        0.009999998,\r
+        0.009999991,\r
+        0.01\r
+      ],\r
+      "min": [\r
+        -0.0100000026,\r
+        -0.0100000035,\r
+        -0.01\r
+      ]\r
+    },\r
+    {\r
+      "bufferView": 3,\r
+      "componentType": 5126,\r
+      "count": 1876,\r
+      "type": "VEC3",\r
+      "name": "Ship"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "componentType": 5126,\r
+      "count": 1876,\r
+      "type": "VEC3",\r
+      "max": [\r
+        0.0313265175,\r
+        0.0226246975,\r
+        0.008465132\r
+      ],\r
+      "min": [\r
+        -0.0313265137,\r
+        -0.022857653,\r
+        0.0\r
+      ],\r
+      "name": "Ship"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "componentType": 5126,\r
+      "count": 1876,\r
+      "type": "VEC3",\r
+      "name": "Ship"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "componentType": 5126,\r
+      "count": 1876,\r
+      "type": "VEC3",\r
+      "name": "Blob"\r
+    },\r
+    {\r
+      "bufferView": 7,\r
+      "componentType": 5126,\r
+      "count": 1876,\r
+      "type": "VEC3",\r
+      "max": [\r
+        0.008781092,\r
+        0.0,\r
+        0.0\r
+      ],\r
+      "min": [\r
+        -0.008781091,\r
+        -0.0007655843,\r
+        0.0\r
+      ],\r
+      "name": "Blob"\r
+    },\r
+    {\r
+      "bufferView": 8,\r
+      "componentType": 5126,\r
+      "count": 1876,\r
+      "type": "VEC3",\r
+      "name": "Blob"\r
+    },\r
+    {\r
+      "bufferView": 9,\r
+      "componentType": 5123,\r
+      "count": 2880,\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 10,\r
+      "componentType": 5126,\r
+      "count": 217,\r
+      "type": "SCALAR",\r
+      "max": [\r
+        7.19999456\r
+      ],\r
+      "min": [\r
+        0.0\r
+      ]\r
+    },\r
+    {\r
+      "bufferView": 11,\r
+      "componentType": 5126,\r
+      "count": 434,\r
+      "type": "SCALAR"\r
+    }\r
+  ],\r
+  "animations": [\r
+    {\r
+      "channels": [\r
+        {\r
+          "sampler": 0,\r
+          "target": {\r
+            "node": 0,\r
+            "path": "weights"\r
+          }\r
+        }\r
+      ],\r
+      "samplers": [\r
+        {\r
+          "input": 10,\r
+          "interpolation": "LINEAR",\r
+          "output": 11\r
+        }\r
+      ],\r
+      "name": "Globe"\r
+    }\r
+  ],\r
+  "asset": {\r
+    "generator": "glTF Tools for Unity",\r
+    "version": "2.0"\r
+  },\r
+  "bufferViews": [\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 22512\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 22512,\r
+      "byteLength": 30016\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 52528,\r
+      "byteLength": 22512\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 75040,\r
+      "byteLength": 22512\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 97552,\r
+      "byteLength": 22512\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 120064,\r
+      "byteLength": 22512\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 142576,\r
+      "byteLength": 22512\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 165088,\r
+      "byteLength": 22512\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 187600,\r
+      "byteLength": 22512\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 210112,\r
+      "byteLength": 5760\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 215872,\r
+      "byteLength": 868\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 216740,\r
+      "byteLength": 1736\r
+    }\r
+  ],\r
+  "buffers": [\r
+    {\r
+      "uri": "AnimatedMorphSphere.bin",\r
+      "byteLength": 218476\r
+    }\r
+  ],\r
+  "meshes": [\r
+    {\r
+      "primitives": [\r
+        {\r
+          "attributes": {\r
+            "NORMAL": 0,\r
+            "TANGENT": 1,\r
+            "POSITION": 2\r
+          },\r
+          "indices": 9,\r
+          "material": 0,\r
+          "targets": [\r
+            {\r
+              "NORMAL": 3,\r
+              "POSITION": 4,\r
+              "TANGENT": 5\r
+            },\r
+            {\r
+              "NORMAL": 6,\r
+              "POSITION": 7,\r
+              "TANGENT": 8\r
+            }\r
+          ]\r
+        }\r
+      ],\r
+      "weights": [\r
+        0.0,\r
+        0.0\r
+      ],\r
+      "name": "Sphere"\r
+    }\r
+  ],\r
+  "materials": [\r
+    {\r
+      "pbrMetallicRoughness": {\r
+        "metallicFactor": 0.0,\r
+        "roughnessFactor": 0.5\r
+      },\r
+      "name": "No Name"\r
+    }\r
+  ],\r
+  "nodes": [\r
+    {\r
+      "mesh": 0,\r
+      "rotation": [\r
+        0.0,\r
+        0.7071067,\r
+        -0.7071068,\r
+        0.0\r
+      ],\r
+      "scale": [\r
+        100.0,\r
+        100.0,\r
+        100.0\r
+      ],\r
+      "name": "AnimatedMorphSphere"\r
+    }\r
+  ],\r
+  "scene": 0,\r
+  "scenes": [\r
+    {\r
+      "nodes": [\r
+        0\r
+      ]\r
+    }\r
+  ]\r
+}
\ No newline at end of file
diff --git a/automated-tests/resources/AnimatedTriangle.gltf b/automated-tests/resources/AnimatedTriangle.gltf
new file mode 100644 (file)
index 0000000..4e4a9cb
--- /dev/null
@@ -0,0 +1,118 @@
+{\r
+  "scene" : 0,\r
+  "scenes" : [\r
+    {\r
+      "nodes" : [ 0 ]\r
+    }\r
+  ],\r
+  \r
+  "nodes" : [\r
+    {\r
+      "mesh" : 0,\r
+      "rotation" : [ 0.0, 0.0, 0.0, 1.0 ]\r
+    }\r
+  ],\r
+  \r
+  "meshes" : [\r
+    {\r
+      "primitives" : [ {\r
+        "attributes" : {\r
+          "POSITION" : 1\r
+        },\r
+        "indices" : 0\r
+      } ]\r
+    }\r
+  ],\r
+  \r
+  "animations": [\r
+    {\r
+      "samplers" : [\r
+        {\r
+          "input" : 2,\r
+          "interpolation" : "LINEAR",\r
+          "output" : 3\r
+        }\r
+      ],\r
+      "channels" : [ {\r
+        "sampler" : 0,\r
+        "target" : {\r
+          "node" : 0,\r
+          "path" : "rotation"\r
+        }\r
+      } ]\r
+    }\r
+  ],\r
+\r
+  "buffers" : [\r
+    {\r
+      "uri" : "simpleTriangle.bin",\r
+      "byteLength" : 44\r
+    },\r
+    {\r
+      "uri" : "animation.bin",\r
+      "byteLength" : 100\r
+    }\r
+  ],\r
+  "bufferViews" : [\r
+    {\r
+      "buffer" : 0,\r
+      "byteOffset" : 0,\r
+      "byteLength" : 6,\r
+      "target" : 34963\r
+    },\r
+    {\r
+      "buffer" : 0,\r
+      "byteOffset" : 8,\r
+      "byteLength" : 36,\r
+      "target" : 34962\r
+    },\r
+    {\r
+      "buffer" : 1,\r
+      "byteOffset" : 0,\r
+      "byteLength" : 100\r
+    }\r
+  ],\r
+  "accessors" : [\r
+    {\r
+      "bufferView" : 0,\r
+      "byteOffset" : 0,\r
+      "componentType" : 5123,\r
+      "count" : 3,\r
+      "type" : "SCALAR",\r
+      "max" : [ 2 ],\r
+      "min" : [ 0 ]\r
+    },\r
+    {\r
+      "bufferView" : 1,\r
+      "byteOffset" : 0,\r
+      "componentType" : 5126,\r
+      "count" : 3,\r
+      "type" : "VEC3",\r
+      "max" : [ 1.0, 1.0, 0.0 ],\r
+      "min" : [ 0.0, 0.0, 0.0 ]\r
+    },\r
+    {\r
+      "bufferView" : 2,\r
+      "byteOffset" : 0,\r
+      "componentType" : 5126,\r
+      "count" : 5,\r
+      "type" : "SCALAR",\r
+      "max" : [ 1.0 ],\r
+      "min" : [ 0.0 ]\r
+    },\r
+    {\r
+      "bufferView" : 2,\r
+      "byteOffset" : 20,\r
+      "componentType" : 5126,\r
+      "count" : 5,\r
+      "type" : "VEC4",\r
+      "max" : [ 0.0, 0.0, 1.0, 1.0 ],\r
+      "min" : [ 0.0, 0.0, 0.0, -0.707 ]\r
+    }\r
+  ],\r
+  \r
+  "asset" : {\r
+    "version" : "2.0"\r
+  }\r
+  \r
+}\r
diff --git a/automated-tests/resources/BoxAnimated.gltf b/automated-tests/resources/BoxAnimated.gltf
new file mode 100644 (file)
index 0000000..afa7afb
--- /dev/null
@@ -0,0 +1,327 @@
+{\r
+    "asset": {\r
+        "generator": "COLLADA2GLTF",\r
+        "version": "2.0"\r
+    },\r
+    "scene": 0,\r
+    "scenes": [\r
+        {\r
+            "nodes": [\r
+                3,\r
+                0\r
+            ]\r
+        }\r
+    ],\r
+    "nodes": [\r
+        {\r
+            "children": [\r
+                1\r
+            ],\r
+            "rotation": [\r
+                -0.0,\r
+                -0.0,\r
+                -0.0,\r
+                -1.0\r
+            ]\r
+        },\r
+        {\r
+            "children": [\r
+                2\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 0,\r
+            "rotation": [\r
+                -0.0,\r
+                -0.0,\r
+                -0.0,\r
+                -1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 1\r
+        }\r
+    ],\r
+    "meshes": [\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 1,\r
+                        "POSITION": 2\r
+                    },\r
+                    "indices": 0,\r
+                    "mode": 4,\r
+                    "material": 0\r
+                }\r
+            ],\r
+            "name": "inner_box"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 4,\r
+                        "POSITION": 5\r
+                    },\r
+                    "indices": 3,\r
+                    "mode": 4,\r
+                    "material": 1\r
+                }\r
+            ],\r
+            "name": "outer_box"\r
+        }\r
+    ],\r
+    "animations": [\r
+        {\r
+            "channels": [\r
+                {\r
+                    "sampler": 0,\r
+                    "target": {\r
+                        "node": 2,\r
+                        "path": "rotation"\r
+                    }\r
+                },\r
+                {\r
+                    "sampler": 1,\r
+                    "target": {\r
+                        "node": 0,\r
+                        "path": "translation"\r
+                    }\r
+                }\r
+            ],\r
+            "samplers": [\r
+                {\r
+                    "input": 6,\r
+                    "interpolation": "LINEAR",\r
+                    "output": 7\r
+                },\r
+                {\r
+                    "input": 8,\r
+                    "interpolation": "LINEAR",\r
+                    "output": 9\r
+                }\r
+            ]\r
+        }\r
+    ],\r
+    "accessors": [\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 0,\r
+            "componentType": 5123,\r
+            "count": 186,\r
+            "max": [\r
+                95\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 0,\r
+            "componentType": 5126,\r
+            "count": 96,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1152,\r
+            "componentType": 5126,\r
+            "count": 96,\r
+            "max": [\r
+                0.33504000306129458,\r
+                0.5,\r
+                0.33504000306129458\r
+            ],\r
+            "min": [\r
+                -0.33504000306129458,\r
+                -0.5,\r
+                -0.33504000306129458\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 372,\r
+            "componentType": 5123,\r
+            "count": 576,\r
+            "max": [\r
+                223\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 2304,\r
+            "componentType": 5126,\r
+            "count": 224,\r
+            "max": [\r
+                1.0,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 4992,\r
+            "componentType": 5126,\r
+            "count": 224,\r
+            "max": [\r
+                0.5,\r
+                0.5,\r
+                0.5\r
+            ],\r
+            "min": [\r
+                -0.5,\r
+                -0.5,\r
+                -0.5\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 2,\r
+            "byteOffset": 0,\r
+            "componentType": 5126,\r
+            "count": 2,\r
+            "max": [\r
+                2.5\r
+            ],\r
+            "min": [\r
+                1.25\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 3,\r
+            "byteOffset": 0,\r
+            "componentType": 5126,\r
+            "count": 2,\r
+            "max": [\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                4.4896593387466768e-11\r
+            ],\r
+            "min": [\r
+                -0.0,\r
+                0.0,\r
+                0.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC4"\r
+        },\r
+        {\r
+            "bufferView": 2,\r
+            "byteOffset": 8,\r
+            "componentType": 5126,\r
+            "count": 4,\r
+            "max": [\r
+                3.708329916000366\r
+            ],\r
+            "min": [\r
+                0.0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 4,\r
+            "byteOffset": 0,\r
+            "componentType": 5126,\r
+            "count": 4,\r
+            "max": [\r
+                0.0,\r
+                2.5199999809265138,\r
+                0.0\r
+            ],\r
+            "min": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ],\r
+            "type": "VEC3"\r
+        }\r
+    ],\r
+    "materials": [\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.800000011920929,\r
+                    0.4159420132637024,\r
+                    0.7952920198440552,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "name": "inner"\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.3016040027141571,\r
+                    0.5335419774055481,\r
+                    0.800000011920929,\r
+                    1.0\r
+                ],\r
+                "metallicFactor": 0.0\r
+            },\r
+            "name": "outer"\r
+        }\r
+    ],\r
+    "bufferViews": [\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 7784,\r
+            "byteLength": 1524,\r
+            "target": 34963\r
+        },\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 80,\r
+            "byteLength": 7680,\r
+            "byteStride": 12,\r
+            "target": 34962\r
+        },\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 7760,\r
+            "byteLength": 24\r
+        },\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 0,\r
+            "byteLength": 32\r
+        },\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 32,\r
+            "byteLength": 48\r
+        }\r
+    ],\r
+    "buffers": [\r
+        {\r
+            "byteLength": 9308,\r
+            "uri": "BoxAnimated0.bin"\r
+        }\r
+    ]\r
+}\r
diff --git a/automated-tests/resources/BoxAnimated0.bin b/automated-tests/resources/BoxAnimated0.bin
new file mode 100644 (file)
index 0000000..2e4ef6e
Binary files /dev/null and b/automated-tests/resources/BoxAnimated0.bin differ
diff --git a/automated-tests/resources/CesiumMan.gltf b/automated-tests/resources/CesiumMan.gltf
new file mode 100644 (file)
index 0000000..e9356ff
--- /dev/null
@@ -0,0 +1,2715 @@
+{\r
+  "asset": {\r
+    "generator": "COLLADA2GLTF",\r
+    "version": "2.0"\r
+  },\r
+  "scenes": [\r
+    {\r
+      "nodes": [\r
+        0\r
+      ]\r
+    }\r
+  ],\r
+  "scene": 0,\r
+  "nodes": [\r
+    {\r
+      "children": [\r
+        1\r
+      ],\r
+      "matrix": [\r
+        1,\r
+        0,\r
+        0,\r
+        0,\r
+        0,\r
+        0,\r
+        -1,\r
+        0,\r
+        0,\r
+        1,\r
+        0,\r
+        0,\r
+        0,\r
+        0,\r
+        0,\r
+        1\r
+      ],\r
+      "name": "Z_UP"\r
+    },\r
+    {\r
+      "children": [\r
+        3,\r
+        2\r
+      ],\r
+      "matrix": [\r
+        -4.371139894487897e-8,\r
+        -1,\r
+        0,\r
+        0,\r
+        1,\r
+        -4.371139894487897e-8,\r
+        0,\r
+        0,\r
+        0,\r
+        0,\r
+        1,\r
+        0,\r
+        0,\r
+        0,\r
+        0,\r
+        1\r
+      ],\r
+      "name": "Armature"\r
+    },\r
+    {\r
+      "mesh": 0,\r
+      "skin": 0,\r
+      "name": "Cesium_Man"\r
+    },\r
+    {\r
+      "children": [\r
+        12,\r
+        8,\r
+        4\r
+      ],\r
+      "translation": [\r
+        1.57554005397742e-8,\r
+        0.004999836906790733,\r
+        0.6789999008178711\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        -0.0378035344183445,\r
+        0,\r
+        -0.9992852210998536\r
+      ],\r
+      "name": "Skeleton_torso_joint_1",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        5\r
+      ],\r
+      "translation": [\r
+        0.02855719067156315,\r
+        -0.06803914159536362,\r
+        -0.06295864284038544\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        -0.6898291707038879,\r
+        0,\r
+        -0.7239722013473511\r
+      ],\r
+      "name": "leg_joint_R_1",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        6\r
+      ],\r
+      "translation": [\r
+        0.26089081168174744,\r
+        -0.009026050567626951,\r
+        0.05167089030146599\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        -0.0941137745976448,\r
+        0,\r
+        -0.9955614805221558\r
+      ],\r
+      "scale": [\r
+        1.0000001192092896,\r
+        1,\r
+        1.0000001192092896\r
+      ],\r
+      "name": "leg_joint_R_2"\r
+    },\r
+    {\r
+      "children": [\r
+        7\r
+      ],\r
+      "translation": [\r
+        0.27546030282974243,\r
+        -0.0014317259192466736,\r
+        -0.014104830101132391\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        0.8666408061981201,\r
+        0,\r
+        0.4989325702190399\r
+      ],\r
+      "name": "leg_joint_R_3",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "translation": [\r
+        -0.06681963056325912,\r
+        -0.001072264974936843,\r
+        0.026351310312747955\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        0.3269147574901581,\r
+        0,\r
+        -0.9450538158416748\r
+      ],\r
+      "name": "leg_joint_R_5",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        9\r
+      ],\r
+      "translation": [\r
+        0.028519999235868457,\r
+        0.06803944706916809,\r
+        -0.06295935809612274\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        -0.32463353872299194,\r
+        0,\r
+        -0.9458398818969728\r
+      ],\r
+      "name": "leg_joint_L_1",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        10\r
+      ],\r
+      "translation": [\r
+        0.20916390419006348,\r
+        0.009055502712726591,\r
+        -0.16426950693130493\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        -0.5294369459152222,\r
+        0,\r
+        -0.8483493328094482\r
+      ],\r
+      "scale": [\r
+        1.0000001192092896,\r
+        1,\r
+        1.0000001192092896\r
+      ],\r
+      "name": "leg_joint_L_2"\r
+    },\r
+    {\r
+      "children": [\r
+        11\r
+      ],\r
+      "translation": [\r
+        0.27579009532928467,\r
+        0.0013972519664093852,\r
+        0.004122479818761349\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        -0.8377647399902344,\r
+        0,\r
+        -0.5460314750671387\r
+      ],\r
+      "scale": [\r
+        1,\r
+        0.9999999403953552,\r
+        1\r
+      ],\r
+      "name": "leg_joint_L_3"\r
+    },\r
+    {\r
+      "translation": [\r
+        -0.06558381021022797,\r
+        0.001090653007850051,\r
+        0.02929146029055119\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        0.3130458891391754,\r
+        0,\r
+        -0.9497380256652832\r
+      ],\r
+      "name": "leg_joint_L_5",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        13\r
+      ],\r
+      "translation": [\r
+        0.0000133617004394182,\r
+        -0.000013373800356930587,\r
+        0.14541690051555634\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        -0.6573964357376099,\r
+        0,\r
+        -0.7535448670387268\r
+      ],\r
+      "name": "Skeleton_torso_joint_2",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        20,\r
+        17,\r
+        14\r
+      ],\r
+      "translation": [\r
+        -0.2505168914794922,\r
+        6.072219775887788e-7,\r
+        -0.00007290810026461259\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        0.6227028965950012,\r
+        0,\r
+        -0.7824583649635315\r
+      ],\r
+      "name": "torso_joint_3",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        15\r
+      ],\r
+      "translation": [\r
+        -0.00003830249988823198,\r
+        -0.09098774939775468,\r
+        -0.000062032304413151\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        0.9909319281578064,\r
+        0,\r
+        -0.13436488807201385\r
+      ],\r
+      "name": "Skeleton_arm_joint_R",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        16\r
+      ],\r
+      "translation": [\r
+        -0.03554634004831314,\r
+        -0.2154989987611771,\r
+        0.10423289984464645\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        0.8961479663848877,\r
+        0,\r
+        0.4437553286552429\r
+      ],\r
+      "scale": [\r
+        0.9999999403953552,\r
+        1,\r
+        0.9999999403953552\r
+      ],\r
+      "name": "Skeleton_arm_joint_R__2_"\r
+    },\r
+    {\r
+      "translation": [\r
+        0.03137021884322167,\r
+        -0.1430010050535202,\r
+        -0.11761169880628586\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        0.3792171180248261,\r
+        0,\r
+        -0.9253078103065492\r
+      ],\r
+      "scale": [\r
+        1.0000001192092896,\r
+        1,\r
+        1.0000001192092896\r
+      ],\r
+      "name": "Skeleton_arm_joint_R__3_"\r
+    },\r
+    {\r
+      "children": [\r
+        18\r
+      ],\r
+      "translation": [\r
+        -0.00003837469921563752,\r
+        0.091013602912426,\r
+        -0.00006143339851405472\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        0.9959768652915956,\r
+        0,\r
+        0.08961082249879837\r
+      ],\r
+      "name": "Skeleton_arm_joint_L__4_",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        19\r
+      ],\r
+      "translation": [\r
+        0.01322161965072155,\r
+        0.21549950540065768,\r
+        0.10933209955692293\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        -0.0711694285273552,\r
+        0,\r
+        -0.9974642395973206\r
+      ],\r
+      "name": "Skeleton_arm_joint_L__3_",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "translation": [\r
+        -0.09332461655139924,\r
+        0.1430000960826874,\r
+        0.07814791053533554\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        -0.02254222705960274,\r
+        0,\r
+        -0.9997459053993224\r
+      ],\r
+      "name": "Skeleton_arm_joint_L__2_",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        21\r
+      ],\r
+      "translation": [\r
+        -0.000002366030003031483,\r
+        0.000002413989932392724,\r
+        0.06483621150255203\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        -0.660634458065033,\r
+        0,\r
+        -0.750707745552063\r
+      ],\r
+      "name": "Skeleton_neck_joint_1",\r
+      "scale": [\r
+        1,\r
+        1,\r
+        1\r
+      ]\r
+    },\r
+    {\r
+      "translation": [\r
+        -0.05204017087817192,\r
+        -3.3993298842460724e-8,\r
+        -0.0000026607899599184748\r
+      ],\r
+      "rotation": [\r
+        0,\r
+        0.9996904730796814,\r
+        0,\r
+        0.024879230186343193\r
+      ],\r
+      "scale": [\r
+        1.0000001192092896,\r
+        1,\r
+        1.0000001192092896\r
+      ],\r
+      "name": "Skeleton_neck_joint_2"\r
+    }\r
+  ],\r
+  "meshes": [\r
+    {\r
+      "primitives": [\r
+        {\r
+          "attributes": {\r
+            "JOINTS_0": 1,\r
+            "NORMAL": 2,\r
+            "POSITION": 3,\r
+            "TEXCOORD_0": 4,\r
+            "WEIGHTS_0": 5\r
+          },\r
+          "indices": 0,\r
+          "mode": 4,\r
+          "material": 0\r
+        }\r
+      ],\r
+      "name": "Cesium_Man"\r
+    }\r
+  ],\r
+  "animations": [\r
+    {\r
+      "channels": [\r
+        {\r
+          "sampler": 0,\r
+          "target": {\r
+            "node": 3,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 1,\r
+          "target": {\r
+            "node": 3,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 2,\r
+          "target": {\r
+            "node": 3,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 3,\r
+          "target": {\r
+            "node": 12,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 4,\r
+          "target": {\r
+            "node": 12,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 5,\r
+          "target": {\r
+            "node": 12,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 6,\r
+          "target": {\r
+            "node": 13,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 7,\r
+          "target": {\r
+            "node": 13,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 8,\r
+          "target": {\r
+            "node": 13,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 9,\r
+          "target": {\r
+            "node": 20,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 10,\r
+          "target": {\r
+            "node": 20,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 11,\r
+          "target": {\r
+            "node": 20,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 12,\r
+          "target": {\r
+            "node": 21,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 13,\r
+          "target": {\r
+            "node": 21,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 14,\r
+          "target": {\r
+            "node": 21,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 15,\r
+          "target": {\r
+            "node": 17,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 16,\r
+          "target": {\r
+            "node": 17,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 17,\r
+          "target": {\r
+            "node": 17,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 18,\r
+          "target": {\r
+            "node": 18,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 19,\r
+          "target": {\r
+            "node": 18,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 20,\r
+          "target": {\r
+            "node": 18,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 21,\r
+          "target": {\r
+            "node": 19,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 22,\r
+          "target": {\r
+            "node": 19,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 23,\r
+          "target": {\r
+            "node": 19,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 24,\r
+          "target": {\r
+            "node": 14,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 25,\r
+          "target": {\r
+            "node": 14,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 26,\r
+          "target": {\r
+            "node": 14,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 27,\r
+          "target": {\r
+            "node": 15,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 28,\r
+          "target": {\r
+            "node": 15,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 29,\r
+          "target": {\r
+            "node": 15,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 30,\r
+          "target": {\r
+            "node": 16,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 31,\r
+          "target": {\r
+            "node": 16,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 32,\r
+          "target": {\r
+            "node": 16,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 33,\r
+          "target": {\r
+            "node": 8,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 34,\r
+          "target": {\r
+            "node": 8,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 35,\r
+          "target": {\r
+            "node": 8,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 36,\r
+          "target": {\r
+            "node": 9,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 37,\r
+          "target": {\r
+            "node": 9,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 38,\r
+          "target": {\r
+            "node": 9,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 39,\r
+          "target": {\r
+            "node": 10,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 40,\r
+          "target": {\r
+            "node": 10,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 41,\r
+          "target": {\r
+            "node": 10,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 42,\r
+          "target": {\r
+            "node": 11,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 43,\r
+          "target": {\r
+            "node": 11,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 44,\r
+          "target": {\r
+            "node": 11,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 45,\r
+          "target": {\r
+            "node": 4,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 46,\r
+          "target": {\r
+            "node": 4,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 47,\r
+          "target": {\r
+            "node": 4,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 48,\r
+          "target": {\r
+            "node": 5,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 49,\r
+          "target": {\r
+            "node": 5,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 50,\r
+          "target": {\r
+            "node": 5,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 51,\r
+          "target": {\r
+            "node": 6,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 52,\r
+          "target": {\r
+            "node": 6,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 53,\r
+          "target": {\r
+            "node": 6,\r
+            "path": "scale"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 54,\r
+          "target": {\r
+            "node": 7,\r
+            "path": "translation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 55,\r
+          "target": {\r
+            "node": 7,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 56,\r
+          "target": {\r
+            "node": 7,\r
+            "path": "scale"\r
+          }\r
+        }\r
+      ],\r
+      "samplers": [\r
+        {\r
+          "input": 6,\r
+          "interpolation": "LINEAR",\r
+          "output": 7\r
+        },\r
+        {\r
+          "input": 6,\r
+          "interpolation": "LINEAR",\r
+          "output": 8\r
+        },\r
+        {\r
+          "input": 6,\r
+          "interpolation": "LINEAR",\r
+          "output": 9\r
+        },\r
+        {\r
+          "input": 10,\r
+          "interpolation": "LINEAR",\r
+          "output": 11\r
+        },\r
+        {\r
+          "input": 10,\r
+          "interpolation": "LINEAR",\r
+          "output": 12\r
+        },\r
+        {\r
+          "input": 10,\r
+          "interpolation": "LINEAR",\r
+          "output": 13\r
+        },\r
+        {\r
+          "input": 14,\r
+          "interpolation": "LINEAR",\r
+          "output": 15\r
+        },\r
+        {\r
+          "input": 14,\r
+          "interpolation": "LINEAR",\r
+          "output": 16\r
+        },\r
+        {\r
+          "input": 14,\r
+          "interpolation": "LINEAR",\r
+          "output": 17\r
+        },\r
+        {\r
+          "input": 18,\r
+          "interpolation": "LINEAR",\r
+          "output": 19\r
+        },\r
+        {\r
+          "input": 18,\r
+          "interpolation": "LINEAR",\r
+          "output": 20\r
+        },\r
+        {\r
+          "input": 18,\r
+          "interpolation": "LINEAR",\r
+          "output": 21\r
+        },\r
+        {\r
+          "input": 22,\r
+          "interpolation": "LINEAR",\r
+          "output": 23\r
+        },\r
+        {\r
+          "input": 22,\r
+          "interpolation": "LINEAR",\r
+          "output": 24\r
+        },\r
+        {\r
+          "input": 22,\r
+          "interpolation": "LINEAR",\r
+          "output": 25\r
+        },\r
+        {\r
+          "input": 26,\r
+          "interpolation": "LINEAR",\r
+          "output": 27\r
+        },\r
+        {\r
+          "input": 26,\r
+          "interpolation": "LINEAR",\r
+          "output": 28\r
+        },\r
+        {\r
+          "input": 26,\r
+          "interpolation": "LINEAR",\r
+          "output": 29\r
+        },\r
+        {\r
+          "input": 30,\r
+          "interpolation": "LINEAR",\r
+          "output": 31\r
+        },\r
+        {\r
+          "input": 30,\r
+          "interpolation": "LINEAR",\r
+          "output": 32\r
+        },\r
+        {\r
+          "input": 30,\r
+          "interpolation": "LINEAR",\r
+          "output": 33\r
+        },\r
+        {\r
+          "input": 34,\r
+          "interpolation": "LINEAR",\r
+          "output": 35\r
+        },\r
+        {\r
+          "input": 34,\r
+          "interpolation": "LINEAR",\r
+          "output": 36\r
+        },\r
+        {\r
+          "input": 34,\r
+          "interpolation": "LINEAR",\r
+          "output": 37\r
+        },\r
+        {\r
+          "input": 38,\r
+          "interpolation": "LINEAR",\r
+          "output": 39\r
+        },\r
+        {\r
+          "input": 38,\r
+          "interpolation": "LINEAR",\r
+          "output": 40\r
+        },\r
+        {\r
+          "input": 38,\r
+          "interpolation": "LINEAR",\r
+          "output": 41\r
+        },\r
+        {\r
+          "input": 42,\r
+          "interpolation": "LINEAR",\r
+          "output": 43\r
+        },\r
+        {\r
+          "input": 42,\r
+          "interpolation": "LINEAR",\r
+          "output": 44\r
+        },\r
+        {\r
+          "input": 42,\r
+          "interpolation": "LINEAR",\r
+          "output": 45\r
+        },\r
+        {\r
+          "input": 46,\r
+          "interpolation": "LINEAR",\r
+          "output": 47\r
+        },\r
+        {\r
+          "input": 46,\r
+          "interpolation": "LINEAR",\r
+          "output": 48\r
+        },\r
+        {\r
+          "input": 46,\r
+          "interpolation": "LINEAR",\r
+          "output": 49\r
+        },\r
+        {\r
+          "input": 50,\r
+          "interpolation": "LINEAR",\r
+          "output": 51\r
+        },\r
+        {\r
+          "input": 50,\r
+          "interpolation": "LINEAR",\r
+          "output": 52\r
+        },\r
+        {\r
+          "input": 50,\r
+          "interpolation": "LINEAR",\r
+          "output": 53\r
+        },\r
+        {\r
+          "input": 54,\r
+          "interpolation": "LINEAR",\r
+          "output": 55\r
+        },\r
+        {\r
+          "input": 54,\r
+          "interpolation": "LINEAR",\r
+          "output": 56\r
+        },\r
+        {\r
+          "input": 54,\r
+          "interpolation": "LINEAR",\r
+          "output": 57\r
+        },\r
+        {\r
+          "input": 58,\r
+          "interpolation": "LINEAR",\r
+          "output": 59\r
+        },\r
+        {\r
+          "input": 58,\r
+          "interpolation": "LINEAR",\r
+          "output": 60\r
+        },\r
+        {\r
+          "input": 58,\r
+          "interpolation": "LINEAR",\r
+          "output": 61\r
+        },\r
+        {\r
+          "input": 62,\r
+          "interpolation": "LINEAR",\r
+          "output": 63\r
+        },\r
+        {\r
+          "input": 62,\r
+          "interpolation": "LINEAR",\r
+          "output": 64\r
+        },\r
+        {\r
+          "input": 62,\r
+          "interpolation": "LINEAR",\r
+          "output": 65\r
+        },\r
+        {\r
+          "input": 66,\r
+          "interpolation": "LINEAR",\r
+          "output": 67\r
+        },\r
+        {\r
+          "input": 66,\r
+          "interpolation": "LINEAR",\r
+          "output": 68\r
+        },\r
+        {\r
+          "input": 66,\r
+          "interpolation": "LINEAR",\r
+          "output": 69\r
+        },\r
+        {\r
+          "input": 70,\r
+          "interpolation": "LINEAR",\r
+          "output": 71\r
+        },\r
+        {\r
+          "input": 70,\r
+          "interpolation": "LINEAR",\r
+          "output": 72\r
+        },\r
+        {\r
+          "input": 70,\r
+          "interpolation": "LINEAR",\r
+          "output": 73\r
+        },\r
+        {\r
+          "input": 74,\r
+          "interpolation": "LINEAR",\r
+          "output": 75\r
+        },\r
+        {\r
+          "input": 74,\r
+          "interpolation": "LINEAR",\r
+          "output": 76\r
+        },\r
+        {\r
+          "input": 74,\r
+          "interpolation": "LINEAR",\r
+          "output": 77\r
+        },\r
+        {\r
+          "input": 78,\r
+          "interpolation": "LINEAR",\r
+          "output": 79\r
+        },\r
+        {\r
+          "input": 78,\r
+          "interpolation": "LINEAR",\r
+          "output": 80\r
+        },\r
+        {\r
+          "input": 78,\r
+          "interpolation": "LINEAR",\r
+          "output": 81\r
+        }\r
+      ]\r
+    }\r
+  ],\r
+  "skins": [\r
+    {\r
+      "inverseBindMatrices": 82,\r
+      "skeleton": 3,\r
+      "joints": [\r
+        3,\r
+        12,\r
+        13,\r
+        20,\r
+        21,\r
+        17,\r
+        14,\r
+        18,\r
+        15,\r
+        19,\r
+        16,\r
+        8,\r
+        4,\r
+        9,\r
+        5,\r
+        10,\r
+        6,\r
+        11,\r
+        7\r
+      ],\r
+      "name": "Armature"\r
+    }\r
+  ],\r
+  "accessors": [\r
+    {\r
+      "bufferView": 0,\r
+      "byteOffset": 0,\r
+      "componentType": 5123,\r
+      "count": 14016,\r
+      "max": [\r
+        3272\r
+      ],\r
+      "min": [\r
+        0\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 1,\r
+      "byteOffset": 0,\r
+      "componentType": 5123,\r
+      "count": 3273,\r
+      "max": [\r
+        18,\r
+        18,\r
+        18,\r
+        18\r
+      ],\r
+      "min": [\r
+        0,\r
+        0,\r
+        0,\r
+        0\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 2,\r
+      "byteOffset": 0,\r
+      "componentType": 5126,\r
+      "count": 3273,\r
+      "max": [\r
+        1,\r
+        0.9999808073043824,\r
+        0.9944415092468262\r
+      ],\r
+      "min": [\r
+        -1,\r
+        -0.9999808073043824,\r
+        -1\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 2,\r
+      "byteOffset": 39276,\r
+      "componentType": 5126,\r
+      "count": 3273,\r
+      "max": [\r
+        0.1809539943933487,\r
+        0.569136917591095,\r
+        1.5065499544143677\r
+      ],\r
+      "min": [\r
+        -0.13100001215934753,\r
+        -0.5691370964050293,\r
+        0\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 1,\r
+      "byteOffset": 26184,\r
+      "componentType": 5126,\r
+      "count": 3273,\r
+      "max": [\r
+        0.990805983543396,\r
+        0.9880298972129822\r
+      ],\r
+      "min": [\r
+        0.014079390093684196,\r
+        0.008445978164672852\r
+      ],\r
+      "type": "VEC2"\r
+    },\r
+    {\r
+      "bufferView": 3,\r
+      "byteOffset": 0,\r
+      "componentType": 5126,\r
+      "count": 3273,\r
+      "max": [\r
+        1,\r
+        0.989919900894165,\r
+        0.9510763883590698,\r
+        0.9196179509162904\r
+      ],\r
+      "min": [\r
+        0.010080089792609217,\r
+        0,\r
+        0,\r
+        0\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 0,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 0,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        3.880559873437051e-8,\r
+        -0.02000010944902897,\r
+        0.7110069990158081\r
+      ],\r
+      "min": [\r
+        2.716890046272624e-9,\r
+        -0.030000129714608192,\r
+        0.6399999856948853\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 0,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.0001435002632206306,\r
+        -0.02146764844655991,\r
+        -0.000009948204024112783,\r
+        -0.9980905055999756\r
+      ],\r
+      "min": [\r
+        -0.012384946458041668,\r
+        -0.06042621284723282,\r
+        -0.0041049933061003685,\r
+        -0.9997026920318604\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 576,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.0000004768371584,\r
+        1.0000001192092896,\r
+        1.000000238418579\r
+      ],\r
+      "min": [\r
+        0.9999999403953552,\r
+        0.9999998211860656,\r
+        0.9999997615814208\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 192,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 1152,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.000013520900211005937,\r
+        0.0009866129839792848,\r
+        0.1454171985387802\r
+      ],\r
+      "min": [\r
+        0.000013436199878924528,\r
+        0.0009865909814834597,\r
+        0.145416796207428\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 768,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.011148878373205662,\r
+        -0.7106494903564453,\r
+        0.0006393495132215321,\r
+        -0.6787928938865662\r
+      ],\r
+      "min": [\r
+        -0.008564536459743977,\r
+        -0.7339289784431458,\r
+        -0.025856714695692062,\r
+        -0.7034341096878052\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 1728,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.0000004768371584,\r
+        1.000000238418579,\r
+        0.9999998211860656\r
+      ],\r
+      "min": [\r
+        0.9999999403953552,\r
+        0.9999998807907104,\r
+        0.9999989867210388\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 384,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 2304,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        -0.25051650404930115,\r
+        5.923209869251878e-7,\r
+        -0.00007277730037458241\r
+      ],\r
+      "min": [\r
+        -0.25051701068878174,\r
+        5.58793999516638e-7,\r
+        -0.00007287789776455611\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 1536,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.13804565370082855,\r
+        0.6359269618988037,\r
+        -0.003375347936525941,\r
+        -0.7641801238059998\r
+      ],\r
+      "min": [\r
+        -0.06163197010755539,\r
+        0.6225405335426331,\r
+        -0.0653248131275177,\r
+        -0.7825579643249512\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 2880,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.000001072883606,\r
+        1.0000003576278689,\r
+        1\r
+      ],\r
+      "min": [\r
+        1.0000004768371584,\r
+        0.9999999403953552,\r
+        0.999999463558197\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 576,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 3456,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        -0.000002384189883741783,\r
+        0.000002458690005369135,\r
+        0.06483876705169678\r
+      ],\r
+      "min": [\r
+        -0.000002536919964768458,\r
+        0.000002384189883741783,\r
+        0.06483828276395798\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 2304,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.0364987850189209,\r
+        -0.6325404644012451,\r
+        0.04193282127380371,\r
+        -0.749859094619751\r
+      ],\r
+      "min": [\r
+        -0.02474863827228546,\r
+        -0.6592763066291809,\r
+        -0.03008362464606762,\r
+        -0.7735469341278076\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 4032,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1,\r
+        1.000000238418579,\r
+        1.000000238418579\r
+      ],\r
+      "min": [\r
+        0.9999996423721313,\r
+        0.9999995231628418,\r
+        0.9999995827674866\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 768,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 4608,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        -0.0520395003259182,\r
+        7.450579708745408e-9,\r
+        -0.000002585350102890516\r
+      ],\r
+      "min": [\r
+        -0.05204005911946297,\r
+        -5.96045985901128e-8,\r
+        -0.0000026747600259113824\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 3072,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.04680187255144119,\r
+        0.999507486820221,\r
+        0.002036086050793529,\r
+        0.09058715403079988\r
+      ],\r
+      "min": [\r
+        -0.093629889190197,\r
+        0.9950671792030336,\r
+        -0.00258980062790215,\r
+        0.0184526015073061\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 5184,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.0000003576278689,\r
+        1.000000238418579,\r
+        1.0000009536743164\r
+      ],\r
+      "min": [\r
+        0.9999998807907104,\r
+        0.9999996423721313,\r
+        1.000000238418579\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 960,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 5760,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        -0.00003742050103028305,\r
+        0.08800023794174194,\r
+        -0.00005880460230400786\r
+      ],\r
+      "min": [\r
+        -0.000037621699448209256,\r
+        0.08799994736909866,\r
+        -0.000059304802562110126\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 3840,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.2951536476612091,\r
+        0.9301012754440308,\r
+        -0.2815393805503845,\r
+        0.3835828900337219\r
+      ],\r
+      "min": [\r
+        -0.13552021980285645,\r
+        0.8065234422683716,\r
+        -0.4443180561065674,\r
+        -0.17752912640571597\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 6336,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.0000005960464478,\r
+        1.0000001192092896,\r
+        1.0000003576278689\r
+      ],\r
+      "min": [\r
+        0.9999999403953552,\r
+        0.9999996423721313,\r
+        0.9999998211860656\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 1152,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 6912,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.013221889734268188,\r
+        0.215499609708786,\r
+        0.10933230072259904\r
+      ],\r
+      "min": [\r
+        0.01322161965072155,\r
+        0.2154994010925293,\r
+        0.10933209955692293\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 4608,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.023567700758576393,\r
+        0.02101488783955574,\r
+        0.176296666264534,\r
+        -0.971515953540802\r
+      ],\r
+      "min": [\r
+        -0.0574759915471077,\r
+        -0.18002526462078097,\r
+        -0.15063291788101196,\r
+        -0.998132348060608\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 7488,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.9999998211860656,\r
+        0.9999998211860656,\r
+        0.9999999403953552\r
+      ],\r
+      "min": [\r
+        0.9999991059303284,\r
+        0.9999993443489076,\r
+        0.9999994039535524\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 1344,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 8064,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        -0.09332455694675446,\r
+        0.1430000960826874,\r
+        0.07814794778823853\r
+      ],\r
+      "min": [\r
+        -0.09332473576068878,\r
+        0.14299990236759189,\r
+        0.07814773917198181\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 5376,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.03372078761458397,\r
+        0.0026474546175450087,\r
+        0.207317128777504,\r
+        -0.9705979824066162\r
+      ],\r
+      "min": [\r
+        0.006105833686888218,\r
+        -0.12215615808963776,\r
+        0.003784916130825877,\r
+        -0.9994208216667176\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 8640,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.0000007152557373,\r
+        1.0000003576278689,\r
+        1.0000008344650269\r
+      ],\r
+      "min": [\r
+        1.0000001192092896,\r
+        0.9999998211860656,\r
+        1.000000238418579\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 1536,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 9216,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        -0.00003894419933203608,\r
+        -0.0879998430609703,\r
+        -0.00005919210161664523\r
+      ],\r
+      "min": [\r
+        -0.0000392795009247493,\r
+        -0.08800008893013,\r
+        -0.00005960090129519813\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 6144,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.2377220243215561,\r
+        0.942186713218689,\r
+        0.37760788202285767,\r
+        0.2007839232683182\r
+      ],\r
+      "min": [\r
+        -0.2700891792774201,\r
+        0.8732703924179077,\r
+        0.2710656225681305,\r
+        -0.2673804461956024\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 9792,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.000000238418579,\r
+        1.0000003576278689,\r
+        1.0000001192092896\r
+      ],\r
+      "min": [\r
+        0.999999701976776,\r
+        0.9999997615814208,\r
+        0.9999997615814208\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 1728,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 10368,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        -0.035546209663152695,\r
+        -0.21549880504608157,\r
+        0.10423330217599867\r
+      ],\r
+      "min": [\r
+        -0.03554638102650643,\r
+        -0.21549910306930545,\r
+        0.10423299670219421\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 6912,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        -0.00792065542191267,\r
+        0.9315358996391296,\r
+        0.0024673622101545334,\r
+        0.41479358077049255\r
+      ],\r
+      "min": [\r
+        -0.15234939754009247,\r
+        0.9063802361488342,\r
+        -0.08167753368616104,\r
+        0.3280625641345978\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 10944,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.0000005960464478,\r
+        1.0000001192092896,\r
+        1.000000238418579\r
+      ],\r
+      "min": [\r
+        1,\r
+        0.9999996423721313,\r
+        0.9999996423721313\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 1920,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 11520,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.03137049078941345,\r
+        -0.1430007964372635,\r
+        -0.11761150509119034\r
+      ],\r
+      "min": [\r
+        0.03137030825018883,\r
+        -0.1430010050535202,\r
+        -0.11761169880628586\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 7680,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.22148266434669495,\r
+        0.3926030695438385,\r
+        0.08952529728412628,\r
+        -0.9178923964500428\r
+      ],\r
+      "min": [\r
+        0.055695075541734695,\r
+        0.277910977602005,\r
+        -0.015314305201172829,\r
+        -0.9438881278038024\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 12096,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.0000004768371584,\r
+        1.0000004768371584,\r
+        1.0000004768371584\r
+      ],\r
+      "min": [\r
+        0.9999997615814208,\r
+        0.9999997615814208,\r
+        0.9999998807907104\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 2112,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 12672,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.028520189225673676,\r
+        0.06762184202671051,\r
+        -0.06295985728502274\r
+      ],\r
+      "min": [\r
+        0.028520019724965096,\r
+        0.06762173771858215,\r
+        -0.06296010315418243\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 8448,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.013129070401191711,\r
+        0.10440785437822342,\r
+        0.004284336231648922,\r
+        -0.7728573679924011\r
+      ],\r
+      "min": [\r
+        -0.013805897906422617,\r
+        -0.6344362497329712,\r
+        -0.03212129324674606,\r
+        -0.9994977116584778\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 13248,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.0000001192092896,\r
+        1.0000004768371584,\r
+        1.000000238418579\r
+      ],\r
+      "min": [\r
+        0.9999995231628418,\r
+        1,\r
+        0.9999995827674866\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 2304,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 13824,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.209164097905159,\r
+        0.009055494330823421,\r
+        -0.16426970064640045\r
+      ],\r
+      "min": [\r
+        0.20916390419006348,\r
+        0.009055464528501034,\r
+        -0.1642698049545288\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 9216,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.009955321438610554,\r
+        -0.2965533435344696,\r
+        0.003957682754844427,\r
+        -0.1911347657442093\r
+      ],\r
+      "min": [\r
+        -0.00983923487365246,\r
+        -0.9813112020492554,\r
+        -0.02193812094628811,\r
+        -0.9549583792686462\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 14400,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.000000238418579,\r
+        0.999999463558197,\r
+        1.0000001192092896\r
+      ],\r
+      "min": [\r
+        0.999999463558197,\r
+        0.999998927116394,\r
+        0.9999993443489076\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 2496,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 14976,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.2757900059223175,\r
+        0.0013972820015624166,\r
+        0.004122554790228605\r
+      ],\r
+      "min": [\r
+        0.27578991651535034,\r
+        0.0013972449814900756,\r
+        0.004122436046600342\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 9984,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.01264618057757616,\r
+        -0.8448027968406677,\r
+        0.03285584971308708,\r
+        -0.15347692370414737\r
+      ],\r
+      "min": [\r
+        -0.045710742473602295,\r
+        -0.9879721403121948,\r
+        0.007757793180644512,\r
+        -0.5345877408981323\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 15552,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.0000008344650269,\r
+        1.0000009536743164,\r
+        1.0000004768371584\r
+      ],\r
+      "min": [\r
+        1.000000238418579,\r
+        1.0000003576278689,\r
+        0.9999997615814208\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 2688,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 16128,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        -0.06558377295732498,\r
+        0.00109061598777771,\r
+        0.029291389510035515\r
+      ],\r
+      "min": [\r
+        -0.06558384746313095,\r
+        0.001090570935048163,\r
+        0.029291240498423576\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 10752,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.022798636928200725,\r
+        0.5332140922546387,\r
+        -0.003377946326509118,\r
+        -0.844382643699646\r
+      ],\r
+      "min": [\r
+        0.007516560610383749,\r
+        0.22626954317092896,\r
+        -0.04913739487528801,\r
+        -0.972984254360199\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 16704,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.0000008344650269,\r
+        1.0000003576278689,\r
+        1.0000003576278689\r
+      ],\r
+      "min": [\r
+        0.9999998211860656,\r
+        0.999999701976776,\r
+        0.9999995231628418\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 2880,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 17280,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.028557300567626953,\r
+        -0.0684543326497078,\r
+        -0.06295845657587051\r
+      ],\r
+      "min": [\r
+        0.028557060286402702,\r
+        -0.06845436990261078,\r
+        -0.0629587471485138\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 11520,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.04037770628929138,\r
+        -0.2803998589515686,\r
+        0.02151232957839966,\r
+        -0.32386553287506104\r
+      ],\r
+      "min": [\r
+        -0.009615562856197357,\r
+        -0.9458208084106444,\r
+        -0.006491139996796846,\r
+        -0.9590301513671876\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 17856,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.000000238418579,\r
+        1.0000005960464478,\r
+        1.0000005960464478\r
+      ],\r
+      "min": [\r
+        0.9999994039535524,\r
+        0.9999999403953552,\r
+        0.9999998211860656\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 3072,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 18432,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.2608909010887146,\r
+        -0.00902603566646576,\r
+        0.05167100951075554\r
+      ],\r
+      "min": [\r
+        0.2608906924724579,\r
+        -0.009026064537465572,\r
+        0.05167080089449883\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 12288,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.02468797937035561,\r
+        0.19154119491577148,\r
+        0.017835097387433052,\r
+        -0.6250466108322144\r
+      ],\r
+      "min": [\r
+        -0.013421673327684404,\r
+        -0.7804162502288818,\r
+        -0.031287722289562225,\r
+        -0.9999792575836182\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 19008,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.0000003576278689,\r
+        1.0000007152557373,\r
+        1.000001072883606\r
+      ],\r
+      "min": [\r
+        0.999999463558197,\r
+        1,\r
+        0.999999701976776\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 3264,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 19584,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.2754603922367096,\r
+        -0.0014316890155896544,\r
+        -0.014104750007390976\r
+      ],\r
+      "min": [\r
+        0.27546021342277527,\r
+        -0.0014317409368231893,\r
+        -0.014104840345680714\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 13056,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.022092316299676895,\r
+        0.9990847110748292,\r
+        0.04779285565018654,\r
+        0.4428757429122925\r
+      ],\r
+      "min": [\r
+        -0.001671039150096476,\r
+        0.8965795040130615,\r
+        0.002310338197275996,\r
+        0.0384783074259758\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 20160,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.9999999403953552,\r
+        0.9999996423721313,\r
+        1.000000238418579\r
+      ],\r
+      "min": [\r
+        0.9999994039535524,\r
+        0.9999991655349731,\r
+        0.9999996423721313\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "byteOffset": 3456,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        2\r
+      ],\r
+      "min": [\r
+        0.04166661947965622\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 20736,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        -0.06681966781616211,\r
+        -0.0010721459984779358,\r
+        0.026351390406489372\r
+      ],\r
+      "min": [\r
+        -0.06681978702545166,\r
+        -0.001072190934792161,\r
+        0.02635126002132893\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "byteOffset": 13824,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        0.003402489935979247,\r
+        0.4966025054454804,\r
+        0.1101396307349205,\r
+        -0.8675833940505981\r
+      ],\r
+      "min": [\r
+        -0.027623889967799187,\r
+        0.26874634623527527,\r
+        -0.02591408602893353,\r
+        -0.9565747380256652\r
+      ],\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "byteOffset": 21312,\r
+      "componentType": 5126,\r
+      "count": 48,\r
+      "max": [\r
+        1.0000004768371584,\r
+        0.9999998211860656,\r
+        0.9999994039535524\r
+      ],\r
+      "min": [\r
+        0.9999995231628418,\r
+        0.999999225139618,\r
+        0.9999986886978148\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 7,\r
+      "byteOffset": 0,\r
+      "componentType": 5126,\r
+      "count": 19,\r
+      "max": [\r
+        0.9971418380737304,\r
+        -4.371139894487897e-8,\r
+        0.9996265172958374,\r
+        0,\r
+        4.3586464215650273e-8,\r
+        1,\r
+        4.3695074225524884e-8,\r
+        0,\r
+        0.9999366402626038,\r
+        0,\r
+        0.9971418380737304,\r
+        0,\r
+        1.1374080181121828,\r
+        0.44450080394744873,\r
+        1.0739599466323853,\r
+        1\r
+      ],\r
+      "min": [\r
+        -0.9999089241027832,\r
+        -4.371139894487897e-8,\r
+        -0.9999366402626038,\r
+        0,\r
+        -4.3707416352845037e-8,\r
+        1,\r
+        -4.37086278282095e-8,\r
+        0,\r
+        -0.9996265172958374,\r
+        0,\r
+        -0.9999089241027832,\r
+        0,\r
+        -1.189831018447876,\r
+        -0.45450031757354736,\r
+        -1.058603048324585,\r
+        1\r
+      ],\r
+      "type": "MAT4"\r
+    }\r
+  ],\r
+  "materials": [\r
+    {\r
+      "pbrMetallicRoughness": {\r
+        "baseColorTexture": {\r
+          "index": 0,\r
+          "texCoord": 0\r
+        },\r
+        "metallicFactor": 0,\r
+        "baseColorFactor": [\r
+          1,\r
+          1,\r
+          1,\r
+          1\r
+        ],\r
+        "roughnessFactor": 1\r
+      },\r
+      "emissiveFactor": [\r
+        0,\r
+        0,\r
+        0\r
+      ],\r
+      "name": "Cesium_Man-effect",\r
+      "alphaMode": "OPAQUE",\r
+      "doubleSided": false\r
+    }\r
+  ],\r
+  "textures": [\r
+    {\r
+      "sampler": 0,\r
+      "source": 0\r
+    }\r
+  ],\r
+  "images": [\r
+    {\r
+      "uri": "CesiumMan_img0.jpg"\r
+    }\r
+  ],\r
+  "samplers": [\r
+    {\r
+      "magFilter": 9729,\r
+      "minFilter": 9986,\r
+      "wrapS": 10497,\r
+      "wrapT": 10497\r
+    }\r
+  ],\r
+  "bufferViews": [\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 0,\r
+      "byteLength": 28032,\r
+      "target": 34963\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 28032,\r
+      "byteLength": 52368,\r
+      "byteStride": 8,\r
+      "target": 34962\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 80400,\r
+      "byteLength": 78552,\r
+      "byteStride": 12,\r
+      "target": 34962\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 158952,\r
+      "byteLength": 52368,\r
+      "byteStride": 16,\r
+      "target": 34962\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 211320,\r
+      "byteLength": 3648\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 214968,\r
+      "byteLength": 21888\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 236856,\r
+      "byteLength": 14592\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 251448,\r
+      "byteLength": 1216\r
+    }\r
+  ],\r
+  "buffers": [\r
+    {\r
+      "uri": "CesiumMan_data.bin",\r
+      "byteLength": 252664\r
+    }\r
+  ]\r
+}
\ No newline at end of file
diff --git a/automated-tests/resources/CesiumMan_data.bin b/automated-tests/resources/CesiumMan_data.bin
new file mode 100644 (file)
index 0000000..d78b75c
Binary files /dev/null and b/automated-tests/resources/CesiumMan_data.bin differ
diff --git a/automated-tests/resources/CesiumMilkTruck.gltf b/automated-tests/resources/CesiumMilkTruck.gltf
new file mode 100644 (file)
index 0000000..c9a24e3
--- /dev/null
@@ -0,0 +1,489 @@
+{\r
+  "asset": {\r
+    "generator": "Khronos glTF Blender I/O v1.0.5",\r
+    "version": "2.0"\r
+  },\r
+  "scene": 0,\r
+  "scenes": [\r
+    {\r
+      "name": "Scene",\r
+      "nodes": [\r
+        5\r
+      ]\r
+    }\r
+  ],\r
+  "nodes": [\r
+    {\r
+      "mesh": 0,\r
+      "name": "Wheels",\r
+      "rotation": [\r
+        0,\r
+        0.08848588913679123,\r
+        0,\r
+        -0.9960774183273315\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        0\r
+      ],\r
+      "name": "Node",\r
+      "translation": [\r
+        1.432669997215271,\r
+        0,\r
+        -0.4277220070362091\r
+      ]\r
+    },\r
+    {\r
+      "mesh": 0,\r
+      "name": "Wheels.001",\r
+      "rotation": [\r
+        0,\r
+        0.08848588913679123,\r
+        0,\r
+        -0.9960774183273315\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        2\r
+      ],\r
+      "name": "Node.001",\r
+      "translation": [\r
+        -1.352329969406128,\r
+        0,\r
+        -0.4277220070362091\r
+      ]\r
+    },\r
+    {\r
+      "children": [\r
+        1,\r
+        3\r
+      ],\r
+      "mesh": 1,\r
+      "name": "Cesium_Milk_Truck"\r
+    },\r
+    {\r
+      "children": [\r
+        4\r
+      ],\r
+      "name": "Yup2Zup",\r
+      "rotation": [\r
+        0.4999999701976776,\r
+        -0.5,\r
+        0.5,\r
+        0.4999999701976776\r
+      ]\r
+    }\r
+  ],\r
+  "animations": [\r
+    {\r
+      "channels": [\r
+        {\r
+          "sampler": 0,\r
+          "target": {\r
+            "node": 0,\r
+            "path": "rotation"\r
+          }\r
+        },\r
+        {\r
+          "sampler": 1,\r
+          "target": {\r
+            "node": 2,\r
+            "path": "rotation"\r
+          }\r
+        }\r
+      ],\r
+      "name": "Wheels",\r
+      "samplers": [\r
+        {\r
+          "input": 16,\r
+          "interpolation": "LINEAR",\r
+          "output": 17\r
+        },\r
+        {\r
+          "input": 16,\r
+          "interpolation": "LINEAR",\r
+          "output": 18\r
+        }\r
+      ]\r
+    }\r
+  ],\r
+  "materials": [\r
+    {\r
+      "name": "wheels",\r
+      "pbrMetallicRoughness": {\r
+        "baseColorTexture": {\r
+          "index": 0,\r
+          "texCoord": 0\r
+        },\r
+        "metallicFactor": 0,\r
+        "roughnessFactor": 1\r
+      }\r
+    },\r
+    {\r
+      "name": "truck",\r
+      "pbrMetallicRoughness": {\r
+        "baseColorTexture": {\r
+          "index": 1,\r
+          "texCoord": 0\r
+        },\r
+        "metallicFactor": 0,\r
+        "roughnessFactor": 1\r
+      }\r
+    },\r
+    {\r
+      "name": "glass",\r
+      "pbrMetallicRoughness": {\r
+        "baseColorFactor": [\r
+          0,\r
+          0.04050629958510399,\r
+          0.021240700036287308,\r
+          1\r
+        ],\r
+        "metallicFactor": 0,\r
+        "roughnessFactor": 1\r
+      }\r
+    },\r
+    {\r
+      "name": "window_trim",\r
+      "pbrMetallicRoughness": {\r
+        "baseColorFactor": [\r
+          0.06400000303983688,\r
+          0.06400000303983688,\r
+          0.06400000303983688,\r
+          1\r
+        ],\r
+        "metallicFactor": 0,\r
+        "roughnessFactor": 1\r
+      }\r
+    }\r
+  ],\r
+  "meshes": [\r
+    {\r
+      "name": "Wheels",\r
+      "primitives": [\r
+        {\r
+          "attributes": {\r
+            "POSITION": 0,\r
+            "NORMAL": 1,\r
+            "TEXCOORD_0": 2\r
+          },\r
+          "indices": 3,\r
+          "material": 0\r
+        }\r
+      ]\r
+    },\r
+    {\r
+      "name": "Cesium_Milk_Truck",\r
+      "primitives": [\r
+        {\r
+          "attributes": {\r
+            "POSITION": 4,\r
+            "NORMAL": 5,\r
+            "TEXCOORD_0": 6\r
+          },\r
+          "indices": 7,\r
+          "material": 1\r
+        },\r
+        {\r
+          "attributes": {\r
+            "POSITION": 8,\r
+            "NORMAL": 9,\r
+            "TEXCOORD_0": 10\r
+          },\r
+          "indices": 11,\r
+          "material": 2\r
+        },\r
+        {\r
+          "attributes": {\r
+            "POSITION": 12,\r
+            "NORMAL": 13,\r
+            "TEXCOORD_0": 14\r
+          },\r
+          "indices": 15,\r
+          "material": 3\r
+        }\r
+      ]\r
+    }\r
+  ],\r
+  "textures": [\r
+    {\r
+      "source": 0\r
+    },\r
+    {\r
+      "source": 0\r
+    }\r
+  ],\r
+  "images": [\r
+    {\r
+      "name": "CesiumMilkTruck.jpg",\r
+      "uri": "CesiumMilkTruck.jpg"\r
+    }\r
+  ],\r
+  "accessors": [\r
+    {\r
+      "bufferView": 0,\r
+      "componentType": 5126,\r
+      "count": 828,\r
+      "max": [\r
+        0.4277999997138977,\r
+        1.0579999685287476,\r
+        0.4277999997138977\r
+      ],\r
+      "min": [\r
+        -0.4277999997138977,\r
+        -1.0579999685287476,\r
+        -0.4277999997138977\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 1,\r
+      "componentType": 5126,\r
+      "count": 828,\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 2,\r
+      "componentType": 5126,\r
+      "count": 828,\r
+      "type": "VEC2"\r
+    },\r
+    {\r
+      "bufferView": 3,\r
+      "componentType": 5123,\r
+      "count": 2304,\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "componentType": 5126,\r
+      "count": 2366,\r
+      "max": [\r
+        2.437999963760376,\r
+        1.3960000276565552,\r
+        -0.2667999863624573\r
+      ],\r
+      "min": [\r
+        -2.430910110473633,\r
+        -1.3960000276565552,\r
+        -2.5843698978424072\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "componentType": 5126,\r
+      "count": 2366,\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "componentType": 5126,\r
+      "count": 2366,\r
+      "type": "VEC2"\r
+    },\r
+    {\r
+      "bufferView": 7,\r
+      "componentType": 5123,\r
+      "count": 5232,\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 8,\r
+      "componentType": 5126,\r
+      "count": 151,\r
+      "max": [\r
+        1.6011799573898315,\r
+        1.3960000276565552,\r
+        -1.631850004196167\r
+      ],\r
+      "min": [\r
+        0.22885000705718994,\r
+        -1.3960000276565552,\r
+        -2.3545401096343994\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 9,\r
+      "componentType": 5126,\r
+      "count": 151,\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 10,\r
+      "componentType": 5126,\r
+      "count": 151,\r
+      "type": "VEC2"\r
+    },\r
+    {\r
+      "bufferView": 11,\r
+      "componentType": 5123,\r
+      "count": 168,\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 12,\r
+      "componentType": 5126,\r
+      "count": 650,\r
+      "max": [\r
+        1.62267005443573,\r
+        1.100000023841858,\r
+        -1.5961999893188477\r
+      ],\r
+      "min": [\r
+        0.1932000070810318,\r
+        -1.1100000143051147,\r
+        -2.3919999599456787\r
+      ],\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 13,\r
+      "componentType": 5126,\r
+      "count": 650,\r
+      "type": "VEC3"\r
+    },\r
+    {\r
+      "bufferView": 14,\r
+      "componentType": 5126,\r
+      "count": 650,\r
+      "type": "VEC2"\r
+    },\r
+    {\r
+      "bufferView": 15,\r
+      "componentType": 5123,\r
+      "count": 864,\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 16,\r
+      "componentType": 5126,\r
+      "count": 31,\r
+      "max": [\r
+        1.25\r
+      ],\r
+      "min": [\r
+        0\r
+      ],\r
+      "type": "SCALAR"\r
+    },\r
+    {\r
+      "bufferView": 17,\r
+      "componentType": 5126,\r
+      "count": 31,\r
+      "type": "VEC4"\r
+    },\r
+    {\r
+      "bufferView": 18,\r
+      "componentType": 5126,\r
+      "count": 31,\r
+      "type": "VEC4"\r
+    }\r
+  ],\r
+  "bufferViews": [\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 9936,\r
+      "byteOffset": 0\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 9936,\r
+      "byteOffset": 9936\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 6624,\r
+      "byteOffset": 19872\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 4608,\r
+      "byteOffset": 26496\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 28392,\r
+      "byteOffset": 31104\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 28392,\r
+      "byteOffset": 59496\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 18928,\r
+      "byteOffset": 87888\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 10464,\r
+      "byteOffset": 106816\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 1812,\r
+      "byteOffset": 117280\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 1812,\r
+      "byteOffset": 119092\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 1208,\r
+      "byteOffset": 120904\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 336,\r
+      "byteOffset": 122112\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 7800,\r
+      "byteOffset": 122448\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 7800,\r
+      "byteOffset": 130248\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 5200,\r
+      "byteOffset": 138048\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 1728,\r
+      "byteOffset": 143248\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 124,\r
+      "byteOffset": 144976\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 496,\r
+      "byteOffset": 145100\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteLength": 496,\r
+      "byteOffset": 145596\r
+    }\r
+  ],\r
+  "buffers": [\r
+    {\r
+      "uri": "CesiumMilkTruck_data.bin",\r
+      "byteLength": 146092\r
+    }\r
+  ]\r
+}\r
diff --git a/automated-tests/resources/CesiumMilkTruck_data.bin b/automated-tests/resources/CesiumMilkTruck_data.bin
new file mode 100644 (file)
index 0000000..51e3646
Binary files /dev/null and b/automated-tests/resources/CesiumMilkTruck_data.bin differ
diff --git a/automated-tests/resources/EnvironmentTest.gltf b/automated-tests/resources/EnvironmentTest.gltf
new file mode 100644 (file)
index 0000000..962e475
--- /dev/null
@@ -0,0 +1,328 @@
+{\r
+    "asset": {\r
+        "copyright": "2018 (c) Adobe Systems Inc.",\r
+        "generator": "Adobe Dimension - b417c10282aa66313155856d4a54e84f3f388647",\r
+        "version": "2.0"\r
+    },\r
+    "accessors": [\r
+        {\r
+            "bufferView": 0,\r
+            "componentType": 5126,\r
+            "count": 4598,\r
+            "type": "VEC3",\r
+            "max": [\r
+                10.647041320800782,\r
+                1.6470409631729127,\r
+                0.6470409631729126\r
+            ],\r
+            "min": [\r
+                -10.647041320800782,\r
+                0.3529590368270874,\r
+                -0.6470409631729126\r
+            ]\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "componentType": 5126,\r
+            "count": 4598,\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 2,\r
+            "componentType": 5126,\r
+            "count": 4598,\r
+            "type": "VEC2"\r
+        },\r
+        {\r
+            "bufferView": 3,\r
+            "componentType": 5125,\r
+            "count": 25344,\r
+            "type": "SCALAR",\r
+            "max": [\r
+                4597.0\r
+            ],\r
+            "min": [\r
+                0.0\r
+            ]\r
+        },\r
+        {\r
+            "bufferView": 4,\r
+            "componentType": 5126,\r
+            "count": 4598,\r
+            "type": "VEC3",\r
+            "max": [\r
+                10.647041320800782,\r
+                -0.3529590368270874,\r
+                0.6470409631729126\r
+            ],\r
+            "min": [\r
+                -10.647041320800782,\r
+                -1.6470409631729127,\r
+                -0.6470409631729126\r
+            ]\r
+        },\r
+        {\r
+            "bufferView": 5,\r
+            "componentType": 5126,\r
+            "count": 4598,\r
+            "type": "VEC2"\r
+        }\r
+    ],\r
+    "bufferViews": [\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 0,\r
+            "byteLength": 55176,\r
+            "target": 34962\r
+        },\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 55176,\r
+            "byteLength": 55176,\r
+            "target": 34962\r
+        },\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 110352,\r
+            "byteLength": 36784,\r
+            "target": 34962\r
+        },\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 147136,\r
+            "byteLength": 101376,\r
+            "target": 34963\r
+        },\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 248512,\r
+            "byteLength": 55176,\r
+            "target": 34962\r
+        },\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 303688,\r
+            "byteLength": 36784,\r
+            "target": 34962\r
+        }\r
+    ],\r
+    "buffers": [\r
+        {\r
+            "byteLength": 340472,\r
+            "uri": "EnvironmentTest_binary.bin"\r
+        }\r
+    ],\r
+    "cameras": [\r
+        {\r
+            "perspective": {\r
+                "znear": 0.0010000000474974514,\r
+                "yfov": 0.6024156808853149,\r
+                "zfar": 200.0,\r
+                "aspectRatio": 1.3333333730697632\r
+            },\r
+            "type": "perspective",\r
+            "name": "render_camera"\r
+        }\r
+    ],\r
+    "images": [\r
+        {\r
+            "name": "tmp_image_pie_dc1e_1a22_fbf9roughness_map_roughness_tmp_image_pie_dc1e_1a22_fbf9metal_map_metallic_0",\r
+            "uri": "EnvironmentTest_images/roughness_metallic_0.jpg",\r
+            "mimeType": "image/jpeg"\r
+        },\r
+        {\r
+            "name": "tmp_image_pie_b20b_ebb4_317droughness_map2_roughness_tmp_image_pie_b20b_ebb4_317dmetal_map2_metallic_1",\r
+            "uri": "EnvironmentTest_images/roughness_metallic_1.jpg",\r
+            "mimeType": "image/jpeg"\r
+        }\r
+    ],\r
+    "materials": [\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "metallicRoughnessTexture": {\r
+                    "index": 0\r
+                }\r
+            },\r
+            "name": "MetallicSpheresMat",\r
+            "doubleSided": true\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "metallicRoughnessTexture": {\r
+                    "index": 1\r
+                }\r
+            },\r
+            "name": "DielectricSpheresMat",\r
+            "doubleSided": true\r
+        },\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorFactor": [\r
+                    0.0,\r
+                    0.0,\r
+                    0.0,\r
+                    1.0\r
+                ],\r
+                "metallicRoughnessTexture": {\r
+                    "index": 1\r
+                }\r
+            },\r
+            "name": "DielectricSpheresMat",\r
+            "doubleSided": true\r
+        }\r
+    ],\r
+    "meshes": [\r
+        {\r
+            "name": "Metallic0_N3D",\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "POSITION": 0,\r
+                        "NORMAL": 1,\r
+                        "TEXCOORD_0": 2\r
+                    },\r
+                    "indices": 3,\r
+                    "material": 0\r
+                }\r
+            ]\r
+        },\r
+        {\r
+            "name": "Dielectric0_N3D2",\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "TEXCOORD_0": 5,\r
+                        "NORMAL": 1,\r
+                        "POSITION": 4\r
+                    },\r
+                    "indices": 3,\r
+                    "material": 1\r
+                }\r
+            ]\r
+        },\r
+        {\r
+            "name": "Dielectric0_N3D",\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "POSITION": 4,\r
+                        "NORMAL": 1,\r
+                        "TEXCOORD_0": 5\r
+                    },\r
+                    "indices": 3,\r
+                    "material": 2\r
+                }\r
+            ]\r
+        }\r
+    ],\r
+    "nodes": [\r
+        {\r
+            "matrix": [\r
+                0.9999533295631409,\r
+                3.16067598760128e-8,\r
+                0.009662099182605744,\r
+                0.0,\r
+                0.0014864075928926468,\r
+                0.9880954027175903,\r
+                -0.15383504331111909,\r
+                0.0,\r
+                -0.009547080844640732,\r
+                0.15384222567081452,\r
+                0.988049328327179,\r
+                0.0,\r
+                -0.7599077224731445,\r
+                7.708760738372803,\r
+                27.743375778198243,\r
+                1.0\r
+            ],\r
+            "camera": 0,\r
+            "name": "render_camera_n3d"\r
+        },\r
+        {\r
+            "name": "ground_plane_n3d"\r
+        },\r
+        {\r
+            "children": [\r
+                3,\r
+                4,\r
+                5\r
+            ],\r
+            "matrix": [\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                -0.5564079284667969,\r
+                4.774584770202637,\r
+                -1.0962677001953126,\r
+                1.0\r
+            ],\r
+            "name": "ENV_Spheres"\r
+        },\r
+        {\r
+            "mesh": 0,\r
+            "name": "Metallic0"\r
+        },\r
+        {\r
+            "mesh": 1,\r
+            "name": "Dielectric0"\r
+        },\r
+        {\r
+            "matrix": [\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                0.0,\r
+                1.0,\r
+                0.0,\r
+                0.0,\r
+                -1.985867977142334,\r
+                0.0,\r
+                1.0\r
+            ],\r
+            "mesh": 2,\r
+            "name": "Dielectric0-Black"\r
+        }\r
+    ],\r
+    "samplers": [\r
+        {},\r
+        {}\r
+    ],\r
+    "scenes": [\r
+        {\r
+            "nodes": [\r
+                0,\r
+                1,\r
+                2\r
+            ],\r
+            "name": "scene"\r
+        }\r
+    ],\r
+    "textures": [\r
+        {\r
+            "name": "tmp_image_pie_dc1e_1a22_fbf9roughness_map_roughness_tmp_image_pie_dc1e_1a22_fbf9metal_map_metallic_0_texture",\r
+            "sampler": 0,\r
+            "source": 0\r
+        },\r
+        {\r
+            "name": "tmp_image_pie_b20b_ebb4_317droughness_map2_roughness_tmp_image_pie_b20b_ebb4_317dmetal_map2_metallic_1_texture",\r
+            "sampler": 1,\r
+            "source": 1\r
+        }\r
+    ],\r
+    "scene": 0\r
+}
\ No newline at end of file
diff --git a/automated-tests/resources/EnvironmentTest_binary.bin b/automated-tests/resources/EnvironmentTest_binary.bin
new file mode 100644 (file)
index 0000000..4473a85
Binary files /dev/null and b/automated-tests/resources/EnvironmentTest_binary.bin differ
diff --git a/automated-tests/resources/MetalRoughSpheres.gltf b/automated-tests/resources/MetalRoughSpheres.gltf
new file mode 100644 (file)
index 0000000..7ea5ba0
--- /dev/null
@@ -0,0 +1,507 @@
+{\r
+    "asset": {\r
+        "copyright": "Copyright 2017 Analytical Graphics, Inc, CC-BY 4.0 https://creativecommons.org/licenses/by/4.0/ - Model and textures by Ed Mackey.",\r
+        "generator": "COLLADA2GLTF with hand-edits",\r
+        "version": "2.0"\r
+    },\r
+    "scene": 0,\r
+    "scenes": [\r
+        {\r
+            "nodes": [\r
+                0\r
+            ]\r
+        }\r
+    ],\r
+    "nodes": [\r
+        {\r
+            "children": [\r
+                5,\r
+                4,\r
+                3,\r
+                2,\r
+                1\r
+            ],\r
+            "matrix": [\r
+                0.4,  0.0,  0.0,  0.0,\r
+                0.0,  0.0, -0.4,  0.0,\r
+                0.0,  0.4,  0.0,  0.0,\r
+                0.0,  0.0,  0.0,  1.0\r
+            ]\r
+        },\r
+        {\r
+            "mesh": 0\r
+        },\r
+        {\r
+            "mesh": 1\r
+        },\r
+        {\r
+            "mesh": 2\r
+        },\r
+        {\r
+            "mesh": 3\r
+        },\r
+        {\r
+            "mesh": 4\r
+        }\r
+    ],\r
+    "meshes": [\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 1,\r
+                        "POSITION": 2,\r
+                        "TEXCOORD_0": 3\r
+                    },\r
+                    "indices": 0,\r
+                    "mode": 4,\r
+                    "material": 0\r
+                }\r
+            ],\r
+            "name": "Spheres.004"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 5,\r
+                        "POSITION": 6,\r
+                        "TEXCOORD_0": 7\r
+                    },\r
+                    "indices": 4,\r
+                    "mode": 4,\r
+                    "material": 0\r
+                }\r
+            ],\r
+            "name": "Spheres.003"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 9,\r
+                        "POSITION": 10,\r
+                        "TEXCOORD_0": 11\r
+                    },\r
+                    "indices": 8,\r
+                    "mode": 4,\r
+                    "material": 0\r
+                }\r
+            ],\r
+            "name": "Spheres.002"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 13,\r
+                        "POSITION": 14,\r
+                        "TEXCOORD_0": 15\r
+                    },\r
+                    "indices": 12,\r
+                    "mode": 4,\r
+                    "material": 0\r
+                }\r
+            ],\r
+            "name": "Spheres.001"\r
+        },\r
+        {\r
+            "primitives": [\r
+                {\r
+                    "attributes": {\r
+                        "NORMAL": 17,\r
+                        "POSITION": 18,\r
+                        "TEXCOORD_0": 19\r
+                    },\r
+                    "indices": 16,\r
+                    "mode": 4,\r
+                    "material": 0\r
+                }\r
+            ],\r
+            "name": "Spheres"\r
+        }\r
+    ],\r
+    "accessors": [\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 2642016,\r
+            "componentType": 5123,\r
+            "count": 184320,\r
+            "max": [\r
+                31331\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 5389968,\r
+            "componentType": 5126,\r
+            "count": 31332,\r
+            "max": [\r
+                0.9999999403953552,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -0.9999999403953552,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 5765952,\r
+            "componentType": 5126,\r
+            "count": 31332,\r
+            "max": [\r
+                -8.0,\r
+                9.0,\r
+                10.0\r
+            ],\r
+            "min": [\r
+                -10.0,\r
+                -1.0,\r
+                -7.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 2,\r
+            "byteOffset": 1796656,\r
+            "componentType": 5126,\r
+            "count": 31332,\r
+            "max": [\r
+                0.1278132051229477,\r
+                0.7597609758377075\r
+            ],\r
+            "min": [\r
+                0.03436123952269554,\r
+                0.013921022415161133\r
+            ],\r
+            "type": "VEC2"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 0,\r
+            "componentType": 5123,\r
+            "count": 368640,\r
+            "max": [\r
+                62663\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 0,\r
+            "componentType": 5126,\r
+            "count": 62664,\r
+            "max": [\r
+                0.9999999403953552,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -0.9999999403953552,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 751968,\r
+            "componentType": 5126,\r
+            "count": 62664,\r
+            "max": [\r
+                -2.0,\r
+                9.0,\r
+                10.0\r
+            ],\r
+            "min": [\r
+                -7.0,\r
+                -1.0,\r
+                -7.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 2,\r
+            "byteOffset": 0,\r
+            "componentType": 5126,\r
+            "count": 62664,\r
+            "max": [\r
+                0.4161497056484223,\r
+                0.7597609758377075\r
+            ],\r
+            "min": [\r
+                0.15740810334682465,\r
+                0.012456059455871582\r
+            ],\r
+            "type": "VEC2"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 1904736,\r
+            "componentType": 5123,\r
+            "count": 368640,\r
+            "max": [\r
+                62663\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 3886032,\r
+            "componentType": 5126,\r
+            "count": 62664,\r
+            "max": [\r
+                0.9999999403953552,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -0.9999999403953552,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 4638000,\r
+            "componentType": 5126,\r
+            "count": 62664,\r
+            "max": [\r
+                4.0,\r
+                9.0,\r
+                10.0\r
+            ],\r
+            "min": [\r
+                -1.0,\r
+                -1.0,\r
+                -7.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 2,\r
+            "byteOffset": 1295344,\r
+            "componentType": 5126,\r
+            "count": 62664,\r
+            "max": [\r
+                0.7028239369392395,\r
+                0.7636672854423523\r
+            ],\r
+            "min": [\r
+                0.4482637047767639,\r
+                0.015471160411834717\r
+            ],\r
+            "type": "VEC2"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 1167456,\r
+            "componentType": 5123,\r
+            "count": 368640,\r
+            "max": [\r
+                62663\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 2382096,\r
+            "componentType": 5126,\r
+            "count": 62664,\r
+            "max": [\r
+                0.9999999403953552,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -0.9999999403953552,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 3134064,\r
+            "componentType": 5126,\r
+            "count": 62664,\r
+            "max": [\r
+                10.0,\r
+                9.0,\r
+                10.0\r
+            ],\r
+            "min": [\r
+                5.0,\r
+                -1.0,\r
+                -7.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 2,\r
+            "byteOffset": 794032,\r
+            "componentType": 5126,\r
+            "count": 62664,\r
+            "max": [\r
+                0.9852716326713562,\r
+                0.7671433687210083\r
+            ],\r
+            "min": [\r
+                0.7233805060386658,\r
+                0.016381680965423585\r
+            ],\r
+            "type": "VEC2"\r
+        },\r
+        {\r
+            "bufferView": 0,\r
+            "byteOffset": 737280,\r
+            "componentType": 5123,\r
+            "count": 215088,\r
+            "max": [\r
+                36589\r
+            ],\r
+            "min": [\r
+                0\r
+            ],\r
+            "type": "SCALAR"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1503936,\r
+            "componentType": 5126,\r
+            "count": 36590,\r
+            "max": [\r
+                0.9999999403953552,\r
+                1.0,\r
+                1.0\r
+            ],\r
+            "min": [\r
+                -0.9999999403953552,\r
+                -1.0,\r
+                -1.0\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 1,\r
+            "byteOffset": 1943016,\r
+            "componentType": 5126,\r
+            "count": 36590,\r
+            "max": [\r
+                11.874730110168456,\r
+                9.0,\r
+                10.969940185546877\r
+            ],\r
+            "min": [\r
+                -12.186589241027832,\r
+                -1.0,\r
+                -12.35944938659668\r
+            ],\r
+            "type": "VEC3"\r
+        },\r
+        {\r
+            "bufferView": 2,\r
+            "byteOffset": 501312,\r
+            "componentType": 5126,\r
+            "count": 36590,\r
+            "max": [\r
+                0.9869875311851502,\r
+                0.9988328814506532\r
+            ],\r
+            "min": [\r
+                0.0013856289442628625,\r
+                0.016568005084991456\r
+            ],\r
+            "type": "VEC2"\r
+        }\r
+    ],\r
+    "materials": [\r
+        {\r
+            "pbrMetallicRoughness": {\r
+                "baseColorTexture": {\r
+                    "index": 0\r
+                },\r
+                "metallicRoughnessTexture": {\r
+                    "index": 1\r
+                }\r
+            },\r
+            "emissiveFactor": [\r
+                0.0,\r
+                0.0,\r
+                0.0\r
+            ]\r
+        }\r
+    ],\r
+    "textures": [\r
+        {\r
+            "sampler": 0,\r
+            "source": 0\r
+        },\r
+        {\r
+            "sampler": 0,\r
+            "source": 1\r
+        }\r
+    ],\r
+    "images": [\r
+        {\r
+            "uri": "Spheres_BaseColor.png"\r
+        },\r
+        {\r
+            "uri": "Spheres_MetalRough.png"\r
+        }\r
+    ],\r
+    "samplers": [\r
+        {\r
+            "magFilter": 9729,\r
+            "minFilter": 9986,\r
+            "wrapS": 33071,\r
+            "wrapT": 33071\r
+        }\r
+    ],\r
+    "bufferViews": [\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 8189248,\r
+            "byteLength": 3010656,\r
+            "target": 34963\r
+        },\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 0,\r
+            "byteLength": 6141936,\r
+            "byteStride": 12,\r
+            "target": 34962\r
+        },\r
+        {\r
+            "buffer": 0,\r
+            "byteOffset": 6141936,\r
+            "byteLength": 2047312,\r
+            "byteStride": 8,\r
+            "target": 34962\r
+        }\r
+    ],\r
+    "buffers": [\r
+        {\r
+            "byteLength": 11199904,\r
+            "uri": "MetalRoughSpheres0.bin"\r
+        }\r
+    ]\r
+}\r
diff --git a/automated-tests/resources/MetalRoughSpheres0.bin b/automated-tests/resources/MetalRoughSpheres0.bin
new file mode 100644 (file)
index 0000000..2823ba4
Binary files /dev/null and b/automated-tests/resources/MetalRoughSpheres0.bin differ
diff --git a/automated-tests/resources/MorphPrimitivesTest.bin b/automated-tests/resources/MorphPrimitivesTest.bin
new file mode 100644 (file)
index 0000000..7f269e1
Binary files /dev/null and b/automated-tests/resources/MorphPrimitivesTest.bin differ
diff --git a/automated-tests/resources/MorphPrimitivesTest.gltf b/automated-tests/resources/MorphPrimitivesTest.gltf
new file mode 100644 (file)
index 0000000..a987f2d
--- /dev/null
@@ -0,0 +1,331 @@
+{\r
+  "asset": {\r
+    "generator": "glTF Converter for Shade3D",\r
+    "version": "2.0",\r
+    "extras": {\r
+      "title": "multiple_primitives",\r
+      "author": "ft-lab",\r
+      "license": "CC BY-4.0 (https://creativecommons.org/licenses/by/4.0/)"\r
+    }\r
+  },\r
+  "accessors": [\r
+    {\r
+      "bufferView": 0,\r
+      "componentType": 5123,\r
+      "count": 72,\r
+      "type": "SCALAR",\r
+      "byteOffset": 0\r
+    },\r
+    {\r
+      "bufferView": 1,\r
+      "componentType": 5126,\r
+      "count": 21,\r
+      "type": "VEC3",\r
+      "byteOffset": 0\r
+    },\r
+    {\r
+      "bufferView": 2,\r
+      "componentType": 5126,\r
+      "count": 21,\r
+      "type": "VEC3",\r
+      "max": [\r
+        0.5,\r
+        0,\r
+        0.5\r
+      ],\r
+      "min": [\r
+        -0.5,\r
+        0,\r
+        -0.5\r
+      ],\r
+      "byteOffset": 0\r
+    },\r
+    {\r
+      "bufferView": 3,\r
+      "componentType": 5126,\r
+      "count": 21,\r
+      "type": "VEC2",\r
+      "byteOffset": 0\r
+    },\r
+    {\r
+      "bufferView": 4,\r
+      "componentType": 5126,\r
+      "count": 21,\r
+      "type": "VEC3",\r
+      "max": [\r
+        0,\r
+        0.20000000298023224,\r
+        0\r
+      ],\r
+      "min": [\r
+        0,\r
+        0,\r
+        0\r
+      ],\r
+      "byteOffset": 0\r
+    },\r
+    {\r
+      "bufferView": 5,\r
+      "componentType": 5123,\r
+      "count": 24,\r
+      "type": "SCALAR",\r
+      "byteOffset": 0\r
+    },\r
+    {\r
+      "bufferView": 6,\r
+      "componentType": 5126,\r
+      "count": 9,\r
+      "type": "VEC3",\r
+      "byteOffset": 0\r
+    },\r
+    {\r
+      "bufferView": 7,\r
+      "componentType": 5126,\r
+      "count": 9,\r
+      "type": "VEC3",\r
+      "max": [\r
+        0.5,\r
+        0,\r
+        0\r
+      ],\r
+      "min": [\r
+        0,\r
+        0,\r
+        -0.5\r
+      ],\r
+      "byteOffset": 0\r
+    },\r
+    {\r
+      "bufferView": 8,\r
+      "componentType": 5126,\r
+      "count": 9,\r
+      "type": "VEC2",\r
+      "byteOffset": 0\r
+    },\r
+    {\r
+      "bufferView": 9,\r
+      "componentType": 5126,\r
+      "count": 9,\r
+      "type": "VEC3",\r
+      "max": [\r
+        0,\r
+        0.20000000298023224,\r
+        0\r
+      ],\r
+      "min": [\r
+        0,\r
+        0,\r
+        0\r
+      ],\r
+      "byteOffset": 0\r
+    }\r
+  ],\r
+  "bufferViews": [\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 0,\r
+      "byteLength": 144,\r
+      "target": 34963\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 144,\r
+      "byteLength": 252,\r
+      "target": 34962,\r
+      "byteStride": 12\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 396,\r
+      "byteLength": 252,\r
+      "target": 34962,\r
+      "byteStride": 12\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 648,\r
+      "byteLength": 168,\r
+      "target": 34962,\r
+      "byteStride": 8\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 816,\r
+      "byteLength": 252,\r
+      "target": 34962,\r
+      "byteStride": 12\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 1068,\r
+      "byteLength": 48,\r
+      "target": 34963\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 1116,\r
+      "byteLength": 108,\r
+      "target": 34962,\r
+      "byteStride": 12\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 1224,\r
+      "byteLength": 108,\r
+      "target": 34962,\r
+      "byteStride": 12\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 1332,\r
+      "byteLength": 72,\r
+      "target": 34962,\r
+      "byteStride": 8\r
+    },\r
+    {\r
+      "buffer": 0,\r
+      "byteOffset": 1404,\r
+      "byteLength": 108,\r
+      "target": 34962,\r
+      "byteStride": 12\r
+    }\r
+  ],\r
+  "buffers": [\r
+    {\r
+      "name": "MorphPrimitivesTest",\r
+      "byteLength": 1512,\r
+      "uri": "MorphPrimitivesTest.bin"\r
+    }\r
+  ],\r
+  "images": [\r
+    {\r
+      "name": "uv_texture.jpg",\r
+      "mimeType": "image/jpeg",\r
+      "uri": "uv_texture.jpg"\r
+    }\r
+  ],\r
+  "materials": [\r
+    {\r
+      "pbrMetallicRoughness": {\r
+        "baseColorFactor": [\r
+          1,\r
+          0,\r
+          0,\r
+          1\r
+        ],\r
+        "baseColorTexture": {\r
+          "index": 0,\r
+          "texCoord": 0\r
+        },\r
+        "metallicFactor": 0,\r
+        "roughnessFactor": 1\r
+      },\r
+      "name": "red",\r
+      "emissiveFactor": [\r
+        0,\r
+        0,\r
+        0\r
+      ],\r
+      "alphaMode": "OPAQUE",\r
+      "doubleSided": false\r
+    },\r
+    {\r
+      "pbrMetallicRoughness": {\r
+        "baseColorFactor": [\r
+          0,\r
+          1,\r
+          0,\r
+          1\r
+        ],\r
+        "baseColorTexture": {\r
+          "index": 0,\r
+          "texCoord": 0\r
+        },\r
+        "metallicFactor": 0,\r
+        "roughnessFactor": 1\r
+      },\r
+      "name": "green",\r
+      "emissiveFactor": [\r
+        0,\r
+        0,\r
+        0\r
+      ],\r
+      "alphaMode": "OPAQUE",\r
+      "doubleSided": false\r
+    }\r
+  ],\r
+  "meshes": [\r
+    {\r
+      "weights": [\r
+        0.5\r
+      ],\r
+      "name": "mesh",\r
+      "primitives": [\r
+        {\r
+          "attributes": {\r
+            "NORMAL": 1,\r
+            "POSITION": 2,\r
+            "TEXCOORD_0": 3\r
+          },\r
+          "indices": 0,\r
+          "material": 0,\r
+          "targets": [\r
+            {\r
+              "POSITION": 4\r
+            }\r
+          ],\r
+          "mode": 4\r
+        },\r
+        {\r
+          "attributes": {\r
+            "POSITION": 7,\r
+            "NORMAL": 6,\r
+            "TEXCOORD_0": 8\r
+          },\r
+          "indices": 5,\r
+          "material": 1,\r
+          "targets": [\r
+            {\r
+              "POSITION": 9\r
+            }\r
+          ],\r
+          "mode": 4\r
+        }\r
+      ]\r
+    }\r
+  ],\r
+  "nodes": [\r
+    {\r
+      "children": [\r
+        1\r
+      ],\r
+      "name": "ルートパート"\r
+    },\r
+    {\r
+      "mesh": 0,\r
+      "name": "mesh"\r
+    }\r
+  ],\r
+  "samplers": [\r
+    {\r
+      "minFilter": 9729,\r
+      "wrapS": 10497,\r
+      "wrapT": 10497\r
+    }\r
+  ],\r
+  "scenes": [\r
+    {\r
+      "nodes": [\r
+        0\r
+      ],\r
+      "name": "Scene"\r
+    }\r
+  ],\r
+  "textures": [\r
+    {\r
+      "sampler": 0,\r
+      "source": 0\r
+    }\r
+  ],\r
+  "scene": 0\r
+}\r
diff --git a/automated-tests/resources/RGB16F.ktx b/automated-tests/resources/RGB16F.ktx
new file mode 100644 (file)
index 0000000..9e9714b
Binary files /dev/null and b/automated-tests/resources/RGB16F.ktx differ
diff --git a/automated-tests/resources/RGB32F.ktx b/automated-tests/resources/RGB32F.ktx
new file mode 100644 (file)
index 0000000..99f5e7f
Binary files /dev/null and b/automated-tests/resources/RGB32F.ktx differ
diff --git a/automated-tests/resources/RGBA8888.ktx b/automated-tests/resources/RGBA8888.ktx
new file mode 100644 (file)
index 0000000..48afa5d
Binary files /dev/null and b/automated-tests/resources/RGBA8888.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_10x10.ktx b/automated-tests/resources/RGBA_ASTC_10x10.ktx
new file mode 100644 (file)
index 0000000..94dde71
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_10x10.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_10x5.ktx b/automated-tests/resources/RGBA_ASTC_10x5.ktx
new file mode 100644 (file)
index 0000000..315674e
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_10x5.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_10x6.ktx b/automated-tests/resources/RGBA_ASTC_10x6.ktx
new file mode 100644 (file)
index 0000000..b5f1cbb
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_10x6.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_12x10.ktx b/automated-tests/resources/RGBA_ASTC_12x10.ktx
new file mode 100644 (file)
index 0000000..c3c04fa
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_12x10.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_12x12.ktx b/automated-tests/resources/RGBA_ASTC_12x12.ktx
new file mode 100644 (file)
index 0000000..c1e5e0d
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_12x12.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_4x4.ktx b/automated-tests/resources/RGBA_ASTC_4x4.ktx
new file mode 100644 (file)
index 0000000..fa7fe6b
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_4x4.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_5x4.ktx b/automated-tests/resources/RGBA_ASTC_5x4.ktx
new file mode 100644 (file)
index 0000000..9a4372c
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_5x4.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_5x5.ktx b/automated-tests/resources/RGBA_ASTC_5x5.ktx
new file mode 100644 (file)
index 0000000..776175b
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_5x5.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_6x5.ktx b/automated-tests/resources/RGBA_ASTC_6x5.ktx
new file mode 100644 (file)
index 0000000..2b10f9d
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_6x5.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_6x6.ktx b/automated-tests/resources/RGBA_ASTC_6x6.ktx
new file mode 100644 (file)
index 0000000..27f511a
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_6x6.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_8x5.ktx b/automated-tests/resources/RGBA_ASTC_8x5.ktx
new file mode 100644 (file)
index 0000000..7bbb4e0
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_8x5.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_8x6.ktx b/automated-tests/resources/RGBA_ASTC_8x6.ktx
new file mode 100644 (file)
index 0000000..2836beb
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_8x6.ktx differ
diff --git a/automated-tests/resources/RGBA_ASTC_8x8.ktx b/automated-tests/resources/RGBA_ASTC_8x8.ktx
new file mode 100644 (file)
index 0000000..c1bc85a
Binary files /dev/null and b/automated-tests/resources/RGBA_ASTC_8x8.ktx differ
diff --git a/automated-tests/resources/SimpleSparseAccessor.bin b/automated-tests/resources/SimpleSparseAccessor.bin
new file mode 100644 (file)
index 0000000..99eaf33
Binary files /dev/null and b/automated-tests/resources/SimpleSparseAccessor.bin differ
diff --git a/automated-tests/resources/SimpleSparseAccessor.gltf b/automated-tests/resources/SimpleSparseAccessor.gltf
new file mode 100644 (file)
index 0000000..918cd48
--- /dev/null
@@ -0,0 +1,106 @@
+{\r
+   "scene" : 0,\r
+   "scenes":[\r
+      {\r
+         "nodes":[\r
+            0\r
+         ]\r
+      }\r
+   ],\r
+   "nodes":[\r
+      {\r
+         "mesh":0\r
+      }\r
+   ],\r
+   "meshes":[\r
+      {\r
+         "primitives":[\r
+            {\r
+               "attributes":{\r
+                  "POSITION":1\r
+               },\r
+               "indices":0\r
+            }\r
+         ]\r
+      }\r
+   ],\r
+   "buffers":[\r
+      {\r
+         "uri":"SimpleSparseAccessor.bin",\r
+         "byteLength":284\r
+      }\r
+   ],\r
+   "bufferViews":[\r
+      {\r
+         "buffer":0,\r
+         "byteOffset":0,\r
+         "byteLength":72,\r
+         "target":34963\r
+      },\r
+      {\r
+         "buffer":0,\r
+         "byteOffset":72,\r
+         "byteLength":168\r
+      },\r
+      {\r
+         "buffer":0,\r
+         "byteOffset":240,\r
+         "byteLength":6\r
+      },\r
+      {\r
+         "buffer":0,\r
+         "byteOffset":248,\r
+         "byteLength":36\r
+      }\r
+   ],\r
+   "accessors":[\r
+      {\r
+         "bufferView":0,\r
+         "byteOffset":0,\r
+         "componentType":5123,\r
+         "count":36,\r
+         "type":"SCALAR",\r
+         "max":[\r
+            13\r
+         ],\r
+         "min":[\r
+            0\r
+         ]\r
+      },\r
+      {\r
+         "bufferView":1,\r
+         "byteOffset":0,\r
+         "componentType":5126,\r
+         "count":14,\r
+         "type":"VEC3",\r
+         "sparse":{\r
+            "count":3,\r
+            "indices":{\r
+               "bufferView":2,\r
+               "byteOffset":0,\r
+               "componentType":5123\r
+            },\r
+            "values":{\r
+               "bufferView":3,\r
+               "byteOffset":0\r
+            }\r
+         },\r
+         "max":[\r
+            6.0,\r
+            4.0,\r
+            0.0\r
+         ],\r
+         "min":[\r
+            0.0,\r
+            0.0,\r
+            0.0\r
+         ]\r
+      }\r
+   ],\r
+   "asset":{\r
+      "version":"2.0"\r
+   }\r
+}\r
+\r
+\r
+\r
diff --git a/automated-tests/resources/Studio/Irradiance.ktx b/automated-tests/resources/Studio/Irradiance.ktx
new file mode 100644 (file)
index 0000000..df24c05
Binary files /dev/null and b/automated-tests/resources/Studio/Irradiance.ktx differ
diff --git a/automated-tests/resources/Studio/Radiance.ktx b/automated-tests/resources/Studio/Radiance.ktx
new file mode 100644 (file)
index 0000000..51aa681
Binary files /dev/null and b/automated-tests/resources/Studio/Radiance.ktx differ
diff --git a/automated-tests/resources/animation.bin b/automated-tests/resources/animation.bin
new file mode 100644 (file)
index 0000000..fa20e9f
Binary files /dev/null and b/automated-tests/resources/animation.bin differ
diff --git a/automated-tests/resources/arc.dli b/automated-tests/resources/arc.dli
new file mode 100644 (file)
index 0000000..4d0c6b5
--- /dev/null
@@ -0,0 +1,35 @@
+{\r
+  "scene": 0,\r
+  "scenes": [ { "nodes": [ 0 ] } ],\r
+  "nodes": [ {\r
+    "name": "root",\r
+    "children": [ 1 ]\r
+  }, {\r
+    "name": "arc",\r
+    "size": [ 2.0, 2.0 ],\r
+       "color": [ 0.5, 1.0, 0.5, 0.5 ],\r
+    "arc": {\r
+      "shader": 0,\r
+      "mesh": 0,\r
+      "startAngle" : -45.0,\r
+      "endAngle" : 135.0,\r
+      "radius" : -0.75,\r
+         "arcCaps": 3\r
+    }\r
+  } ],\r
+  "environment": [ {\r
+  } ],\r
+  "meshes": [ {\r
+    "uri": "quad",\r
+    "attributes": 0\r
+  } ],\r
+  "materials": [ {\r
+  } ],\r
+  "shaders": [ {\r
+    "vertex": "dli_arc.vsh",\r
+    "fragment": "dli_arc.fsh",\r
+    "rendererState" : "ALPHA_BLEND",\r
+       "startAngle": [ -1.0, 0.0 ],\r
+       "endAngle": [ 1.0, 0.0 ]\r
+  } ]\r
+}\r
diff --git a/automated-tests/resources/dli/animation-failed-to-open.dli b/automated-tests/resources/dli/animation-failed-to-open.dli
new file mode 100644 (file)
index 0000000..9cd0602
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {
+    "name": "root"
+  } ],
+  "animations": [ {
+    "properties": [ {
+      "node": "root",
+      "property": "position",
+      "keyFramesBin": { "url": "doesntexist.bin" }
+    } ]
+  } ]
+}
diff --git a/automated-tests/resources/dli/constraints.dli b/automated-tests/resources/dli/constraints.dli
new file mode 100644 (file)
index 0000000..200bd88
--- /dev/null
@@ -0,0 +1,32 @@
+{
+  "scene": 0,
+  "scenes": [ { "nodes": [ 0 ] } ],
+  "nodes": [ {
+    "name": "root",
+    "children": [ 1, 2, 3 ]
+  }, {
+    "name": "Alice",
+    "angle": [ 0.0, -45.0, 10.0 ],
+    "position": [ 500.0, -50.0, 0.0 ]
+  }, {
+    "name": "Bob",
+    "position": [ 1000.0, 1.0, -400.0 ],
+    "angle": [ 10.0, 0.0, -90.0 ],
+    "extras": {
+      "angularVelocity": [ -0.5, 0.0004 ]
+    }
+  }, {
+    "name": "Charlie",
+    "position": [ -400.0, 0.0, 100.0 ],
+    "constraints": {
+      "orientation": 1,
+      "position": 2,
+      "angularVelocity": 2,
+      "invalid": "zero"
+    },
+    "extras": {
+      "angularVelocity": [ 0.0, 0.0 ]
+    }
+  } ]
+}
+
diff --git a/automated-tests/resources/dli/extras.dli b/automated-tests/resources/dli/extras.dli
new file mode 100644 (file)
index 0000000..4f5edab
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "scene": 0,
+  "scenes": [ { "nodes": [ 0 ] } ],
+  "nodes": [ {
+    "name": "root",
+    "extras": {
+      "fudgeFactor": 9000.1,
+      "fudgeVector": [ 8, 7.0 ],
+      "fudgeVector": [ -0.25, 17.0 ],
+      "isThisTheRealLife": true,
+      "isThisJustFantasy": false,
+      "": 16384,
+      "velocity": [ 0.1, 58.0, -0.2 ],
+      "not a great extra": "this one",
+      "frameOfReference": [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ]
+    }
+  } ]
+}
+
diff --git a/automated-tests/resources/dli/material-environment-out-of-bounds.dli b/automated-tests/resources/dli/material-environment-out-of-bounds.dli
new file mode 100644 (file)
index 0000000..effdcf2
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ { "name": "root" } ],
+  "materials": [ {
+    "environment": 0
+  } ]
+}
diff --git a/automated-tests/resources/dli/mesh-indices-read-fail.dli b/automated-tests/resources/dli/mesh-indices-read-fail.dli
new file mode 100644 (file)
index 0000000..b911f0f
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {} ],
+  "meshes": [ {
+    "uri": "mesh.bin",
+       "attributes": 1
+  } ]
+}
+
diff --git a/automated-tests/resources/dli/mesh-positions-read-fail.dli b/automated-tests/resources/dli/mesh-positions-read-fail.dli
new file mode 100644 (file)
index 0000000..1e98fda
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {} ],
+  "meshes": [ {
+    "uri": "mesh.bin",
+       "attributes": 2
+  } ]
+}
diff --git a/automated-tests/resources/dli/mesh-uri-missing.dli b/automated-tests/resources/dli/mesh-uri-missing.dli
new file mode 100644 (file)
index 0000000..26af1e4
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {} ],
+  "meshes": [ {
+  } ]
+}
diff --git a/automated-tests/resources/dli/node-animated-image-mesh-missing.dli b/automated-tests/resources/dli/node-animated-image-mesh-missing.dli
new file mode 100644 (file)
index 0000000..85c3edf
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {
+    "name": "root",
+    "model": { }
+  } ]
+}
diff --git a/automated-tests/resources/dli/node-arc-mesh-missing.dli b/automated-tests/resources/dli/node-arc-mesh-missing.dli
new file mode 100644 (file)
index 0000000..69b8ed4
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {
+    "name": "root",
+    "arc": { }
+  } ]
+}
diff --git a/automated-tests/resources/dli/node-child-invalid-type.dli b/automated-tests/resources/dli/node-child-invalid-type.dli
new file mode 100644 (file)
index 0000000..49a719f
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {
+    "name": "root",
+    "children": [
+      "what?"
+    ]
+  } ]
+}
diff --git a/automated-tests/resources/dli/node-model-mesh-missing.dli b/automated-tests/resources/dli/node-model-mesh-missing.dli
new file mode 100644 (file)
index 0000000..85c3edf
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {
+    "name": "root",
+    "model": { }
+  } ]
+}
diff --git a/automated-tests/resources/dli/node-name-already-used.dli b/automated-tests/resources/dli/node-name-already-used.dli
new file mode 100644 (file)
index 0000000..f176e3a
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {
+    "name": "root",
+    "children" : [ 1 ]
+  }, {
+    "name": "root"
+  } ]
+}
diff --git a/automated-tests/resources/dli/node-processor.dli b/automated-tests/resources/dli/node-processor.dli
new file mode 100644 (file)
index 0000000..2b069ad
--- /dev/null
@@ -0,0 +1,21 @@
+{
+  "scene": 0,
+  "scenes": [ { "nodes": [ 0 ] }, { "nodes": [ 1 ] } ],
+  "nodes": [ {
+    "name": "rootA",
+    "array": [ 1, 2, 4, 8, -500 ],
+    "object": {
+      "physics": true,
+      "elasticity": 0.27,
+      "drag": 0.91,
+      "inner array": [ "why", "not", false ],
+      "inner object": {
+        "supported": true
+      }
+    },
+    "nickname": "same as name",
+    "favourite number": 63478
+  }, {
+    "name": "rootB"
+  } ]
+}
diff --git a/automated-tests/resources/dli/node-renderable-mesh-invalid-type.dli b/automated-tests/resources/dli/node-renderable-mesh-invalid-type.dli
new file mode 100644 (file)
index 0000000..96cb99c
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {
+    "name": "root",
+    "model": { 
+      "mesh": 0.5
+    }
+  } ]
+}
diff --git a/automated-tests/resources/dli/node-renderable-mesh-out-of-bounds.dli b/automated-tests/resources/dli/node-renderable-mesh-out-of-bounds.dli
new file mode 100644 (file)
index 0000000..0b48607
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {
+    "name": "root",
+    "model": {
+      "mesh": 0
+    }
+  } ]
+}
diff --git a/automated-tests/resources/dli/nodes-array-empty.dli b/automated-tests/resources/dli/nodes-array-empty.dli
new file mode 100644 (file)
index 0000000..9a8674d
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "scenes": [ { "nodes" : [] } ],
+  "nodes": []
+}
diff --git a/automated-tests/resources/dli/nodes-invalid-type.dli b/automated-tests/resources/dli/nodes-invalid-type.dli
new file mode 100644 (file)
index 0000000..97fe920
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "scenes": [ { "nodes" : 0 } ],
+  "nodes": []
+}
diff --git a/automated-tests/resources/dli/nodes-missing.dli b/automated-tests/resources/dli/nodes-missing.dli
new file mode 100644 (file)
index 0000000..27504c8
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "scenes": []
+}
diff --git a/automated-tests/resources/dli/root-id-invalid.dli b/automated-tests/resources/dli/root-id-invalid.dli
new file mode 100644 (file)
index 0000000..e6a8608
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "scenes": [ { "nodes" : [ "0" ] } ],
+  "nodes": []
+}
diff --git a/automated-tests/resources/dli/root-id-out-of-bounds.dli b/automated-tests/resources/dli/root-id-out-of-bounds.dli
new file mode 100644 (file)
index 0000000..0549b0b
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": []
+}
diff --git a/automated-tests/resources/dli/root-node-invalid-type.dli b/automated-tests/resources/dli/root-node-invalid-type.dli
new file mode 100644 (file)
index 0000000..e43c9ea
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ "helloo" ]
+}
diff --git a/automated-tests/resources/dli/scene-out-of-bounds.dli b/automated-tests/resources/dli/scene-out-of-bounds.dli
new file mode 100644 (file)
index 0000000..40189a0
--- /dev/null
@@ -0,0 +1,4 @@
+{
+  "scenes": [],
+  "nodes": []
+}
diff --git a/automated-tests/resources/dli/scenes-missing.dli b/automated-tests/resources/dli/scenes-missing.dli
new file mode 100644 (file)
index 0000000..1b74fa4
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "": {}
+}
diff --git a/automated-tests/resources/dli/scenes-nodes-missing.dli b/automated-tests/resources/dli/scenes-nodes-missing.dli
new file mode 100644 (file)
index 0000000..e7738fb
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "scenes": [ { } ]
+}
diff --git a/automated-tests/resources/dli/shader-fragment-missing.dli b/automated-tests/resources/dli/shader-fragment-missing.dli
new file mode 100644 (file)
index 0000000..9e7a9e4
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {} ],
+  "shaders": [ {
+    "vertex": "some.vsh"
+  } ]
+}
diff --git a/automated-tests/resources/dli/shader-uniforms.dli b/automated-tests/resources/dli/shader-uniforms.dli
new file mode 100644 (file)
index 0000000..1573fe2
--- /dev/null
@@ -0,0 +1,20 @@
+{
+  "scene": 0,
+  "scenes": [ { "nodes": [ 0 ] } ],
+  "nodes": [ {
+    "name": "root"
+  } ],
+  "shaders": [ {
+    "vertex": "dli_pbr.vsh",
+    "fragment": "dli_pbr.fsh",
+    "uBool": true,
+    "uInt": 255,
+    "uFloat": -0.5,
+    "uVec2": [ 100.0, -100.0 ],
+    "uVec3": [ 50.0, 0.0, -200.0 ],
+    "uVec4": [ 0.1774, 1.0, 0.5333, 0.7997 ],
+    "uMat3": [ 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0 ],
+    "uMat4": [ 8.0, 0.0, 0.0, 0.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 8.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+    "invalid": "Hello, I'm a string"
+  } ]
+}
diff --git a/automated-tests/resources/dli/shader-vertex-missing.dli b/automated-tests/resources/dli/shader-vertex-missing.dli
new file mode 100644 (file)
index 0000000..4f28012
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ { "name": "root" } ],
+  "shaders": [ {} ]
+}
diff --git a/automated-tests/resources/dli/skeleton-node-missing.dli b/automated-tests/resources/dli/skeleton-node-missing.dli
new file mode 100644 (file)
index 0000000..8940f5f
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ {} ],
+  "skeletons": [ {} ]
+}
diff --git a/automated-tests/resources/dli/skeleton-root-not-found.dli b/automated-tests/resources/dli/skeleton-root-not-found.dli
new file mode 100644 (file)
index 0000000..2b6ec8a
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "scenes": [ { "nodes" : [ 0 ] } ],
+  "nodes": [ { "name": "1" } ],
+  "skeletons": [ { "node": "some-other" } ]
+}
diff --git a/automated-tests/resources/dli_arc.fsh b/automated-tests/resources/dli_arc.fsh
new file mode 100644 (file)
index 0000000..7eb83af
--- /dev/null
@@ -0,0 +1,112 @@
+#version 300 es\r
+\r
+#ifdef HIGHP\r
+  precision highp float;\r
+#else\r
+  precision mediump float;\r
+#endif\r
+\r
+#define ROUND_STARTCAP   0x1\r
+#define ROUND_ENDCAP     0x2\r
+#define SQUARE_STARTCAP  0x4\r
+#define SQUARE_ENDCAP    0x8\r
+precision mediump float;\r
+\r
+in vec2 vUV;\r
+\r
+uniform vec4 uColor;\r
+uniform int antiAliasing;\r
+uniform float radius;\r
+uniform vec2 startAngle;\r
+uniform vec2 endAngle;\r
+uniform int arcCaps;\r
+\r
+out vec4 FragColor;\r
+\r
+\r
+void main()\r
+{\r
+////////////////////\r
+//Inputs\r
+  float radius1 = 1.0;\r
+  float uvPixel = length( dFdx( vUV ) ) * 2.0;\r
+  float dx = mix( 0.0001, uvPixel, antiAliasing > 0 );\r
+  float radius2 = mix( 1.0 - radius * uvPixel - dx , -radius , step( 0.0, -radius ) );\r
+////////////////////\r
+  float alpha = 0.0;\r
+  float circle = 0.0;\r
+\r
+  vec2 uv = fract(vUV) * 2.0 - 1.0;\r
+\r
+  float ang_img = endAngle.y * startAngle.x - endAngle.x * startAngle.y;\r
+  float half1 = uv.x * startAngle.x - uv.y * startAngle.y;\r
+  float half2 = uv.y * endAngle.y - uv.x * endAngle.x;\r
+  float len = length(uv);\r
+  float equalAngles = step(0.99999, dot(endAngle, startAngle));\r
+  float right_side = step(0.0, ang_img);\r
+  vec2 uv_norm = normalize(uv);\r
+  float cap_radius = -0.5 + radius2 / 2.0;\r
+  float square_cap;\r
+  float sq_plane;\r
+\r
+    /******************************************\r
+   * Equivalent to:\r
+   * if( len > radius1 || len < radius2 )\r
+   */\r
+  circle = alpha =  smoothstep( len, len + dx, radius1 ) * smoothstep( -len, -len + dx, -radius2 );\r
+  float half1_step = smoothstep( -dx, 0.0, half1 );\r
+  float half2_step = smoothstep( -dx, 0.0, half2 );\r
+\r
+  float neg_angimg = max( half1_step, half2_step );\r
+  float pos_angimg = min( half1_step, half2_step );\r
+  /******************************************\r
+   * Equivalent to:\r
+  if(ang_img < 0.0)\r
+  {\r
+    alpha *= max(smoothstep( 0.0, dx, half1 ), smoothstep( 0.0, dx, half2));\r
+  }\r
+  else\r
+  {\r
+    alpha *= min(smoothstep( 0.0, dx, half1 ) , smoothstep( 0.0, dx, half2 ));\r
+  }\r
+  */\r
+  alpha *= mix( neg_angimg, pos_angimg, step(0.0, ang_img) );\r
+\r
+  alpha = mix( alpha, mix( circle, 0.0, right_side ), equalAngles );\r
+\r
+  if((arcCaps & ROUND_STARTCAP) > 0)\r
+  {\r
+    len = length(uv - normalize( vec2( startAngle.y, startAngle.x )) * (1.0 + cap_radius));\r
+    alpha = max(alpha, smoothstep(cap_radius, cap_radius + dx, -len) );\r
+  }\r
+  if((arcCaps & ROUND_ENDCAP) > 0)\r
+  {\r
+    len = length(uv - normalize( vec2( endAngle.y, endAngle.x )) * (1.0 + cap_radius));\r
+    alpha = max(alpha, smoothstep(cap_radius, cap_radius + dx, -len) );\r
+  }\r
+\r
+  if((arcCaps & SQUARE_STARTCAP) > 0)\r
+  {\r
+    sq_plane = -uv.x * startAngle.y - uv.y * startAngle.x;\r
+    square_cap = min(step( 0.0, -half1 ), smoothstep( cap_radius, cap_radius + dx, half1 ));\r
+    square_cap = min(square_cap, smoothstep( radius2, radius2 + dx, -sq_plane ));\r
+    square_cap = min(square_cap, smoothstep( -1.0, -1.0 + dx, sq_plane ));\r
+    alpha = max(square_cap, alpha);\r
+  }\r
+\r
+  if((arcCaps & SQUARE_ENDCAP) > 0)\r
+  {\r
+    sq_plane = -uv.x * endAngle.y - uv.y * endAngle.x;\r
+    square_cap = min(step( 0.0, -half2 ), smoothstep( cap_radius, cap_radius + dx, half2 ));\r
+    square_cap = min(square_cap, smoothstep( radius2, radius2 + dx, -sq_plane ));\r
+    square_cap = min(square_cap, smoothstep( -1.0, -1.0 + dx, sq_plane ));\r
+    alpha = max(square_cap, alpha);\r
+  }\r
+\r
+  if( alpha == 0.0 )\r
+  {\r
+    discard;\r
+  }\r
+\r
+  FragColor = vec4(vec3(uColor), uColor.a * alpha);\r
+}\r
diff --git a/automated-tests/resources/dli_arc.vsh b/automated-tests/resources/dli_arc.vsh
new file mode 100644 (file)
index 0000000..7b38dea
--- /dev/null
@@ -0,0 +1,36 @@
+#version 300 es\r
+\r
+#ifdef HIGHP\r
+  precision highp float;\r
+#else\r
+  precision mediump float;\r
+#endif\r
+\r
+in vec3 aPosition;\r
+in vec2 aTexCoord;\r
+\r
+out vec2 vUV;\r
+\r
+uniform vec3 uSize;\r
+\r
+uniform mat4 uProjection;\r
+uniform mat4 uModelMatrix;\r
+uniform mat4 uViewMatrix;\r
+\r
+uniform vec2 uTilt;\r
+\r
+void main()\r
+{\r
+  vec4 vPosition = vec4( aPosition * uSize, 1.0);\r
+  vec4 mPosition = uModelMatrix * vPosition;\r
+\r
+  mPosition.xy += 0.3 * uTilt * mPosition.z;\r
+  gl_Position = uProjection * uViewMatrix * mPosition;\r
+\r
+#ifdef FLIP_V\r
+  vUV = vec2(aTexCoord.x, 1.0 - aTexCoord.y);\r
+#else\r
+  vUV = aTexCoord;\r
+#endif\r
+\r
+}\r
diff --git a/automated-tests/resources/dli_images.fsh b/automated-tests/resources/dli_images.fsh
new file mode 100644 (file)
index 0000000..72a88b3
--- /dev/null
@@ -0,0 +1,31 @@
+#version 300 es\r
+\r
+#ifdef HIGHP\r
+  precision highp float;\r
+#else\r
+  precision mediump float;\r
+#endif\r
+\r
+uniform sampler2D sAlbedo;\r
+\r
+uniform vec4 uColor;\r
+\r
+#ifdef ALPHA_TEST\r
+  uniform float uAlphaThreshold;\r
+#endif //ALPHA_TEST\r
+\r
+in vec2 vUV;\r
+\r
+out vec4 FragColor;\r
+\r
+void main()\r
+{\r
+  vec4 color = texture(sAlbedo, vUV.st);\r
+#ifdef ALPHA_TEST\r
+  if (color.a <= uAlphaThreshold)\r
+  {\r
+    discard;\r
+  }\r
+#endif\r
+  FragColor = color * uColor;\r
+}\r
diff --git a/automated-tests/resources/dli_images.vsh b/automated-tests/resources/dli_images.vsh
new file mode 100644 (file)
index 0000000..9698937
--- /dev/null
@@ -0,0 +1,37 @@
+#version 300 es\r
+\r
+#ifdef HIGHP\r
+  precision highp float;\r
+#else\r
+  precision mediump float;\r
+#endif\r
+\r
+in vec3 aPosition;\r
+in vec2 aTexCoord;\r
+\r
+out vec2 vUV;\r
+\r
+uniform vec3 uSize;\r
+\r
+uniform mat4 uCubeMatrix;\r
+\r
+uniform mat4 uProjection;\r
+uniform mat4 uModelMatrix;\r
+uniform mat4 uViewMatrix;\r
+\r
+uniform vec2 uTilt;\r
+\r
+void main()\r
+{\r
+  vec4 vPosition = vec4( aPosition * uSize, 1.0);\r
+  vec4 mPosition = uModelMatrix * vPosition;\r
+\r
+  mPosition.xy += 0.3 * uTilt * mPosition.z;\r
+  gl_Position = uProjection * uViewMatrix * mPosition;\r
+\r
+#ifdef FLIP_V\r
+  vUV = vec2(aTexCoord.x, 1.0 - aTexCoord.y);\r
+#else\r
+  vUV = aTexCoord;\r
+#endif\r
+}\r
diff --git a/automated-tests/resources/dli_pbr.fsh b/automated-tests/resources/dli_pbr.fsh
new file mode 100644 (file)
index 0000000..72a0d31
--- /dev/null
@@ -0,0 +1,160 @@
+#version 300 es\r
+\r
+#ifdef HIGHP\r
+  precision highp float;\r
+#else\r
+  precision mediump float;\r
+#endif\r
+\r
+#ifdef THREE_TEX\r
+#ifdef GLTF_CHANNELS\r
+// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#pbrmetallicroughnessmetallicroughnesstexture\r
+#define METALLIC b\r
+#define ROUGHNESS g\r
+#else //GLTF_CHANNELS\r
+#define METALLIC r\r
+#define ROUGHNESS a\r
+#endif //GLTF_CHANNELS\r
+#endif //THREE_TEX\r
+\r
+#ifdef THREE_TEX\r
+  uniform sampler2D sAlbedoAlpha;\r
+  uniform sampler2D sMetalRoughness;\r
+  uniform sampler2D sNormal;\r
+\r
+#ifdef ALPHA_TEST\r
+  uniform float uAlphaThreshold;\r
+#endif //ALPHA_TEST\r
+\r
+#else\r
+  uniform sampler2D sAlbedoMetal;\r
+  uniform sampler2D sNormalRoughness;\r
+#endif\r
+\r
+uniform samplerCube sDiffuse;\r
+uniform samplerCube sSpecular;\r
+\r
+// Number of mip map levels in the texture\r
+uniform float uMaxLOD;\r
+\r
+// Transformation matrix of the cubemap texture\r
+uniform mat4 uCubeMatrix;\r
+\r
+uniform vec4 uColor;\r
+uniform float uMetallicFactor;\r
+uniform float uRoughnessFactor;\r
+\r
+//IBL Light intensity\r
+uniform float uIblIntensity;\r
+\r
+in vec2 vUV;\r
+in vec3 vNormal;\r
+in vec3 vTangent;\r
+in vec3 vViewVec;\r
+\r
+out vec4 FragColor;\r
+\r
+// Functions for BRDF calculation come from\r
+// https://www.unrealengine.com/blog/physically-based-shading-on-mobile\r
+// Based on the paper by Dimitar Lazarov\r
+// http://blog.selfshadow.com/publications/s2013-shading-course/lazarov/s2013_pbs_black_ops_2_notes.pdf\r
+vec3 EnvBRDFApprox( vec3 SpecularColor, float Roughness, float NoV )\r
+{\r
+  const vec4 c0 = vec4( -1.0, -0.0275, -0.572, 0.022 );\r
+  const vec4 c1 = vec4( 1.0, 0.0425, 1.04, -0.04 );\r
+  vec4 r = Roughness * c0 + c1;\r
+  float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y;\r
+  vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\r
+\r
+  return SpecularColor * AB.x + AB.y;\r
+}\r
+\r
+void main()\r
+{\r
+  // We get information from the maps (albedo, normal map, roughness, metalness\r
+  // I access the maps in the order they will be used\r
+#ifdef THREE_TEX\r
+  vec4 albedoAlpha = texture(sAlbedoAlpha, vUV.st);\r
+  float alpha = albedoAlpha.a;\r
+#ifdef ALPHA_TEST\r
+  if (alpha <= uAlphaThreshold)\r
+  {\r
+    discard;\r
+  }\r
+#endif //ALPHA_TEST\r
+  vec3 albedoColor = albedoAlpha.rgb * uColor.rgb;\r
+\r
+  vec4 metalRoughness = texture(sMetalRoughness, vUV.st);\r
+  float metallic = metalRoughness.METALLIC * uMetallicFactor;\r
+  float roughness = metalRoughness.ROUGHNESS * uRoughnessFactor;\r
+\r
+  vec3 normalMap = texture(sNormal, vUV.st).rgb;\r
+#else  //THREE_TEX\r
+  vec4 albedoMetal = texture(sAlbedoMetal, vUV.st);\r
+  vec3 albedoColor = albedoMetal.rgb * uColor.rgb;\r
+  float metallic = albedoMetal.a * uMetallicFactor;\r
+\r
+  vec4 normalRoughness = texture(sNormalRoughness, vUV.st);\r
+  vec3 normalMap = normalRoughness.rgb;\r
+  float roughness = normalRoughness.a * uRoughnessFactor;\r
+#endif\r
+  //Normalize vectors\r
+  vec3 normal = normalize(vNormal);\r
+  vec3 tangent = normalize(vTangent);\r
+\r
+  // NOTE: normal and tangent have to be orthogonal for the result of the cross()\r
+  // product to be a unit vector. We might find that we need to normalize().\r
+  vec3 bitangent = cross(normal, tangent);\r
+\r
+  vec3 viewVec = normalize(vViewVec);\r
+\r
+  // Create Inverse Local to world matrix\r
+  mat3 vInvTBN = mat3(tangent, bitangent, normal);\r
+\r
+  // Get normal map info in world space\r
+  normalMap = normalize(normalMap - 0.5);\r
+  vec3 newNormal = vInvTBN * normalMap.rgb;\r
+\r
+  // Calculate normal dot view vector\r
+  float NoV = max(dot(newNormal, -viewVec), 0.0);\r
+\r
+  // Reflect vector\r
+  vec3 reflectionVec = reflect(viewVec, newNormal);\r
+\r
+  //transform it now to environment coordinates (used when the environment rotates)\r
+  vec3 reflecCube = (uCubeMatrix * vec4( reflectionVec, 0.0 ) ).xyz;\r
+  reflecCube = normalize( reflecCube );\r
+\r
+  //transform it now to environment coordinates\r
+  vec3 normalCube = ( uCubeMatrix * vec4( newNormal, 0.0 ) ).xyz;\r
+  normalCube = normalize( normalCube );\r
+\r
+  // Get irradiance from diffuse cubemap\r
+  vec3 irradiance = texture( sDiffuse, normalCube ).rgb;\r
+\r
+  // Access reflection color using roughness value\r
+  float finalLod = mix( 0.0, uMaxLOD - 2.0, roughness);\r
+  vec3 reflectionColor = textureLod(sSpecular, reflecCube, finalLod).rgb;\r
+\r
+  // We are supposed to be using DielectricColor (0.04) of a plastic (almost everything)\r
+  // http://blog.selfshadow.com/publications/s2014-shading-course/hoffman/s2014_pbs_physics_math_slides.pdf\r
+  // however that seems to prevent achieving very dark tones (i.e. get dark gray blacks).\r
+  vec3 DiffuseColor = albedoColor - albedoColor * metallic;  // 1 mad\r
+  vec3 SpecularColor = mix( vec3(0.04), albedoColor, metallic); // 2 mad\r
+\r
+  // Calculate specular color using Magic Function (takes original roughness and normal dot view).\r
+  vec3 specColor =  reflectionColor.rgb * EnvBRDFApprox(SpecularColor, roughness, NoV );\r
+\r
+  // Multiply the result by albedo texture and do energy conservation\r
+  vec3 diffuseColor = irradiance * DiffuseColor;\r
+\r
+  // Final color is the sum of the diffuse and specular term\r
+  vec3 finalColor = diffuseColor + specColor;\r
+\r
+  finalColor = sqrt( finalColor ) * uIblIntensity;\r
+#ifdef THREE_TEX\r
+  FragColor = vec4( finalColor, alpha );\r
+#else //THREE_TEX\r
+  FragColor = vec4( finalColor, 1.0 );\r
+#endif //THREE_TEX\r
+}\r
diff --git a/automated-tests/resources/dli_pbr.vsh b/automated-tests/resources/dli_pbr.vsh
new file mode 100644 (file)
index 0000000..d1be304
--- /dev/null
@@ -0,0 +1,150 @@
+#version 300 es\r
+\r
+#ifdef HIGHP\r
+  precision highp float;\r
+#else\r
+  precision mediump float;\r
+#endif\r
+\r
+in vec3 aPosition;\r
+in vec2 aTexCoord;\r
+in vec3 aNormal;\r
+in vec3 aTangent;\r
+\r
+#ifdef MORPH\r
+  uniform sampler2D sBlendShapeGeometry;\r
+#endif\r
+\r
+out vec2 vUV;\r
+out vec3 vNormal;\r
+out vec3 vTangent;\r
+out vec3 vViewVec;\r
+\r
+\r
+uniform highp mat4 uMvpMatrix;\r
+uniform highp mat4 uViewMatrix;\r
+uniform mat3 uNormalMatrix;\r
+uniform mat4 uModelMatrix;\r
+uniform mat4 uModelView;\r
+uniform mat4 uProjection;\r
+\r
+#ifdef SKINNING\r
+  in vec4 aJoints;\r
+  in vec4 aWeights;\r
+  #define MAX_BONES 64\r
+  uniform mat4 uBone[MAX_BONES];\r
+#endif\r
+\r
+#ifdef MORPH\r
+#define MAX_BLEND_SHAPE_NUMBER 128\r
+uniform int uNumberOfBlendShapes;                                   ///< Total number of blend shapes loaded.\r
+uniform float uBlendShapeWeight[MAX_BLEND_SHAPE_NUMBER];            ///< The weight of each blend shape.\r
+#ifdef MORPH_VERSION_2_0\r
+uniform float uBlendShapeUnnormalizeFactor;                         ///< Factor used to unnormalize the geometry of the blend shape.\r
+#else\r
+uniform float uBlendShapeUnnormalizeFactor[MAX_BLEND_SHAPE_NUMBER]; ///< Factor used to unnormalize the geometry of the blend shape.\r
+#endif\r
+uniform int uBlendShapeComponentSize;                               ///< The size in the texture of either the vertices, normals or tangents. Used to calculate the offset to address them.\r
+#endif\r
+\r
+void main()\r
+{\r
+  vec4 position = vec4(aPosition, 1.0);\r
+  vec3 normal = aNormal;\r
+  vec3 tangent = aTangent;\r
+\r
+#ifdef MORPH\r
+  int width = textureSize( sBlendShapeGeometry, 0 ).x;\r
+\r
+  int blendShapeBufferOffset = 0;\r
+  for( int index = 0; index < uNumberOfBlendShapes; ++index )\r
+  {\r
+#ifdef MORPH_POSITION\r
+    // Calculate the index to retrieve the geometry from the texture.\r
+    int vertexId = gl_VertexID + blendShapeBufferOffset;\r
+    int x = vertexId % width;\r
+    int y = vertexId / width;\r
+\r
+    vec3 diff = vec3(0.0);\r
+    // Retrieves the blend shape geometry from the texture, unnormalizes it and multiply by the weight.\r
+    if( 0.0 != uBlendShapeWeight[index] )\r
+    {\r
+#ifdef MORPH_VERSION_2_0\r
+       float unnormalizeFactor = uBlendShapeUnnormalizeFactor;\r
+#else\r
+       float unnormalizeFactor = uBlendShapeUnnormalizeFactor[index];\r
+#endif\r
+\r
+      diff = uBlendShapeWeight[index] * unnormalizeFactor * ( texelFetch( sBlendShapeGeometry, ivec2(x, y), 0 ).xyz - 0.5 );\r
+    }\r
+\r
+    position.xyz += diff;\r
+\r
+    blendShapeBufferOffset += uBlendShapeComponentSize;\r
+#endif\r
+\r
+#ifdef MORPH_NORMAL\r
+    // Calculate the index to retrieve the normal from the texture.\r
+    vertexId = gl_VertexID + blendShapeBufferOffset;\r
+    x = vertexId % width;\r
+    y = vertexId / width;\r
+\r
+    // Retrieves the blend shape normal from the texture, unnormalizes it and multiply by the weight.\r
+    if( 0.0 != uBlendShapeWeight[index] )\r
+    {\r
+      diff = uBlendShapeWeight[index] * 2.0 * ( texelFetch( sBlendShapeGeometry, ivec2(x, y), 0 ).xyz - 0.5 );\r
+    }\r
+\r
+    normal += diff.xyz;\r
+\r
+    blendShapeBufferOffset += uBlendShapeComponentSize;\r
+#endif\r
+\r
+#ifdef MORPH_TANGENT\r
+    // Calculate the index to retrieve the tangent from the texture.\r
+    vertexId = gl_VertexID + blendShapeBufferOffset;\r
+    x = vertexId % width;\r
+    y = vertexId / width;\r
+\r
+    // Retrieves the blend shape tangent from the texture, unnormalizes it and multiply by the weight.\r
+    if( 0.0 != uBlendShapeWeight[index] )\r
+    {\r
+      diff = uBlendShapeWeight[index] * 2.0 * ( texelFetch( sBlendShapeGeometry, ivec2(x, y), 0 ).xyz - 0.5 );\r
+    }\r
+\r
+    tangent += diff.xyz;\r
+\r
+    blendShapeBufferOffset += uBlendShapeComponentSize;\r
+#endif\r
+  }\r
+\r
+#endif\r
+\r
+#ifdef SKINNING\r
+  mat4 bone = uBone[int(aJoints.x)] * aWeights.x +\r
+    uBone[int(aJoints.y)] * aWeights.y +\r
+    uBone[int(aJoints.z)] * aWeights.z +\r
+    uBone[int(aJoints.w)] * aWeights.w;\r
+  position = bone * position;\r
+  normal = (bone * vec4(normal, 0.0)).xyz;\r
+  tangent = (bone * vec4(tangent, 0.0)).xyz;\r
+#endif\r
+\r
+  vec4 vPosition = uModelMatrix * position;\r
+\r
+  vNormal = normalize(uNormalMatrix * normal);\r
+\r
+  vTangent = normalize(uNormalMatrix * tangent);\r
+\r
+\r
+  vec4 viewPosition = uViewMatrix * vPosition;\r
+  gl_Position = uProjection * viewPosition;\r
+\r
+#ifdef FLIP_V\r
+  vUV = vec2(aTexCoord.x, 1.0 - aTexCoord.y);\r
+#else\r
+  vUV = aTexCoord;\r
+#endif\r
+\r
+  vViewVec = viewPosition.xyz;\r
+}\r
diff --git a/automated-tests/resources/exercise.dli b/automated-tests/resources/exercise.dli
new file mode 100644 (file)
index 0000000..7d9c6b4
--- /dev/null
@@ -0,0 +1,11634 @@
+{\r
+    "asset" : {\r
+        "version" : "1.0"\r
+    },\r
+    "scene" : 1,\r
+    "scenes" : [ {\r
+        "nodes" : [ 0 ]\r
+    }, {\r
+        "nodes" : [ 95 ]\r
+    } ],\r
+    "nodes" : [ {\r
+        "name" : "ExerciseDemo",\r
+        "matrix" : [ 0.01, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 1, 79, 88, 91, 92, 93, 94 ],\r
+        "behavior" : [ 4 ],\r
+        "visible" : true,\r
+        "lightingMode" : "lit"\r
+    }, {\r
+        "name" : "ReferenceShape_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 2 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "hips_JNT",\r
+        "matrix" : [ 0.984536, 0.175182, 0.0, 0.0, -0.175122, 0.984198, 0.026198, 0.0, 0.004589, -0.025793, 0.999657, 0.0, 5.27538, 78.6471, -1.41421, 1.0 ],\r
+        "children" : [ 3, 69, 74 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.013219, -80.0521, 1.41421, 1.0 ]\r
+    }, {\r
+        "name" : "spine_JNT",\r
+        "matrix" : [ 0.984536, -0.175122, 0.004589, 0.0, 0.174502, 0.982682, 0.062338, 0.0, -0.015426, -0.060573, 0.998044, 0.0, 0.015846, 4.13037, 0.166239, 1.0 ],\r
+        "children" : [ 4 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.002627, -84.1824, 1.24797, 1.0 ]\r
+    }, {\r
+        "name" : "spine1_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 0.998172, -0.06044, 0.0, 0.0, 0.06044, 0.998172, 0.0, 0.005973, 10.694, 0.968772, 1.0 ],\r
+        "children" : [ 5 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.0086, -94.8764, 0.279197, 1.0 ]\r
+    }, {\r
+        "name" : "spine2_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 0.998172, -0.06044, 0.0, 0.0, 0.06044, 0.998172, 0.0, 0.0, 10.1532, -0.580362, 1.0 ],\r
+        "children" : [ 6, 30, 54 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.008557, -105.03, 0.859559, 1.0 ]\r
+    }, {\r
+        "name" : "l_shoulder_JNT",\r
+        "matrix" : [ 0.949195, 0.273744, -0.155217, 0.0, -0.201691, 0.907824, 0.367663, 0.0, 0.241555, -0.317678, 0.916914, 0.0, 2.88666, 12.5391, -0.534974, 1.0 ],\r
+        "children" : [ 7 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -2.89521, -117.569, 1.39453, 1.0 ]\r
+    }, {\r
+        "name" : "l_arm_JNT",\r
+        "matrix" : [ 0.421571, -0.904401, -0.065849, 0.0, 0.517137, 0.180132, 0.836733, 0.0, -0.744881, -0.386795, 0.543638, 0.0, 10.2653, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 8 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -13.1738, -116.164, 1.39451, 1.0 ]\r
+    }, {\r
+        "name" : "l_forearm_JNT",\r
+        "matrix" : [ -0.073871, 0.0, 0.997268, 0.0, -3.91E-4, 1.0, 0.0, 0.0, -0.997268, -3.93E-4, -0.073871, 0.0, 25.5729, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 9 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -38.7466, -116.164, 1.39451, 1.0 ]\r
+    }, {\r
+        "name" : "l_hand_JNT",\r
+        "matrix" : [ 0.738915, 0.444766, -0.506149, 0.0, -0.59918, 0.777334, -0.191665, 0.0, 0.308201, 0.444898, 0.840879, 0.0, 19.8068, -0.345714, 1.00359, 1.0 ],\r
+        "children" : [ 10, 14, 18, 22, 26 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -58.5534, -115.818, 0.390923, 1.0 ]\r
+    }, {\r
+        "name" : "l_handThumb1_JNT",\r
+        "matrix" : [ 0.782913, -0.117863, 0.610865, 0.0, 0.011962, 0.984561, 0.174635, 0.0, -0.622017, -0.129417, 0.772235, 0.0, 2.27648, -1.65194, 3.31969, 1.0 ],\r
+        "children" : [ 11 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 0.707107, 0.0, -0.707107, 0.0, 0.0, 1.0, 0.0, 0.0, 0.707107, 0.0, 0.707107, 0.0, -45.0842, -114.166, 40.9423, 1.0 ]\r
+    }, {\r
+        "name" : "l_handThumb2_JNT",\r
+        "matrix" : [ 0.830425, -0.487404, -0.269872, 0.0, 0.464771, 0.873169, -0.146844, 0.0, 0.307216, -0.003486, 0.951633, 0.0, 3.0735, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 12 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 0.707107, 0.0, -0.707108, 0.0, 0.0, 1.0, 0.0, 0.0, 0.707108, 0.0, 0.707107, 0.0, -48.1577, -114.166, 40.9423, 1.0 ]\r
+    }, {\r
+        "name" : "l_handThumb3_JNT",\r
+        "matrix" : [ 0.98882, -0.149117, 0.0, 0.0, 0.149117, 0.98882, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 3.37693, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 13 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 0.707107, 0.0, -0.707108, 0.0, 0.0, 1.0, 0.0, 0.0, 0.707107, 0.0, 0.707107, 0.0, -51.5344, -114.166, 40.9423, 1.0 ]\r
+    }, {\r
+        "name" : "l_handThumb4_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 3.92326, 0.0, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "l_handIndex1_JNT",\r
+        "matrix" : [ 0.904938, -0.425377, 0.011949, 0.0, 0.425382, 0.905011, 0.002209, 0.0, -0.011754, 0.003084, 1.0, 0.0, 9.34022, -0.291901, 4.04881, 1.0 ],\r
+        "children" : [ 15 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -67.8936, -115.526, -3.65789, 1.0 ]\r
+    }, {\r
+        "name" : "l_handIndex2_JNT",\r
+        "matrix" : [ 0.973047, -0.230606, 0.0, 0.0, 0.230606, 0.973047, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 4.09938, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 16 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -71.993, -115.526, -3.65788, 1.0 ]\r
+    }, {\r
+        "name" : "l_handIndex3_JNT",\r
+        "matrix" : [ 0.973047, -0.230606, 0.0, 0.0, 0.230606, 0.973047, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.57415, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 17 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -74.5672, -115.526, -3.65788, 1.0 ]\r
+    }, {\r
+        "name" : "l_handIndex4_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.56603, 0.0, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "l_handPinky1_JNT",\r
+        "matrix" : [ 0.996226, -0.081261, -0.030513, 0.0, 0.081431, 0.996669, 0.004384, 0.0, 0.030055, -0.006852, 0.999525, 0.0, 8.45263, -1.52385, -3.88068, 1.0 ],\r
+        "children" : [ 19 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -67.0061, -114.294, 4.2716, 1.0 ]\r
+    }, {\r
+        "name" : "l_handPinky2_JNT",\r
+        "matrix" : [ 0.973047, -0.230606, 0.0, 0.0, 0.230606, 0.973047, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 3.07304, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 20 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -70.0791, -114.294, 4.2716, 1.0 ]\r
+    }, {\r
+        "name" : "l_handPinky3_JNT",\r
+        "matrix" : [ 0.973047, -0.230606, 0.0, 0.0, 0.230606, 0.973047, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.96712, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 21 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -72.0462, -114.294, 4.27159, 1.0 ]\r
+    }, {\r
+        "name" : "l_handPinky4_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.8257, 0.0, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "l_handRing1_JNT",\r
+        "matrix" : [ 0.980994, -0.191551, 0.030976, 0.0, 0.19168, 0.981457, -0.001225, 0.0, -0.030167, 0.007139, 0.999519, 0.0, 9.86254, -0.583908, -1.401, 1.0 ],\r
+        "children" : [ 23 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -68.416, -115.234, 1.79193, 1.0 ]\r
+    }, {\r
+        "name" : "l_handRing2_JNT",\r
+        "matrix" : [ 0.973047, -0.230606, 0.0, 0.0, 0.230606, 0.973047, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 3.90873, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 24 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -72.3247, -115.234, 1.79192, 1.0 ]\r
+    }, {\r
+        "name" : "l_handRing3_JNT",\r
+        "matrix" : [ 0.973047, -0.230606, 0.0, 0.0, 0.230606, 0.973047, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.36478, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 25 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -74.6895, -115.234, 1.79192, 1.0 ]\r
+    }, {\r
+        "name" : "l_handRing4_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.88243, 0.0, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "l_handMiddle1_JNT",\r
+        "matrix" : [ 0.953413, -0.301668, 0.0, 0.0, 0.301668, 0.953413, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 10.0737, -0.408325, 1.31404, 1.0 ],\r
+        "children" : [ 27 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -68.6272, -115.41, -0.923121, 1.0 ]\r
+    }, {\r
+        "name" : "l_handMiddle2_JNT",\r
+        "matrix" : [ 0.973047, -0.230606, 0.0, 0.0, 0.230606, 0.973047, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 4.2249, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 28 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -72.8521, -115.41, -0.923118, 1.0 ]\r
+    }, {\r
+        "name" : "l_handMiddle3_JNT",\r
+        "matrix" : [ 0.973047, -0.230606, 0.0, 0.0, 0.230606, 0.973048, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.46916, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 29 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -75.3213, -115.41, -0.923117, 1.0 ]\r
+    }, {\r
+        "name" : "l_handMiddle4_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 3.28131, 0.0, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "r_shoulder_JNT",\r
+        "matrix" : [ 0.999696, 0.01745, -0.017452, 0.0, -0.017145, 0.999701, 0.01745, 0.0, 0.017751, -0.017145, 0.999696, 0.0, -2.88677, 12.5391, -0.53497, 1.0 ],\r
+        "children" : [ 31 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 2.87821, -117.569, 1.39453, 1.0 ]\r
+    }, {\r
+        "name" : "r_arm_JNT",\r
+        "matrix" : [ 0.148671, 0.988778, -0.014664, 0.0, -0.988733, 0.14837, -0.019851, 0.0, -0.017453, 0.01745, 0.999695, 0.0, -10.2653, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 32 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 13.1303, -116.164, 1.39455, 1.0 ]\r
+    }, {\r
+        "name" : "r_forearm_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 4.01E-4, 0.0, 0.0, -4.01E-4, 1.0, 0.0, -25.5729, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 33 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 38.7032, -116.164, 1.39456, 1.0 ]\r
+    }, {\r
+        "name" : "r_hand_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, -4.01E-4, 0.0, 0.0, 4.01E-4, 1.0, 0.0, -19.807, -0.346, 1.00358, 1.0 ],\r
+        "children" : [ 34, 38, 42, 46, 50 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 58.5102, -115.818, 0.390979, 1.0 ]\r
+    }, {\r
+        "name" : "r_handThumb1_JNT",\r
+        "matrix" : [ 0.653433, 0.24549, -0.716073, 0.0, -0.244838, 0.963648, 0.106945, 0.0, 0.716296, 0.105441, 0.689785, 0.0, -2.27647, -1.65195, 3.31969, 1.0 ],\r
+        "children" : [ 35 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 0.707106, 0.0, 0.707106, 0.0, 0.0, 1.0, 0.0, 0.0, -0.707106, 0.0, 0.707106, 0.0, 45.0536, -114.166, 40.9117, 1.0 ]\r
+    }, {\r
+        "name" : "r_handThumb2_JNT",\r
+        "matrix" : [ 0.867302, 0.0, 0.497783, 0.0, 0.0, 1.0, 0.0, 0.0, -0.497783, 0.0, 0.867301, 0.0, -3.0735, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 36 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 0.707106, 0.0, 0.707106, 0.0, 0.0, 1.0, 0.0, 0.0, -0.707106, 0.0, 0.707106, 0.0, 48.127, -114.166, 40.9117, 1.0 ]\r
+    }, {\r
+        "name" : "r_handThumb3_JNT",\r
+        "matrix" : [ 0.996311, 0.0, 0.085813, 0.0, 0.0, 1.0, 0.0, 0.0, -0.085813, 0.0, 0.996311, 0.0, -3.37693, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 37 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 0.707106, 0.0, 0.707107, 0.0, 0.0, 1.0, 0.0, 0.0, -0.707106, 0.0, 0.707107, 0.0, 51.504, -114.166, 40.9117, 1.0 ]\r
+    }, {\r
+        "name" : "r_handThumb4_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -3.923, 0.0, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "r_handRing1_JNT",\r
+        "matrix" : [ 0.915131, 0.400047, -0.049976, 0.0, -0.400717, 0.916194, -0.003754, 0.0, 0.044286, 0.023462, 0.998743, 0.0, -9.86253, -0.583984, -1.40101, 1.0 ],\r
+        "children" : [ 39 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 68.3727, -115.234, 1.79199, 1.0 ]\r
+    }, {\r
+        "name" : "r_handRing2_JNT",\r
+        "matrix" : [ 0.94204, 0.335501, 0.0, 0.0, -0.335501, 0.94204, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -3.90872, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 40 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 72.2814, -115.234, 1.79199, 1.0 ]\r
+    }, {\r
+        "name" : "r_handRing3_JNT",\r
+        "matrix" : [ 0.94204, 0.335501, 0.0, 0.0, -0.335501, 0.94204, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -2.36478, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 41 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 74.6462, -115.234, 1.792, 1.0 ]\r
+    }, {\r
+        "name" : "r_handRing4_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -2.882, 0.0, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "r_handPinky1_JNT",\r
+        "matrix" : [ 0.863063, 0.499949, -0.071924, 0.0, -0.501878, 0.864874, -0.010555, 0.0, 0.056928, 0.045207, 0.997354, 0.0, -8.45262, -1.52391, -3.88068, 1.0 ],\r
+        "children" : [ 43 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 66.9628, -114.294, 4.27166, 1.0 ]\r
+    }, {\r
+        "name" : "r_handPinky2_JNT",\r
+        "matrix" : [ 0.94204, 0.335501, 0.0, 0.0, -0.335501, 0.94204, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -3.07304, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 44 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 70.0358, -114.294, 4.27167, 1.0 ]\r
+    }, {\r
+        "name" : "r_handPinky3_JNT",\r
+        "matrix" : [ 0.94204, 0.335501, 0.0, 0.0, -0.335501, 0.94204, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -1.96712, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 45 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 72.0029, -114.294, 4.27168, 1.0 ]\r
+    }, {\r
+        "name" : "r_handPinky4_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -2.826, 0.0, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "r_handMiddle1_JNT",\r
+        "matrix" : [ 0.944916, 0.327312, 0.0, 0.0, -0.327312, 0.944916, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -10.0737, -0.408401, 1.31403, 1.0 ],\r
+        "children" : [ 47 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 68.5839, -115.409, -0.923056, 1.0 ]\r
+    }, {\r
+        "name" : "r_handMiddle2_JNT",\r
+        "matrix" : [ 0.94204, 0.335501, 0.0, 0.0, -0.335501, 0.94204, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -4.2249, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 48 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 72.8088, -115.409, -0.923044, 1.0 ]\r
+    }, {\r
+        "name" : "r_handMiddle3_JNT",\r
+        "matrix" : [ 0.94204, 0.335501, 0.0, 0.0, -0.335501, 0.94204, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -2.46917, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 49 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 75.278, -115.409, -0.92304, 1.0 ]\r
+    }, {\r
+        "name" : "r_handMiddle4_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -3.281, 0.0, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "r_handIndex1_JNT",\r
+        "matrix" : [ 0.980248, 0.186022, 0.067154, 0.0, -0.186597, 0.982434, 0.002344, 0.0, -0.065538, -0.014828, 0.99774, 0.0, -9.34022, -0.291962, 4.0488, 1.0 ],\r
+        "children" : [ 51 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 67.8504, -115.526, -3.65782, 1.0 ]\r
+    }, {\r
+        "name" : "r_handIndex2_JNT",\r
+        "matrix" : [ 0.94204, 0.335501, 0.0, 0.0, -0.335501, 0.94204, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -4.09938, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 52 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 71.9498, -115.526, -3.65782, 1.0 ]\r
+    }, {\r
+        "name" : "r_handIndex3_JNT",\r
+        "matrix" : [ 0.94204, 0.335501, 0.0, 0.0, -0.335501, 0.94204, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -2.57415, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 53 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 74.5239, -115.526, -3.65782, 1.0 ]\r
+    }, {\r
+        "name" : "r_handIndex4_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -2.566, 0.0, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "neck_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 0.998172, -0.06044, 0.0, 0.0, 0.06044, 0.998172, 0.0, 0.0, 17.0656, -0.695395, 1.0 ],\r
+        "children" : [ 55 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.008488, -122.095, 1.55495, 1.0 ]\r
+    }, {\r
+        "name" : "head_JNT",\r
+        "matrix" : [ 0.995828, 0.087837, -0.024703, 0.0, -0.081637, 0.978626, 0.188753, 0.0, 0.040755, -0.185949, 0.981714, 0.0, 0.0, 8.13757, 2.14518, 1.0 ],\r
+        "children" : [ 56, 59, 62, 65, 66, 67, 68 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.021705, -128.828, -2.00444, 1.0 ]\r
+    }, {\r
+        "name" : "hair_01_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 11.442, 24.1427, -1.69867, 1.0 ],\r
+        "children" : [ 57 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "hair_01_01_JNT",\r
+        "matrix" : [ 0.0, 0.0, -1.0, 0.0, -0.093641, 0.995606, 0.0, 0.0, 0.995606, 0.093641, 0.0, 0.0, 0.0, -9.01569, 0.0, 1.0 ],\r
+        "children" : [ 58 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "hair_01_02_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, -11.0805, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "hair_02_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -13.1304, 24.2143, -1.69866, 1.0 ],\r
+        "children" : [ 60 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "hair_02_01_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, -9.01563, 0.0, 1.0 ],\r
+        "children" : [ 61 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "hair_02_02_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, -11.0805, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "hair_03_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 24.2142, -13.2264, 1.0 ],\r
+        "children" : [ 63 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "hair_03_01_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, -9.01563, 0.0, 1.0 ],\r
+        "children" : [ 64 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "hair_03_02_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, -11.0805, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "l_earring_accessory_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 10.936, 6.972, 0.888, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "r_earring_accessory_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -11.0026, 6.74516, 0.187242, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "l_eye_JNT",\r
+        "matrix" : [ 0.997963, -0.003027, 0.063724, 0.0, -0.003026, 0.995503, 0.094684, 0.0, -0.063724, -0.094684, 0.993466, 0.0, 4.68487, 12.7498, 9.96268, 1.0 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -4.74966, -141.45, -10.6596, 1.0 ]\r
+    }, {\r
+        "name" : "r_eye_JNT",\r
+        "matrix" : [ 0.997966, -0.002933, 0.063684, 0.0, -0.002932, 0.995772, 0.091809, 0.0, -0.063684, -0.091809, 0.993738, 0.0, -4.77108, 12.7498, 9.96268, 1.0 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 4.70629, -141.45, -10.6596, 1.0 ]\r
+    }, {\r
+        "name" : "r_upleg_JNT",\r
+        "matrix" : [ 0.927337, -0.198173, 0.317448, 0.0, 0.278028, 0.93264, -0.229963, 0.0, -0.250492, 0.301512, 0.919969, 0.0, -7.5159, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 70 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, -0.002495, -1.16E-4, 0.0, 0.002493, 0.999837, -0.017917, 0.0, 1.61E-4, 0.017917, 0.99984, 0.0, 7.32992, -80.0324, 2.84741, 1.0 ]\r
+    }, {\r
+        "name" : "r_leg_JNT",\r
+        "matrix" : [ 0.997732, 0.060039, 0.030441, 0.0, -0.067243, 0.9099, 0.409341, 0.0, -0.003122, -0.41046, 0.911874, 0.0, -0.724057, -37.7363, 2.80023, 1.0 ],\r
+        "children" : [ 71 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.002998, 0.0, 0.0, -0.002998, 0.999857, 0.016638, 0.0, 0.0, -0.016638, 0.999862, 0.0, 8.28614, -42.2277, -1.41208, 1.0 ]\r
+    }, {\r
+        "name" : "r_foot_JNT",\r
+        "matrix" : [ 0.964557, 0.026598, 0.262529, 0.0, 0.024923, 0.981276, -0.190988, 0.0, -0.262693, 0.190762, 0.945834, 0.0, 1.26E-4, -34.5752, -1.6048, 1.0 ],\r
+        "children" : [ 72 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 9.09E-4, 0.0, 0.0, -9.09E-4, 1.0, 0.0, 8.26299, -7.67328, 0.312986, 1.0 ]\r
+    }, {\r
+        "name" : "r_toebase_JNT",\r
+        "matrix" : [ 0.0, -0.103501, 0.994629, 0.0, 0.0, 0.994629, 0.103501, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, -6.10491, 11.1619, 1.0 ],\r
+        "children" : [ 73 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 0.0, 0.0, -1.0, 0.0, -0.102591, 0.994724, 0.0, 0.0, 0.994724, 0.102591, 0.0, 0.0, -10.6284, -2.68276, -8.26298, 1.0 ]\r
+    }, {\r
+        "name" : "joint1",\r
+        "matrix" : [ 0.0, 0.0, -1.0, 0.0, -0.108623, 0.994083, 0.0, 0.0, 0.994083, 0.108623, 0.0, 0.0, 5.7477, 0.0, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "l_upleg_JNT",\r
+        "matrix" : [ 0.969105, -0.243372, 0.040065, 0.0, 0.242338, 0.969759, 0.028984, 0.0, -0.045907, -0.018379, 0.998777, 0.0, 7.51591, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 75 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 0.999881, -0.015441, 0.0, 0.0, 0.015441, 0.999881, 0.0, -7.50515, -80.0204, 2.6501, 1.0 ]\r
+    }, {\r
+        "name" : "l_leg_JNT",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 0.993288, -0.115665, 0.0, 0.0, 0.115665, 0.993288, 0.0, 2.16E-4, -37.7436, 2.62906, 1.0 ],\r
+        "children" : [ 76 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 0.999822, 0.018881, 0.0, 0.0, -0.018881, 0.999822, 0.0, -7.50542, -42.2526, -1.42976, 1.0 ]\r
+    }, {\r
+        "name" : "l_foot_JNT",\r
+        "matrix" : [ 0.996936, 0.072648, -0.029003, 0.0, -0.070636, 0.995367, 0.065233, 0.0, 0.033608, -0.062984, 0.997448, 0.0, 0.0, -34.5752, -1.6048, 1.0 ],\r
+        "children" : [ 77 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 9.11E-4, 0.0, 0.0, -9.11E-4, 1.0, 0.0, -7.50523, -7.67329, 0.31298, 1.0 ]\r
+    }, {\r
+        "name" : "l_toebase_JNT",\r
+        "matrix" : [ 1.9E-4, -0.10218, 0.994766, 0.0, 0.0, 0.994766, 0.10218, 0.0, -1.0, 0.0, 1.81E-4, 0.0, 0.0, -6.10494, 11.1619, 1.0 ],\r
+        "children" : [ 78 ],\r
+        "visible" : true,\r
+        "inverseBindPoseMatrix" : [ 0.0, 0.0, -1.0, 0.0, -0.102591, 0.994724, 0.0, 0.0, 0.994724, 0.102591, 0.0, 0.0, -10.6284, -2.68276, 7.50523, 1.0 ]\r
+    }, {\r
+        "name" : "joint2",\r
+        "matrix" : [ 0.0, 0.0, -1.0, 0.0, -0.104271, 0.994549, 0.0, 0.0, 0.994549, 0.104271, 0.0, 0.0, 5.91715, 0.0, 0.0, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "Model",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 80, 81, 82, 83, 84, 85, 86, 87 ],\r
+        "visible" : true,\r
+        "lightingMode" : "lit-solid"\r
+    }, {\r
+        "name" : "R_New_Eye",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0, 1.0 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 8,\r
+            "mesh" : 0,\r
+            "shader" : 1\r
+        }\r
+    }, {\r
+        "name" : "L_New_Eye",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, -1.0, 1.0 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 8,\r
+            "mesh" : 1,\r
+            "shader" : 1\r
+        }\r
+    }, {\r
+        "name" : "newhair",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 9,\r
+            "mesh" : 2,\r
+            "shader" : 1\r
+        }\r
+    }, {\r
+        "name" : "OutFit_Bottom",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 10,\r
+            "mesh" : 3,\r
+            "shader" : 1\r
+        }\r
+    }, {\r
+        "name" : "OutFit_Top",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 11,\r
+            "mesh" : 4,\r
+            "shader" : 1\r
+        }\r
+    }, {\r
+        "name" : "Shoes_Shoes01_Female_Adult_Thumbnail_GEO",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 12,\r
+            "mesh" : 5,\r
+            "shader" : 1\r
+        }\r
+    }, {\r
+        "name" : "Head_GEO",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 7,\r
+            "mesh" : 6,\r
+            "shader" : 2\r
+        }\r
+    }, {\r
+        "name" : "Body_GEO",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 6,\r
+            "mesh" : 9,\r
+            "shader" : 2\r
+        }\r
+    }, {\r
+        "name" : "directionalLight1",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 91.3751, 284.323, 1.0 ],\r
+        "visible" : true\r
+    }, {\r
+        "name" : "BackGround",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 5,\r
+            "mesh" : 7,\r
+            "shader" : 4\r
+        }\r
+    }, {\r
+        "name" : "Ground",\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 5,\r
+            "mesh" : 8,\r
+            "shader" : 4\r
+        }\r
+    }, {\r
+        "name" : "Idle",\r
+        "size" : [ 30.0, 30.0 ],\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -82.0, 64.0, -1.0, 1.0 ],\r
+        "behavior" : [ 0 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 1,\r
+            "mesh" : 10,\r
+            "shader" : 3\r
+        }\r
+    }, {\r
+        "name" : "Squat",\r
+        "size" : [ 30.0, 30.0 ],\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -85.0, 94.0, 1.0, 1.0 ],\r
+        "behavior" : [ 1 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 2,\r
+            "mesh" : 10,\r
+            "shader" : 3\r
+        }\r
+    }, {\r
+        "name" : "JumpingJack",\r
+        "size" : [ 30.0, 30.0 ],\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -75.0, 124.0, 0.0, 1.0 ],\r
+        "behavior" : [ 2 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 3,\r
+            "mesh" : 10,\r
+            "shader" : 3\r
+        }\r
+    }, {\r
+        "name" : "Lunge",\r
+        "size" : [ 30.0, 30.0 ],\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -55.0, 154.0, 0.0, 1.0 ],\r
+        "behavior" : [ 3 ],\r
+        "visible" : true,\r
+        "model" : {\r
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+            "material" : 4,\r
+            "mesh" : 10,\r
+            "shader" : 3\r
+        }\r
+    }, {\r
+        "name" : "Backdrop",\r
+        "matrix" : [ 0.01, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "children" : [ 89, 90 ],\r
+        "visible" : true,\r
+        "lightingMode" : "lit"\r
+    } ],\r
+    "meshes" : [ {\r
+        "uri" : "exercise/model.dae.bin",\r
+        "attributes" : 223,\r
+        "primitive" : "TRIANGLES",\r
+        "indices" : {\r
+            "byteOffset" : 0,\r
+            "byteLength" : 2520\r
+        },\r
+        "positions" : {\r
+            "byteOffset" : 2520,\r
+            "byteLength" : 3168\r
+        },\r
+        "normals" : {\r
+            "byteOffset" : 5688,\r
+            "byteLength" : 3168\r
+        },\r
+        "textures" : {\r
+            "byteOffset" : 8856,\r
+            "byteLength" : 2112\r
+        },\r
+        "tangents" : {\r
+            "byteOffset" : 10968,\r
+            "byteLength" : 3168\r
+        },\r
+        "skeleton" : 0,\r
+        "joints0" : {\r
+            "byteOffset" : 14136,\r
+            "byteLength" : 4224\r
+        },\r
+        "weights0" : {\r
+            "byteOffset" : 18360,\r
+            "byteLength" : 4224\r
+        }\r
+    }, {\r
+        "uri" : "exercise/model.dae.bin",\r
+        "attributes" : 223,\r
+        "primitive" : "TRIANGLES",\r
+        "indices" : {\r
+            "byteOffset" : 22584,\r
+            "byteLength" : 2520\r
+        },\r
+        "positions" : {\r
+            "byteOffset" : 25104,\r
+            "byteLength" : 3180\r
+        },\r
+        "normals" : {\r
+            "byteOffset" : 28284,\r
+            "byteLength" : 3180\r
+        },\r
+        "textures" : {\r
+            "byteOffset" : 31464,\r
+            "byteLength" : 2120\r
+        },\r
+        "tangents" : {\r
+            "byteOffset" : 33584,\r
+            "byteLength" : 3180\r
+        },\r
+        "skeleton" : 0,\r
+        "joints0" : {\r
+            "byteOffset" : 36764,\r
+            "byteLength" : 4240\r
+        },\r
+        "weights0" : {\r
+            "byteOffset" : 41004,\r
+            "byteLength" : 4240\r
+        }\r
+    }, {\r
+        "uri" : "exercise/model.dae.bin",\r
+        "attributes" : 223,\r
+        "primitive" : "TRIANGLES",\r
+        "indices" : {\r
+            "byteOffset" : 45244,\r
+            "byteLength" : 25938\r
+        },\r
+        "positions" : {\r
+            "byteOffset" : 71182,\r
+            "byteLength" : 34092\r
+        },\r
+        "normals" : {\r
+            "byteOffset" : 105274,\r
+            "byteLength" : 34092\r
+        },\r
+        "textures" : {\r
+            "byteOffset" : 139366,\r
+            "byteLength" : 22728\r
+        },\r
+        "tangents" : {\r
+            "byteOffset" : 162094,\r
+            "byteLength" : 34092\r
+        },\r
+        "skeleton" : 0,\r
+        "joints0" : {\r
+            "byteOffset" : 196186,\r
+            "byteLength" : 45456\r
+        },\r
+        "weights0" : {\r
+            "byteOffset" : 241642,\r
+            "byteLength" : 45456\r
+        }\r
+    }, {\r
+        "uri" : "exercise/model.dae.bin",\r
+        "attributes" : 223,\r
+        "primitive" : "TRIANGLES",\r
+        "indices" : {\r
+            "byteOffset" : 287098,\r
+            "byteLength" : 5988\r
+        },\r
+        "positions" : {\r
+            "byteOffset" : 293086,\r
+            "byteLength" : 8412\r
+        },\r
+        "normals" : {\r
+            "byteOffset" : 301498,\r
+            "byteLength" : 8412\r
+        },\r
+        "textures" : {\r
+            "byteOffset" : 309910,\r
+            "byteLength" : 5608\r
+        },\r
+        "tangents" : {\r
+            "byteOffset" : 315518,\r
+            "byteLength" : 8412\r
+        },\r
+        "skeleton" : 0,\r
+        "joints0" : {\r
+            "byteOffset" : 323930,\r
+            "byteLength" : 11216\r
+        },\r
+        "weights0" : {\r
+            "byteOffset" : 335146,\r
+            "byteLength" : 11216\r
+        }\r
+    }, {\r
+        "uri" : "exercise/model.dae.bin",\r
+        "attributes" : 223,\r
+        "primitive" : "TRIANGLES",\r
+        "indices" : {\r
+            "byteOffset" : 346362,\r
+            "byteLength" : 3720\r
+        },\r
+        "positions" : {\r
+            "byteOffset" : 350082,\r
+            "byteLength" : 5004\r
+        },\r
+        "normals" : {\r
+            "byteOffset" : 355086,\r
+            "byteLength" : 5004\r
+        },\r
+        "textures" : {\r
+            "byteOffset" : 360090,\r
+            "byteLength" : 3336\r
+        },\r
+        "tangents" : {\r
+            "byteOffset" : 363426,\r
+            "byteLength" : 5004\r
+        },\r
+        "skeleton" : 0,\r
+        "joints0" : {\r
+            "byteOffset" : 368430,\r
+            "byteLength" : 6672\r
+        },\r
+        "weights0" : {\r
+            "byteOffset" : 375102,\r
+            "byteLength" : 6672\r
+        }\r
+    }, {\r
+        "uri" : "exercise/model.dae.bin",\r
+        "attributes" : 223,\r
+        "primitive" : "TRIANGLES",\r
+        "indices" : {\r
+            "byteOffset" : 381774,\r
+            "byteLength" : 5520\r
+        },\r
+        "positions" : {\r
+            "byteOffset" : 387294,\r
+            "byteLength" : 7284\r
+        },\r
+        "normals" : {\r
+            "byteOffset" : 394578,\r
+            "byteLength" : 7284\r
+        },\r
+        "textures" : {\r
+            "byteOffset" : 401862,\r
+            "byteLength" : 4856\r
+        },\r
+        "tangents" : {\r
+            "byteOffset" : 406718,\r
+            "byteLength" : 7284\r
+        },\r
+        "skeleton" : 0,\r
+        "joints0" : {\r
+            "byteOffset" : 414002,\r
+            "byteLength" : 9712\r
+        },\r
+        "weights0" : {\r
+            "byteOffset" : 423714,\r
+            "byteLength" : 9712\r
+        }\r
+    }, {\r
+        "uri" : "exercise/model.dae.bin",\r
+        "attributes" : 223,\r
+        "primitive" : "TRIANGLES",\r
+        "indices" : {\r
+            "byteOffset" : 433426,\r
+            "byteLength" : 34524\r
+        },\r
+        "positions" : {\r
+            "byteOffset" : 467950,\r
+            "byteLength" : 37824\r
+        },\r
+        "normals" : {\r
+            "byteOffset" : 505774,\r
+            "byteLength" : 37824\r
+        },\r
+        "textures" : {\r
+            "byteOffset" : 543598,\r
+            "byteLength" : 25216\r
+        },\r
+        "tangents" : {\r
+            "byteOffset" : 568814,\r
+            "byteLength" : 37824\r
+        },\r
+        "skeleton" : 0,\r
+        "joints0" : {\r
+            "byteOffset" : 606638,\r
+            "byteLength" : 50432\r
+        },\r
+        "weights0" : {\r
+            "byteOffset" : 657070,\r
+            "byteLength" : 50432\r
+        }\r
+    }, {\r
+        "uri" : "exercise/model.dae.bin",\r
+        "attributes" : 31,\r
+        "primitive" : "TRIANGLES",\r
+        "indices" : {\r
+            "byteOffset" : 707502,\r
+            "byteLength" : 360\r
+        },\r
+        "positions" : {\r
+            "byteOffset" : 707862,\r
+            "byteLength" : 732\r
+        },\r
+        "normals" : {\r
+            "byteOffset" : 708594,\r
+            "byteLength" : 732\r
+        },\r
+        "textures" : {\r
+            "byteOffset" : 709326,\r
+            "byteLength" : 488\r
+        },\r
+        "tangents" : {\r
+            "byteOffset" : 709814,\r
+            "byteLength" : 732\r
+        }\r
+    }, {\r
+        "uri" : "exercise/model.dae.bin",\r
+        "attributes" : 31,\r
+        "primitive" : "TRIANGLES",\r
+        "indices" : {\r
+            "byteOffset" : 710546,\r
+            "byteLength" : 12\r
+        },\r
+        "positions" : {\r
+            "byteOffset" : 710558,\r
+            "byteLength" : 48\r
+        },\r
+        "normals" : {\r
+            "byteOffset" : 710606,\r
+            "byteLength" : 48\r
+        },\r
+        "textures" : {\r
+            "byteOffset" : 710654,\r
+            "byteLength" : 32\r
+        },\r
+        "tangents" : {\r
+            "byteOffset" : 710686,\r
+            "byteLength" : 48\r
+        }\r
+    }, {\r
+        "uri" : "exercise/model.dae.bin",\r
+        "attributes" : 223,\r
+        "primitive" : "TRIANGLES",\r
+        "indices" : {\r
+            "byteOffset" : 710734,\r
+            "byteLength" : 24312\r
+        },\r
+        "positions" : {\r
+            "byteOffset" : 735046,\r
+            "byteLength" : 33000\r
+        },\r
+        "normals" : {\r
+            "byteOffset" : 768046,\r
+            "byteLength" : 33000\r
+        },\r
+        "textures" : {\r
+            "byteOffset" : 801046,\r
+            "byteLength" : 22000\r
+        },\r
+        "tangents" : {\r
+            "byteOffset" : 823046,\r
+            "byteLength" : 33000\r
+        },\r
+        "skeleton" : 0,\r
+        "joints0" : {\r
+            "byteOffset" : 856046,\r
+            "byteLength" : 44000\r
+        },\r
+        "weights0" : {\r
+            "byteOffset" : 900046,\r
+            "byteLength" : 44000\r
+        }\r
+    }, {\r
+        "uri" : "quad",\r
+        "attributes" : 0,\r
+        "primitive" : "TRIANGLES"\r
+    } ],\r
+    "cameras" : [ {\r
+        "near" : 2.5,\r
+        "far" : 1000.0,\r
+        "matrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.9138, 5.0, 1.0 ],\r
+        "fov" : 37.8492\r
+    } ],\r
+    "lights" : [ {\r
+        "matrix" : [ 0.8950600624084473, 0.0914330929517746, 0.4364715814590454, 0.0, 0.3676385283470154, 0.4026888310909271, -0.8382623195648193, 0.0, -0.25240713357925415, 0.9107588529586792, 0.3268163800239563, 0.0, 0.0, 0.0, -1.0, 1.0 ],\r
+        "color" : [ 1.0, 1.0, 1.0 ],\r
+        "intensity" : 1.0,\r
+        "shadowMapSize" : 256,\r
+        "orthographicSize" : 4.2760005,\r
+        "shadowIntensity" : 1.0\r
+    } ],\r
+    "materials" : [ {\r
+        "name" : "No name",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : true,\r
+        "environment" : 1\r
+    }, {\r
+        "name" : "Icons\\Icon_Idle.png",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : false,\r
+        "albedoMap" : "exercise/Icons/Icon_Idle.png"\r
+    }, {\r
+        "name" : "Icons\\Icon_Squat.png",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : false,\r
+        "albedoMap" : "exercise/Icons/Icon_Squat.png"\r
+    }, {\r
+        "name" : "Icons\\Icon_JJ.png",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : false,\r
+        "albedoMap" : "exercise/Icons/Icon_JJ.png"\r
+    }, {\r
+        "name" : "Icons\\Icon_Lunge.png",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : false,\r
+        "albedoMap" : "exercise/Icons/Icon_Lunge.png"\r
+    }, {\r
+        "name" : "Background",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "normalRoughnessMap" : "exercise/Textures/BG_NormalRoughness.png",\r
+        "albedoMetallicMap" : "exercise/Textures/BG_AlbedoMetallic.png",\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : true,\r
+        "environment" : 1\r
+    }, {\r
+        "name" : "Body",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "normalRoughnessMap" : "exercise/Textures/Body_NormalRoughness.png",\r
+        "albedoMetallicMap" : "exercise/Textures/Body_AlbedoMetallic.png",\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : true,\r
+        "subsurfaceMap" : "exercise/Textures/Body_Female_Asian_Adult_SubsurfaceColor.png",\r
+        "environment" : 1\r
+    }, {\r
+        "name" : "Head",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "normalRoughnessMap" : "exercise/Textures/Head_NormalRoughness.png",\r
+        "albedoMetallicMap" : "exercise/Textures/Head_AlbedoMetallic.png",\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : true,\r
+        "subsurfaceMap" : "exercise/Textures/Head_Female_SubsurfaceColor.png",\r
+        "environment" : 1\r
+    }, {\r
+        "name" : "Eye",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "normalRoughnessMap" : "exercise/Textures/Eye_NormalRoughness.png",\r
+        "albedoMetallicMap" : "exercise/Textures/Eye_AlbedoMetallic.png",\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : true,\r
+        "environment" : 1\r
+    }, {\r
+        "name" : "Hair",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "normalRoughnessMap" : "exercise/Textures/Hair_NormalRoughness.png",\r
+        "albedoMetallicMap" : "exercise/Textures/Hair_AlbedoMetallic.png",\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : true,\r
+        "environment" : 1\r
+    }, {\r
+        "name" : "Bottom outfit",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "normalRoughnessMap" : "exercise/Textures/FitBot_NormalRoughness.png",\r
+        "albedoMetallicMap" : "exercise/Textures/FitBot_AlbedoMetallic.png",\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : true,\r
+        "environment" : 1\r
+    }, {\r
+        "name" : "Top outfit",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "normalRoughnessMap" : "exercise/Textures/FitTop_NormalRoughness.png",\r
+        "albedoMetallicMap" : "exercise/Textures/FitTop_AlbedoMetallic.png",\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : true,\r
+        "environment" : 1\r
+    }, {\r
+        "name" : "Shoe",\r
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],\r
+        "normalRoughnessMap" : "exercise/Textures/Shoe_NormalRoughness.png",\r
+        "albedoMetallicMap" : "exercise/Textures/Shoe_AlbedoMetallic.png",\r
+        "roughness" : 1.0,\r
+        "metallic" : 1.0,\r
+        "mipmap" : true,\r
+        "environment" : 1\r
+    } ],\r
+    "environment" : [ {\r
+        "cubeInitialOrientation" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "iblIntensity" : 1.0\r
+    }, {\r
+        "cubeSpecular" : "Studio/Radiance.ktx",\r
+        "cubeDiffuse" : "Studio/Irradiance.ktx",\r
+        "cubeInitialOrientation" : [ 0.6716271638870239, 0.07979151606559753, -0.7365801334381104, 0.0, 0.07979151606559753, 0.9806114435195923, 0.17898204922676086, 0.0, 0.7365801334381104, -0.17898204922676086, 0.6522386074066162, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "iblIntensity" : 0.75\r
+    } ],\r
+    "shaders" : [ {\r
+        "vertex" : "dli_pbr.vsh",\r
+        "fragment" : "dli_pbr.fsh",\r
+        "rendererState" : "DEPTH_TEST|DEPTH_WRITE|CULL_BACK|DEPTH_FUNC:LESS_EQUAL",\r
+        "uCubeMatrix" : [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],\r
+        "uMaxLOD" : 8\r
+    }, {\r
+        "vertex" : "dli_pbr.vsh",\r
+        "fragment" : "dli_pbr.fsh",\r
+        "hints": [ "MODIFIES_GEOMETRY" ],\r
+        "defines" : [ "LIT", "SKINNING" ],\r
+        "rendererState" : "DEPTH_TEST|DEPTH_WRITE|CULL_BACK|DEPTH_FUNC:LESS_EQUAL",\r
+        "uCubeMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "uMaxLOD" : 6\r
+    }, {\r
+        "vertex" : "dli_pbr.vsh",\r
+        "fragment" : "dli_pbr.fsh",\r
+        "hints": [ "MODIFIES_GEOMETRY" ],\r
+        "defines" : [ "LIT", "SKINNING", "SSS" ],\r
+        "rendererState" : "DEPTH_TEST|DEPTH_WRITE|CULL_BACK|DEPTH_FUNC:LESS_EQUAL",\r
+        "uCubeMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "uMaxLOD" : 6\r
+    }, {\r
+        "vertex" : "dli_pbr.vsh",\r
+        "fragment" : "dli_pbr.fsh",\r
+        "rendererState" : "DEPTH_TEST|ALPHA_BLEND",\r
+        "uCubeMatrix" : [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],\r
+        "uTilt" : [ 0.0, 0.0 ]\r
+    }, {\r
+        "vertex" : "dli_pbr.vsh",\r
+        "fragment" : "dli_pbr.fsh",\r
+        "defines" : [ "LIT", "SHADOW" ],\r
+        "rendererState" : "DEPTH_TEST|DEPTH_WRITE|CULL_BACK|DEPTH_FUNC:LESS_EQUAL",\r
+        "uCubeMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],\r
+        "uMaxLOD" : 6\r
+    } ],\r
+    "animations" : [ {\r
+        "name" : "idleClip",\r
+        "loopCount" : 0,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 3024,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 6048,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 9072,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 12096,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 15120,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 18144,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 20592,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 23616,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 26640,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 29664,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 32688,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 35712,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 38736,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 41760,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 44784,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 47808,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 50832,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-animation.ani",\r
+                "byteOffset" : 53856,\r
+                "numKeys" : 144\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 6.0\r
+            },\r
+            "relative" : false\r
+        } ]\r
+    }, {\r
+        "name" : "squatClip_0",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-0.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-0.ani",\r
+                "byteOffset" : 1764,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-0.ani",\r
+                "byteOffset" : 3192,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-0.ani",\r
+                "byteOffset" : 4956,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-0.ani",\r
+                "byteOffset" : 6720,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-0.ani",\r
+                "byteOffset" : 8484,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-0.ani",\r
+                "byteOffset" : 10248,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hair_02_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-0.ani",\r
+                "byteOffset" : 12012,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hair_03_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-0.ani",\r
+                "byteOffset" : 13776,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 1 ]\r
+    }, {\r
+        "name" : "squatClip_1",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 1596,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 3192,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 4788,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 6384,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 7980,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 9576,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 11172,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 12768,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 14364,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 15656,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 17252,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 18848,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 20444,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 22040,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 23636,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 25232,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 26828,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 28424,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 30020,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 31616,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 33212,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 34808,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 36404,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 38000,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 39596,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 41192,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 42788,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-animation-1.ani",\r
+                "byteOffset" : 44384,\r
+                "numKeys" : 76\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.16667\r
+            },\r
+            "relative" : false\r
+        } ]\r
+    }, {\r
+        "name" : "jumpingJackClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 630,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 1140,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 1770,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 2400,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 3030,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 3660,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 4290,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 4920,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 5550,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 6180,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 6810,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 7440,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 8070,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 8700,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 9330,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 9960,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 10590,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 11220,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 11850,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 12480,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 13110,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 13740,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 14370,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 15000,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 15630,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 16260,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 16890,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 17520,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 18150,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 18780,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 19410,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 20040,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 20670,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 21300,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 21930,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 22560,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 23190,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 23820,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 24450,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 25080,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 25710,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 26340,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 26970,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 27600,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 28230,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 28860,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 29490,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 30120,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 30750,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 31380,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-animation.ani",\r
+                "byteOffset" : 32010,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 4 ]\r
+    }, {\r
+        "name" : "lungeClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 1649,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 3686,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 5723,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 7760,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 9797,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 11834,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 13871,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 15908,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 17945,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 19982,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 22019,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 24056,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 26093,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 28130,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 30167,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 32204,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 34241,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 36278,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 38315,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 40352,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 42389,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 44426,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 46075,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 48112,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 50149,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 52186,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 54223,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 56260,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 58297,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 60334,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 62371,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 64408,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 66445,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-animation.ani",\r
+                "byteOffset" : 68482,\r
+                "numKeys" : 97\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 4.04167\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 7 ]\r
+    }, {\r
+        "name" : "idleToSquatClip_0",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 1932,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 3496,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 5428,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 6992,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 8924,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_shoulder_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 10856,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 12788,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 14720,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 16652,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 18584,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 20516,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 22448,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 24380,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 26312,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 28244,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 30176,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 32108,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 34040,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 35972,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 37904,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 39836,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 41768,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 43700,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 45632,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 47564,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 49496,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 51060,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 52992,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 54924,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 56856,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 58788,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 60720,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 62652,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 64584,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 66516,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 68448,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 70380,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 72312,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 74244,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 76176,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 78108,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 80040,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 81972,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 83904,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 85836,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 87768,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 89700,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 91632,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 93564,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 95496,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 97428,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 99360,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 101292,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 103224,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 105156,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-0.ani",\r
+                "byteOffset" : 107088,\r
+                "numKeys" : 92\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.83333\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 0 ]\r
+    }, {\r
+        "name" : "idleToSquatClip_1",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hair_02_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-1.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hair_03_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-squat-animation-1.ani",\r
+                "byteOffset" : 1764,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        } ]\r
+    }, {\r
+        "name" : "squatToIdleClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 1806,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 3268,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 5074,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 6880,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_shoulder_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 8686,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 10492,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 12298,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 14104,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 15910,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 17716,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 19522,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 21328,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 23134,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 24940,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 26746,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 28552,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 30358,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 32164,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 33970,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 35776,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 37582,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 39388,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 41194,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 43000,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 44462,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 46268,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 48074,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 49880,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 51686,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 53492,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 55298,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 57104,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 58910,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 60716,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 62522,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 64328,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 66134,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 67940,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 69746,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 71552,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 73358,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 75164,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 76970,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 78776,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hair_02_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 80582,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hair_03_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 82388,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 84194,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 86000,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 87806,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 89612,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 91418,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 93224,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 95030,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 96836,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 98642,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-idle-animation.ani",\r
+                "byteOffset" : 100448,\r
+                "numKeys" : 86\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.58333\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 2 ]\r
+    }, {\r
+        "name" : "idleToJumpingJackClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 1365,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 2470,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 3835,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 5200,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_shoulder_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 6565,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 7930,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 9295,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 10660,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 12025,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 13390,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 14755,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 16120,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 17485,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 18850,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 20215,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 21580,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 22945,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 24310,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 25675,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 27040,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 28405,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 29770,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 31135,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 32500,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 33865,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 34970,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 36335,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 37700,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 39065,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 40430,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 41795,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 43160,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 44525,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 45890,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 47255,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 48620,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 49985,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 51350,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 52715,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 54080,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 55445,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 56810,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 58175,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 59540,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 60905,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 62270,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 63635,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 65000,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 66365,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 67730,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 69095,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 70460,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 71825,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 73190,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 74555,\r
+                "numKeys" : 65\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.70833\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 3 ]\r
+    }, {\r
+        "name" : "jumpingJackToIdleClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 1491,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 2698,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 4189,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 5680,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_shoulder_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 7171,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 8662,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 10153,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 11644,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 13135,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 14626,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 16117,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 17608,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 19099,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 20590,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 22081,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 23572,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 25063,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 26554,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 28045,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 29536,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 31027,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 32518,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 34009,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 35500,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 36707,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 38198,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 39689,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 41180,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 42671,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 44162,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 45653,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 47144,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 48635,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 50126,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 51617,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 53108,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 54599,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 56090,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 57581,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 59072,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 60563,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 62054,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 63545,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 65036,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 66527,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 68018,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 69509,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 71000,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 72491,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 73982,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 75473,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 76964,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 78455,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-idle-animation.ani",\r
+                "byteOffset" : 79946,\r
+                "numKeys" : 71\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 2.95833\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 5 ]\r
+    }, {\r
+        "name" : "idleToLungeClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 1764,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 3192,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 4956,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 6384,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 8148,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_shoulder_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 9912,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 11676,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 13440,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 15204,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 16968,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 18732,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 20496,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 22260,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 24024,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 25788,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 27552,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 29316,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 31080,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 32844,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 34608,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 36372,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 38136,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 39900,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 41664,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 43428,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 45192,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 46620,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 48384,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 50148,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 51912,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 53676,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 55440,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 57204,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 58968,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 60732,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 62496,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 64260,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 66024,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 67788,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 69552,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 71316,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 73080,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 74844,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 76608,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 78372,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 80136,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 81900,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 83664,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 85428,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 87192,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 88956,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 90720,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 92484,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 94248,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/idle-to-lunge-animation.ani",\r
+                "byteOffset" : 96012,\r
+                "numKeys" : 84\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.5\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 6 ]\r
+    }, {\r
+        "name" : "lungeToIdleClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 1701,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 3078,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 4779,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 6480,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_shoulder_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 8181,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 9882,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 11583,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 13284,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 14985,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 16686,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 18387,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 20088,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 21789,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 23490,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 25191,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 26892,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 28593,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 30294,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 31995,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 33696,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 35397,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 37098,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 38799,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 40500,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 41877,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 43578,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 45279,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 46980,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 48681,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 50382,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 52083,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 53784,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 55485,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 57186,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 58887,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 60588,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 62289,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 63990,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 65691,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 67392,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 69093,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 70794,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 72495,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 74196,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_eye_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 75897,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 77598,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 79299,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 81000,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 82701,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 84402,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 86103,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 87804,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-idle-animation.ani",\r
+                "byteOffset" : 89505,\r
+                "numKeys" : 81\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 3.375\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 8 ]\r
+    }, {\r
+        "name" : "jumpingJackToLungeClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 756,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 1368,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 2124,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 2736,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 3492,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 4248,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 5004,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 5760,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 6516,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 7272,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 8028,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 8784,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 9540,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 10296,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 11052,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 11808,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 12564,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 13320,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 14076,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 14832,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 15588,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 16344,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 17100,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 17856,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 18468,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 19224,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 19980,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 20736,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 21492,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 22248,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 23004,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 23760,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 24516,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 25272,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 26028,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 26784,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 27540,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 28296,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 29052,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 29808,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 30564,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 31320,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 32076,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 32832,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 33588,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 34344,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 35100,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 35856,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 36612,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 37368,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-lunge-animation.ani",\r
+                "byteOffset" : 38124,\r
+                "numKeys" : 36\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.5\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 9 ]\r
+    }, {\r
+        "name" : "jumpingJackToSquatClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 525,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 950,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 1475,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 2000,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 2525,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 3050,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 3575,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 4100,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 4625,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 5150,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 5675,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 6200,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 6725,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 7250,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 7775,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 8300,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 8825,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 9350,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 9875,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 10400,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 10925,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 11450,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 11975,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/jumping-jack-to-squat-animation.ani",\r
+                "byteOffset" : 12500,\r
+                "numKeys" : 25\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.04167\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 10 ]\r
+    }, {\r
+        "name" : "lungeToJumpingJackClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 861,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 1558,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 2419,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 3116,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 3977,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 4838,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 5699,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 6560,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 7421,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 8282,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 9143,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 10004,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 10865,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 11726,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 12587,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 13448,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 14309,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 15170,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 16031,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 16892,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 17753,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 18614,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 19475,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 20336,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 21033,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 21894,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 22755,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 23616,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 24477,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 25338,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 26199,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 27060,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 27921,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 28782,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 29643,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 30504,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 31365,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 32226,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 33087,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 33948,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 34809,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 35670,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 36531,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 37392,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 38253,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 39114,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 39975,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 40836,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 41697,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 42558,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 43419,\r
+                "numKeys" : 41\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.70833\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 11 ]\r
+    }, {\r
+        "name" : "lungeToSquatClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 546,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 988,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 1534,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 1976,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 2522,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 3068,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 3614,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 4160,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 4706,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 5252,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 5798,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 6344,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 6890,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 7436,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 7982,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 8528,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 9074,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 9620,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 10166,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 10712,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 11258,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 11804,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 12350,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 12896,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 13338,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 13884,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 14430,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 14976,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 15522,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 16068,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 16614,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 17160,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 17706,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 18252,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 18798,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 19344,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 19890,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 20436,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 20982,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 21528,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 22074,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 22620,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 23166,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 23712,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 24258,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 24804,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 25350,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 25896,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 26442,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 26988,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/lunge-to-squat-animation.ani",\r
+                "byteOffset" : 27534,\r
+                "numKeys" : 26\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.08333\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 12 ]\r
+    }, {\r
+        "name" : "squatToJumpingJackClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 630,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 1140,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 1770,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 2400,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 3030,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 3660,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 4290,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 4920,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 5550,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_shoulder_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 6180,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 6690,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 7320,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 7950,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 8580,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 9210,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 9840,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 10470,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 11100,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 11730,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 12360,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 12990,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 13620,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 14250,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 14880,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 15510,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-jumping-jack-animation.ani",\r
+                "byteOffset" : 16140,\r
+                "numKeys" : 30\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.25\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 13 ]\r
+    }, {\r
+        "name" : "squatToLungeClip",\r
+        "loopCount" : 1,\r
+        "duration" : 0.0,\r
+        "endAction" : "BAKE",\r
+        "disconnectAction" : "BAKE",\r
+        "properties" : [ {\r
+            "node" : "hips_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 0,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "hips_JNT",\r
+            "property" : "position",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 504,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 912,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 1416,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "spine2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 1920,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 2424,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 2928,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 3432,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 3936,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 4440,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 4944,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 5448,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 5952,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 6456,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 6960,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 7464,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 7968,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 8472,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 8976,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 9480,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 9984,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 10488,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 10992,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_arm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 11496,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_forearm_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 12000,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_hand_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 12504,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 13008,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 13512,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handThumb3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 14016,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 14520,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 15024,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handRing3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 15528,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 16032,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 16536,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handPinky3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 17040,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 17544,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 18048,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handMiddle3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 18552,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex1_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 19056,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex2_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 19560,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_handIndex3_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 20064,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "neck_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 20568,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "head_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 21072,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 21576,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 22080,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 22584,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "r_toebase_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 23088,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_upleg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 23592,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_leg_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 24096,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        }, {\r
+            "node" : "l_foot_JNT",\r
+            "property" : "orientation",\r
+            "keyFramesBin" : {\r
+                "url" : "exercise/squat-to-lunge-animation.ani",\r
+                "byteOffset" : 24600,\r
+                "numKeys" : 24\r
+            },\r
+            "alphaFunction" : "DEFAULT",\r
+            "timePeriod" : {\r
+                "delay" : 0.0,\r
+                "duration" : 1.0\r
+            },\r
+            "relative" : false\r
+        } ],\r
+        "finishedActions" : [ 14 ]\r
+    } ],\r
+    "animationGroups" : [ {\r
+        "name" : "Idle",\r
+        "animations" : [ "idleClip" ]\r
+    }, {\r
+        "name" : "Squat",\r
+        "animations" : [ "squatClip_0", "squatClip_1" ]\r
+    }, {\r
+        "name" : "JumpingJack",\r
+        "animations" : [ "jumpingJackClip" ]\r
+    }, {\r
+        "name" : "Lunge",\r
+        "animations" : [ "lungeClip" ]\r
+    }, {\r
+        "name" : "IdleToSquat",\r
+        "animations" : [ "idleToSquatClip_0", "idleToSquatClip_1" ]\r
+    }, {\r
+        "name" : "SquatToIdle",\r
+        "animations" : [ "squatToIdleClip" ]\r
+    }, {\r
+        "name" : "IdleToJumpingJack",\r
+        "animations" : [ "idleToJumpingJackClip" ]\r
+    }, {\r
+        "name" : "JumpingJackToIdle",\r
+        "animations" : [ "jumpingJackToIdleClip" ]\r
+    }, {\r
+        "name" : "IdleToLunge",\r
+        "animations" : [ "idleToLungeClip" ]\r
+    }, {\r
+        "name" : "LungeToIdle",\r
+        "animations" : [ "lungeToIdleClip" ]\r
+    }, {\r
+        "name" : "JumpingJackToLunge",\r
+        "animations" : [ "jumpingJackToLungeClip" ]\r
+    }, {\r
+        "name" : "JumpingJackToSquat",\r
+        "animations" : [ "jumpingJackToSquatClip" ]\r
+    }, {\r
+        "name" : "LungeToJumpingJack",\r
+        "animations" : [ "lungeToJumpingJackClip" ]\r
+    }, {\r
+        "name" : "LungeToSquat",\r
+        "animations" : [ "lungeToSquatClip" ]\r
+    }, {\r
+        "name" : "SquatToJumpingJack",\r
+        "animations" : [ "squatToJumpingJackClip" ]\r
+    }, {\r
+        "name" : "SquatToLunge",\r
+        "animations" : [ "squatToLungeClip" ]\r
+    } ],\r
+    "skeletons" : [ {\r
+        "node" : "hips_JNT"\r
+    } ]\r
+}\r
diff --git a/automated-tests/resources/exercise/Icons/Icon_Idle.png b/automated-tests/resources/exercise/Icons/Icon_Idle.png
new file mode 100644 (file)
index 0000000..ee10734
Binary files /dev/null and b/automated-tests/resources/exercise/Icons/Icon_Idle.png differ
diff --git a/automated-tests/resources/exercise/Icons/Icon_JJ.png b/automated-tests/resources/exercise/Icons/Icon_JJ.png
new file mode 100644 (file)
index 0000000..1c690f5
Binary files /dev/null and b/automated-tests/resources/exercise/Icons/Icon_JJ.png differ
diff --git a/automated-tests/resources/exercise/Icons/Icon_Lunge.png b/automated-tests/resources/exercise/Icons/Icon_Lunge.png
new file mode 100644 (file)
index 0000000..8853b14
Binary files /dev/null and b/automated-tests/resources/exercise/Icons/Icon_Lunge.png differ
diff --git a/automated-tests/resources/exercise/Icons/Icon_Squat.png b/automated-tests/resources/exercise/Icons/Icon_Squat.png
new file mode 100644 (file)
index 0000000..f4f8ce3
Binary files /dev/null and b/automated-tests/resources/exercise/Icons/Icon_Squat.png differ
diff --git a/automated-tests/resources/exercise/Textures/BG_AlbedoMetallic.png b/automated-tests/resources/exercise/Textures/BG_AlbedoMetallic.png
new file mode 100644 (file)
index 0000000..45489d2
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/BG_AlbedoMetallic.png differ
diff --git a/automated-tests/resources/exercise/Textures/BG_NormalRoughness.png b/automated-tests/resources/exercise/Textures/BG_NormalRoughness.png
new file mode 100644 (file)
index 0000000..52cb66d
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/BG_NormalRoughness.png differ
diff --git a/automated-tests/resources/exercise/Textures/Body_AlbedoMetallic.png b/automated-tests/resources/exercise/Textures/Body_AlbedoMetallic.png
new file mode 100644 (file)
index 0000000..d8211a6
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/Body_AlbedoMetallic.png differ
diff --git a/automated-tests/resources/exercise/Textures/Body_Female_Asian_Adult_SubsurfaceColor.png b/automated-tests/resources/exercise/Textures/Body_Female_Asian_Adult_SubsurfaceColor.png
new file mode 100644 (file)
index 0000000..8df1878
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/Body_Female_Asian_Adult_SubsurfaceColor.png differ
diff --git a/automated-tests/resources/exercise/Textures/Body_NormalRoughness.png b/automated-tests/resources/exercise/Textures/Body_NormalRoughness.png
new file mode 100644 (file)
index 0000000..91d02cb
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/Body_NormalRoughness.png differ
diff --git a/automated-tests/resources/exercise/Textures/Eye_AlbedoMetallic.png b/automated-tests/resources/exercise/Textures/Eye_AlbedoMetallic.png
new file mode 100644 (file)
index 0000000..0137f24
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/Eye_AlbedoMetallic.png differ
diff --git a/automated-tests/resources/exercise/Textures/Eye_NormalRoughness.png b/automated-tests/resources/exercise/Textures/Eye_NormalRoughness.png
new file mode 100644 (file)
index 0000000..9d5971b
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/Eye_NormalRoughness.png differ
diff --git a/automated-tests/resources/exercise/Textures/FitBot_AlbedoMetallic.png b/automated-tests/resources/exercise/Textures/FitBot_AlbedoMetallic.png
new file mode 100644 (file)
index 0000000..d70a3de
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/FitBot_AlbedoMetallic.png differ
diff --git a/automated-tests/resources/exercise/Textures/FitBot_NormalRoughness.png b/automated-tests/resources/exercise/Textures/FitBot_NormalRoughness.png
new file mode 100644 (file)
index 0000000..fd47924
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/FitBot_NormalRoughness.png differ
diff --git a/automated-tests/resources/exercise/Textures/FitTop_AlbedoMetallic.png b/automated-tests/resources/exercise/Textures/FitTop_AlbedoMetallic.png
new file mode 100644 (file)
index 0000000..f19e93c
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/FitTop_AlbedoMetallic.png differ
diff --git a/automated-tests/resources/exercise/Textures/FitTop_NormalRoughness.png b/automated-tests/resources/exercise/Textures/FitTop_NormalRoughness.png
new file mode 100644 (file)
index 0000000..6c3e743
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/FitTop_NormalRoughness.png differ
diff --git a/automated-tests/resources/exercise/Textures/Hair_AlbedoMetallic.png b/automated-tests/resources/exercise/Textures/Hair_AlbedoMetallic.png
new file mode 100644 (file)
index 0000000..8b96834
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/Hair_AlbedoMetallic.png differ
diff --git a/automated-tests/resources/exercise/Textures/Hair_NormalRoughness.png b/automated-tests/resources/exercise/Textures/Hair_NormalRoughness.png
new file mode 100644 (file)
index 0000000..9d643ab
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/Hair_NormalRoughness.png differ
diff --git a/automated-tests/resources/exercise/Textures/Head_AlbedoMetallic.png b/automated-tests/resources/exercise/Textures/Head_AlbedoMetallic.png
new file mode 100644 (file)
index 0000000..568a115
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/Head_AlbedoMetallic.png differ
diff --git a/automated-tests/resources/exercise/Textures/Head_Female_SubsurfaceColor.png b/automated-tests/resources/exercise/Textures/Head_Female_SubsurfaceColor.png
new file mode 100644 (file)
index 0000000..609ce5b
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/Head_Female_SubsurfaceColor.png differ
diff --git a/automated-tests/resources/exercise/Textures/Head_NormalRoughness.png b/automated-tests/resources/exercise/Textures/Head_NormalRoughness.png
new file mode 100644 (file)
index 0000000..b2eb952
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/Head_NormalRoughness.png differ
diff --git a/automated-tests/resources/exercise/Textures/Shoe_AlbedoMetallic.png b/automated-tests/resources/exercise/Textures/Shoe_AlbedoMetallic.png
new file mode 100644 (file)
index 0000000..6bf07f9
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/Shoe_AlbedoMetallic.png differ
diff --git a/automated-tests/resources/exercise/Textures/Shoe_NormalRoughness.png b/automated-tests/resources/exercise/Textures/Shoe_NormalRoughness.png
new file mode 100644 (file)
index 0000000..f953ec0
Binary files /dev/null and b/automated-tests/resources/exercise/Textures/Shoe_NormalRoughness.png differ
diff --git a/automated-tests/resources/exercise/idle-animation.ani b/automated-tests/resources/exercise/idle-animation.ani
new file mode 100644 (file)
index 0000000..02f76fd
Binary files /dev/null and b/automated-tests/resources/exercise/idle-animation.ani differ
diff --git a/automated-tests/resources/exercise/idle-to-jumping-jack-animation.ani b/automated-tests/resources/exercise/idle-to-jumping-jack-animation.ani
new file mode 100644 (file)
index 0000000..74b8647
Binary files /dev/null and b/automated-tests/resources/exercise/idle-to-jumping-jack-animation.ani differ
diff --git a/automated-tests/resources/exercise/idle-to-lunge-animation.ani b/automated-tests/resources/exercise/idle-to-lunge-animation.ani
new file mode 100644 (file)
index 0000000..81743dd
Binary files /dev/null and b/automated-tests/resources/exercise/idle-to-lunge-animation.ani differ
diff --git a/automated-tests/resources/exercise/idle-to-squat-animation-0.ani b/automated-tests/resources/exercise/idle-to-squat-animation-0.ani
new file mode 100644 (file)
index 0000000..f7d792c
Binary files /dev/null and b/automated-tests/resources/exercise/idle-to-squat-animation-0.ani differ
diff --git a/automated-tests/resources/exercise/idle-to-squat-animation-1.ani b/automated-tests/resources/exercise/idle-to-squat-animation-1.ani
new file mode 100644 (file)
index 0000000..e0ec040
Binary files /dev/null and b/automated-tests/resources/exercise/idle-to-squat-animation-1.ani differ
diff --git a/automated-tests/resources/exercise/jumping-jack-animation.ani b/automated-tests/resources/exercise/jumping-jack-animation.ani
new file mode 100644 (file)
index 0000000..8bd679d
Binary files /dev/null and b/automated-tests/resources/exercise/jumping-jack-animation.ani differ
diff --git a/automated-tests/resources/exercise/jumping-jack-to-idle-animation.ani b/automated-tests/resources/exercise/jumping-jack-to-idle-animation.ani
new file mode 100644 (file)
index 0000000..6d143e1
Binary files /dev/null and b/automated-tests/resources/exercise/jumping-jack-to-idle-animation.ani differ
diff --git a/automated-tests/resources/exercise/jumping-jack-to-lunge-animation.ani b/automated-tests/resources/exercise/jumping-jack-to-lunge-animation.ani
new file mode 100644 (file)
index 0000000..08c588b
Binary files /dev/null and b/automated-tests/resources/exercise/jumping-jack-to-lunge-animation.ani differ
diff --git a/automated-tests/resources/exercise/jumping-jack-to-squat-animation.ani b/automated-tests/resources/exercise/jumping-jack-to-squat-animation.ani
new file mode 100644 (file)
index 0000000..ae66e77
Binary files /dev/null and b/automated-tests/resources/exercise/jumping-jack-to-squat-animation.ani differ
diff --git a/automated-tests/resources/exercise/lunge-animation.ani b/automated-tests/resources/exercise/lunge-animation.ani
new file mode 100644 (file)
index 0000000..3912665
Binary files /dev/null and b/automated-tests/resources/exercise/lunge-animation.ani differ
diff --git a/automated-tests/resources/exercise/lunge-to-idle-animation.ani b/automated-tests/resources/exercise/lunge-to-idle-animation.ani
new file mode 100644 (file)
index 0000000..d88c844
Binary files /dev/null and b/automated-tests/resources/exercise/lunge-to-idle-animation.ani differ
diff --git a/automated-tests/resources/exercise/lunge-to-jumping-jack-animation.ani b/automated-tests/resources/exercise/lunge-to-jumping-jack-animation.ani
new file mode 100644 (file)
index 0000000..118ffa1
Binary files /dev/null and b/automated-tests/resources/exercise/lunge-to-jumping-jack-animation.ani differ
diff --git a/automated-tests/resources/exercise/lunge-to-squat-animation.ani b/automated-tests/resources/exercise/lunge-to-squat-animation.ani
new file mode 100644 (file)
index 0000000..6d435de
Binary files /dev/null and b/automated-tests/resources/exercise/lunge-to-squat-animation.ani differ
diff --git a/automated-tests/resources/exercise/model.dae.bin b/automated-tests/resources/exercise/model.dae.bin
new file mode 100644 (file)
index 0000000..c894ae9
Binary files /dev/null and b/automated-tests/resources/exercise/model.dae.bin differ
diff --git a/automated-tests/resources/exercise/squat-animation-0.ani b/automated-tests/resources/exercise/squat-animation-0.ani
new file mode 100644 (file)
index 0000000..435aaeb
Binary files /dev/null and b/automated-tests/resources/exercise/squat-animation-0.ani differ
diff --git a/automated-tests/resources/exercise/squat-animation-1.ani b/automated-tests/resources/exercise/squat-animation-1.ani
new file mode 100644 (file)
index 0000000..6fd09cb
Binary files /dev/null and b/automated-tests/resources/exercise/squat-animation-1.ani differ
diff --git a/automated-tests/resources/exercise/squat-to-idle-animation.ani b/automated-tests/resources/exercise/squat-to-idle-animation.ani
new file mode 100644 (file)
index 0000000..1bfef9a
Binary files /dev/null and b/automated-tests/resources/exercise/squat-to-idle-animation.ani differ
diff --git a/automated-tests/resources/exercise/squat-to-jumping-jack-animation.ani b/automated-tests/resources/exercise/squat-to-jumping-jack-animation.ani
new file mode 100644 (file)
index 0000000..82316cb
Binary files /dev/null and b/automated-tests/resources/exercise/squat-to-jumping-jack-animation.ani differ
diff --git a/automated-tests/resources/exercise/squat-to-lunge-animation.ani b/automated-tests/resources/exercise/squat-to-lunge-animation.ani
new file mode 100644 (file)
index 0000000..c967b9a
Binary files /dev/null and b/automated-tests/resources/exercise/squat-to-lunge-animation.ani differ
diff --git a/automated-tests/resources/forest_irradiance.ktx b/automated-tests/resources/forest_irradiance.ktx
new file mode 100644 (file)
index 0000000..c6b2332
Binary files /dev/null and b/automated-tests/resources/forest_irradiance.ktx differ
diff --git a/automated-tests/resources/forest_radiance.ktx b/automated-tests/resources/forest_radiance.ktx
new file mode 100644 (file)
index 0000000..38f7f1a
Binary files /dev/null and b/automated-tests/resources/forest_radiance.ktx differ
diff --git a/automated-tests/resources/invalid.gltf b/automated-tests/resources/invalid.gltf
new file mode 100644 (file)
index 0000000..a9e1c8a
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "error": yes
+}
diff --git a/automated-tests/resources/morph.dli b/automated-tests/resources/morph.dli
new file mode 100644 (file)
index 0000000..9f9a5a2
--- /dev/null
@@ -0,0 +1,280 @@
+{
+    "metadata" : [ {
+        "key" : "clock-type",
+        "value" : "com.samsung.watchface-01"
+    }, {
+        "key" : "tickpersecond",
+        "value" : "1"
+    }, {
+        "key" : "preview_time",
+        "value" : "10:08:32"
+    }, {
+        "key" : "tilt-mode",
+        "value" : "light"
+    } ],
+    "asset" : {
+        "version" : "1.0"
+    },
+    "scene" : 0,
+    "scenes" : [ {
+        "nodes" : [ 0 ]
+    } ],
+    "nodes" : [ {
+        "name" : "HeadTest_002",
+        "matrix" : [ 0.01, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+        "children" : [ 1, 2 ],
+        "visible" : true,
+        "lightingMode" : "unlit"
+    }, {
+        "name" : "head",
+        "matrix" : [ 0.819152044288992, 0.0, 0.5735764363510462, 0.0, 0.0, 1.0, 0.0, 0.0, -0.5735764363510462, 0.0, 0.819152044288992, 0.0, 0.0, -143.995, -1.1208, 1.0 ],
+        "behavior" : [ 0 ],
+        "visible" : true,
+        "lightingMode" : "lit-solid",
+        "model" : {
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],
+            "material" : 0,
+            "mesh" : 0,
+            "shader" : 3
+        }
+    }, {
+        "name" : "Cube",
+        "matrix" : [ 25.98076211353316, 0.0, -15.0, 0.0, 0.0, 30.0, 0.0, 0.0, 15.0, 0.0, 25.98076211353316, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+        "visible" : true,
+        "lightingMode" : "lit",
+        "model" : {
+            "color" : [ 1.0, 1.0, 1.0, 1.0 ],
+            "material" : 0,
+            "mesh" : 1,
+            "shader" : 4
+        }
+    } ],
+    "meshes" : [ {
+        "uri" : "morph/HeadTest_002.dae.bin",
+        "attributes" : 31,
+        "primitive" : "TRIANGLES",
+        "indices" : {
+            "byteOffset" : 0,
+            "byteLength" : 34524
+        },
+        "positions" : {
+            "byteOffset" : 34524,
+            "byteLength" : 37812
+        },
+        "normals" : {
+            "byteOffset" : 72336,
+            "byteLength" : 37812
+        },
+        "textures" : {
+            "byteOffset" : 110148,
+            "byteLength" : 25208
+        },
+        "tangents" : {
+            "byteOffset" : 135356,
+            "byteLength" : 37812
+        },
+        "blendShapeHeader" : {
+            "version" : "1.0",
+            "byteOffset" : 173168,
+            "byteLength" : 4
+        },
+        "blendShapes" : [ {
+            "name" : "Shape_01MeshMesh",
+            "weight" : 0.0,
+            "positions" : {
+                "byteOffset" : 173172,
+                "byteLength" : 37812
+            },
+            "normals" : {
+                "byteOffset" : 210984,
+                "byteLength" : 37812
+            }
+        }, {
+            "name" : "Shape_02MeshMesh",
+            "weight" : 0.0,
+            "positions" : {
+                "byteOffset" : 248796,
+                "byteLength" : 37812
+            },
+            "normals" : {
+                "byteOffset" : 286608,
+                "byteLength" : 37812
+            }
+        }, {
+            "name" : "Shape_03MeshMesh",
+            "weight" : 0.0,
+            "positions" : {
+                "byteOffset" : 324420,
+                "byteLength" : 37812
+            },
+            "normals" : {
+                "byteOffset" : 362232,
+                "byteLength" : 37812
+            }
+        } ]
+    }, {
+        "uri" : "morph/cube.gltf.bin",
+        "attributes" : 31,
+        "primitive" : "TRIANGLES",
+        "indices" : {
+            "byteOffset" : 0,
+            "byteLength" : 72
+        },
+        "positions" : {
+            "byteOffset" : 72,
+            "byteLength" : 288
+        },
+        "normals" : {
+            "byteOffset" : 360,
+            "byteLength" : 288
+        },
+        "textures" : {
+            "byteOffset" : 648,
+            "byteLength" : 192
+        },
+        "tangents" : {
+            "byteOffset" : 840,
+            "byteLength" : 288
+        }
+    } ],
+    "cameras" : [ {
+        "near" : 0.1,
+        "far" : 10000.0,
+        "matrix" : [ 0.01, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.0, 0.01, 0.0, 0.0, 0.0, 0.767773, 1.0 ],
+        "fov" : 54.4321
+    } ],
+    "lights" : [ {
+        "matrix" : [ 0.4999999403953552, 0.0, 0.8660255074501038, 0.0, 0.0, 1.0, 0.0, 0.0, -0.8660255074501038, 0.0, 0.4999999403953552, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+        "color" : [ 1.0, 1.0, 1.0 ],
+        "intensity" : 1.0,
+        "shadowMapSize" : 1024,
+        "orthographicSize" : 1.0,
+        "shadowIntensity" : 1.0
+    } ],
+    "materials" : [ {
+        "name" : "No name",
+        "color" : [ 1.0, 1.0, 1.0, 1.0 ],
+        "mipmap" : true,
+        "roughness" : 1.0,
+        "metallic" : 1.0,
+        "environment" : 1
+    } ],
+    "environment" : [ {
+        "cubeInitialOrientation" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+        "iblIntensity" : 1.0
+    }, {
+        "cubeSpecular" : "Studio/Radiance.ktx",
+        "cubeDiffuse" : "Studio/Irradiance.ktx",
+        "cubeInitialOrientation" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+        "iblIntensity" : 1.0
+    } ],
+    "shaders" : [ {
+        "vertex" : "dli_pbr.vsh",
+        "fragment" : "dli_pbr.fsh",
+        "defines" : [ "LIT" ],
+        "rendererState" : "DEPTH_TEST|DEPTH_WRITE|CULL_BACK|DEPTH_FUNC:LESS_EQUAL",
+        "uCubeMatrix" : [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
+        "uMaxLOD" : 6
+    }, {
+        "vertex" : "dli_pbr.vsh",
+        "fragment" : "dli_pbr.fsh",
+        "defines" : [ "THREE_TEX" ],
+        "rendererState" : "DEPTH_TEST|ALPHA_BLEND",
+        "uCubeMatrix" : [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
+        "uMaxLOD" : 6
+    }, {
+        "vertex" : "dli_pbr.vsh",
+        "fragment" : "dli_pbr.fsh",
+        "defines" : [ "MORPH", "MORPH_POSITION", "MORPH_NORMAL" ],
+        "rendererState" : "DEPTH_TEST|DEPTH_WRITE|CULL_BACK|DEPTH_FUNC:LESS_EQUAL",
+        "uCubeMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+        "uMaxLOD" : 6
+    }, {
+        "vertex" : "dli_pbr.vsh",
+        "fragment" : "dli_pbr.fsh",
+        "hints" : [ "MODIFIES_GEOMETRY" ],
+        "defines" : [ "MORPH", "MORPH_POSITION", "MORPH_NORMAL" ],
+        "rendererState" : "DEPTH_TEST|DEPTH_WRITE|CULL_BACK|DEPTH_FUNC:LESS_EQUAL",
+        "uCubeMatrix" : [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ],
+        "uMaxLOD" : 6
+    }, {
+        "vertex" : "dli_pbr.vsh",
+        "fragment" : "dli_pbr.fsh",
+        "defines" : [ "LIT", "SHADOW" ],
+        "rendererState" : "DEPTH_TEST|DEPTH_WRITE|CULL_FRONT",
+        "uCubeMatrix" : [ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 ],
+        "uMaxLOD" : 6
+    } ],
+    "behaviors" : [ {
+        "url" : "head-behavior_1.lua",
+        "event" : "scene.loaded"
+    } ],
+    "animations" : [ {
+        "name" : "Morph",
+        "loopCount" : 0,
+        "duration" : 1.0,
+        "endAction" : "DISCARD",
+        "disconnectAction" : "BAKE",
+        "properties" : [ {
+            "node" : "head",
+            "property" : "uBlendShapeWeight[0]",
+            "value" : 1.0,
+            "alphaFunction" : "DEFAULT",
+            "timePeriod" : {
+                "delay" : 0.0,
+                "duration" : 0.5
+            },
+            "relative" : false
+        }, {
+            "node" : "head",
+            "property" : "uBlendShapeWeight[1]",
+            "value" : 1.0,
+            "alphaFunction" : "DEFAULT",
+            "timePeriod" : {
+                "delay" : 0.0,
+                "duration" : 0.5
+            },
+            "relative" : false
+        }, {
+            "node" : "head",
+            "property" : "uBlendShapeWeight[2]",
+            "value" : 1.0,
+            "alphaFunction" : "DEFAULT",
+            "timePeriod" : {
+                "delay" : 0.0,
+                "duration" : 0.5
+            },
+            "relative" : false
+        }, {
+            "node" : "head",
+            "property" : "uBlendShapeWeight[0]",
+            "value" : 0.0,
+            "alphaFunction" : "DEFAULT",
+            "timePeriod" : {
+                "delay" : 0.5,
+                "duration" : 0.5
+            },
+            "relative" : false
+        }, {
+            "node" : "head",
+            "property" : "uBlendShapeWeight[1]",
+            "value" : 0.0,
+            "alphaFunction" : "DEFAULT",
+            "timePeriod" : {
+                "delay" : 0.5,
+                "duration" : 0.5
+            },
+            "relative" : false
+        }, {
+            "node" : "head",
+            "property" : "uBlendShapeWeight[2]",
+            "value" : 0.0,
+            "alphaFunction" : "DEFAULT",
+            "timePeriod" : {
+                "delay" : 0.5,
+                "duration" : 0.5
+            },
+            "relative" : false
+        } ]
+    } ]
+}
diff --git a/automated-tests/resources/morph/HeadTest_002.dae.bin b/automated-tests/resources/morph/HeadTest_002.dae.bin
new file mode 100644 (file)
index 0000000..5fcc8c5
Binary files /dev/null and b/automated-tests/resources/morph/HeadTest_002.dae.bin differ
diff --git a/automated-tests/resources/morph/cube.gltf.bin b/automated-tests/resources/morph/cube.gltf.bin
new file mode 100644 (file)
index 0000000..8d715e4
Binary files /dev/null and b/automated-tests/resources/morph/cube.gltf.bin differ
diff --git a/automated-tests/resources/simpleTriangle.bin b/automated-tests/resources/simpleTriangle.bin
new file mode 100644 (file)
index 0000000..d642500
Binary files /dev/null and b/automated-tests/resources/simpleTriangle.bin differ
diff --git a/automated-tests/resources/truncated.ktx b/automated-tests/resources/truncated.ktx
new file mode 100644 (file)
index 0000000..a2dbcf1
Binary files /dev/null and b/automated-tests/resources/truncated.ktx differ
diff --git a/automated-tests/src/dali-scene-loader-internal/CMakeLists.txt b/automated-tests/src/dali-scene-loader-internal/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..14c7edd
--- /dev/null
@@ -0,0 +1,88 @@
+SET(PKG_NAME "dali-scene-loader-internal")
+
+SET(EXEC_NAME "tct-${PKG_NAME}-core")
+SET(RPM_NAME "core-${PKG_NAME}-tests")
+
+SET(CAPI_LIB "dali-scene-loader")
+
+# List of test case sources (Only these get parsed for test cases)
+SET(TC_SOURCES
+  utc-Dali-Gltf2Asset.cpp
+  utc-Dali-Hash.cpp
+  utc-Dali-JsonReader.cpp
+  utc-Dali-JsonUtil.cpp
+)
+
+# List of test harness files (Won't get parsed for test cases)
+SET(TEST_HARNESS_DIR "../dali-toolkit/dali-toolkit-test-utils")
+
+SET(TEST_HARNESS_SOURCES
+  ${TEST_HARNESS_DIR}/toolkit-adaptor.cpp
+  ${TEST_HARNESS_DIR}/toolkit-application.cpp
+  ${TEST_HARNESS_DIR}/toolkit-event-thread-callback.cpp
+  ${TEST_HARNESS_DIR}/toolkit-environment-variable.cpp
+  ${TEST_HARNESS_DIR}/toolkit-input-method-context.cpp
+  ${TEST_HARNESS_DIR}/toolkit-input-method-options.cpp
+  ${TEST_HARNESS_DIR}/toolkit-lifecycle-controller.cpp
+  ${TEST_HARNESS_DIR}/toolkit-orientation.cpp
+  ${TEST_HARNESS_DIR}/toolkit-style-monitor.cpp
+  ${TEST_HARNESS_DIR}/toolkit-test-application.cpp
+  ${TEST_HARNESS_DIR}/toolkit-timer.cpp
+  ${TEST_HARNESS_DIR}/toolkit-trigger-event-factory.cpp
+  ${TEST_HARNESS_DIR}/toolkit-window.cpp
+  ${TEST_HARNESS_DIR}/toolkit-scene-holder.cpp
+  ${TEST_HARNESS_DIR}/dali-test-suite-utils.cpp
+  ${TEST_HARNESS_DIR}/dali-toolkit-test-suite-utils.cpp
+  ${TEST_HARNESS_DIR}/dummy-control.cpp
+  ${TEST_HARNESS_DIR}/mesh-builder.cpp
+  ${TEST_HARNESS_DIR}/test-actor-utils.cpp
+  ${TEST_HARNESS_DIR}/test-animation-data.cpp
+  ${TEST_HARNESS_DIR}/test-application.cpp
+  ${TEST_HARNESS_DIR}/test-button.cpp
+  ${TEST_HARNESS_DIR}/test-harness.cpp
+  ${TEST_HARNESS_DIR}/test-gesture-generator.cpp
+  ${TEST_HARNESS_DIR}/test-gl-abstraction.cpp
+  ${TEST_HARNESS_DIR}/test-gl-sync-abstraction.cpp
+  ${TEST_HARNESS_DIR}/test-platform-abstraction.cpp
+  ${TEST_HARNESS_DIR}/test-render-controller.cpp
+  ${TEST_HARNESS_DIR}/test-trace-call-stack.cpp
+)
+
+PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED
+  dali2-core
+  dali2-adaptor
+  dali2-toolkit
+  dali2-scene-loader
+)
+
+ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror -DDEBUG_ENABLED)
+ADD_COMPILE_OPTIONS( ${${CAPI_LIB}_CFLAGS_OTHER} )
+
+ADD_DEFINITIONS(-DTEST_RESOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../resources\" )
+
+FOREACH(directory ${${CAPI_LIB}_LIBRARY_DIRS})
+    SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -L${directory}")
+ENDFOREACH(directory ${CAPI_LIB_LIBRARY_DIRS})
+
+INCLUDE_DIRECTORIES(
+    ../../../
+    ${${CAPI_LIB}_INCLUDE_DIRS}
+    ../dali-toolkit/dali-toolkit-test-utils
+)
+
+ADD_CUSTOM_COMMAND(
+  COMMAND ${SCRIPT_DIR}/tcheadgen.sh ${EXEC_NAME}.h ${TC_SOURCES}
+  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  OUTPUT ${EXEC_NAME}.h
+  COMMENT "Generating test tables"
+  )
+
+ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.h ${EXEC_NAME}.cpp ${TC_SOURCES} ${TEST_HARNESS_SOURCES})
+TARGET_LINK_LIBRARIES(${EXEC_NAME}
+    ${${CAPI_LIB}_LIBRARIES}
+    -lpthread --coverage
+)
+
+INSTALL(PROGRAMS ${EXEC_NAME}
+    DESTINATION ${BIN_DIR}/${EXEC_NAME}
+)
diff --git a/automated-tests/src/dali-scene-loader-internal/tct-dali-scene-loader-internal-core.cpp b/automated-tests/src/dali-scene-loader-internal/tct-dali-scene-loader-internal-core.cpp
new file mode 100644 (file)
index 0000000..9dea347
--- /dev/null
@@ -0,0 +1,51 @@
+#include <string.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <test-harness.h>
+#include "tct-dali-scene-loader-internal-core.h"
+
+int main(int argc, char * const argv[])
+{
+  int result = TestHarness::EXIT_STATUS_BAD_ARGUMENT;
+
+  const char* optString = "sf";
+  bool optRerunFailed(true);
+  bool optRunSerially(false);
+
+  int nextOpt = 0;
+  do
+  {
+    nextOpt = getopt( argc, argv, optString );
+    switch(nextOpt)
+    {
+      case 'f':
+        optRerunFailed = false;
+        break;
+      case 's':
+        optRunSerially = true;
+        break;
+      case '?':
+        TestHarness::Usage(argv[0]);
+        exit(TestHarness::EXIT_STATUS_BAD_ARGUMENT);
+        break;
+    }
+  } while( nextOpt != -1 );
+
+  if( optind == argc ) // no testcase name in argument list
+  {
+    if( optRunSerially )
+    {
+      result = TestHarness::RunAll( argv[0], tc_array );
+    }
+    else
+    {
+      result = TestHarness::RunAllInParallel( argv[0], tc_array, optRerunFailed );
+    }
+  }
+  else
+  {
+    // optind is index of next argument - interpret as testcase name
+    result = TestHarness::FindAndRunTestCase(tc_array, argv[optind]);
+  }
+  return result;
+}
diff --git a/automated-tests/src/dali-scene-loader-internal/utc-Dali-Gltf2Asset.cpp b/automated-tests/src/dali-scene-loader-internal/utc-Dali-Gltf2Asset.cpp
new file mode 100644 (file)
index 0000000..8574137
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/internal/gltf2-asset.h"
+#include <dali-test-suite-utils.h>
+#include <string>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+int UtcDaliGltf2AssetComponentIsUnsigned(void)
+{
+  DALI_TEST_EQUAL(gltf2::Component::IsUnsigned(gltf2::Component::BYTE), false);
+  DALI_TEST_EQUAL(gltf2::Component::IsUnsigned(gltf2::Component::UNSIGNED_BYTE), true);
+  DALI_TEST_EQUAL(gltf2::Component::IsUnsigned(gltf2::Component::SHORT), false);
+  DALI_TEST_EQUAL(gltf2::Component::IsUnsigned(gltf2::Component::UNSIGNED_SHORT), true);
+  DALI_TEST_EQUAL(gltf2::Component::IsUnsigned(gltf2::Component::UNSIGNED_INT), true);
+  DALI_TEST_EQUAL(gltf2::Component::IsUnsigned(gltf2::Component::FLOAT), false);
+  DALI_TEST_EQUAL(gltf2::Component::IsUnsigned(gltf2::Component::INVALID), false);
+
+  END_TEST;
+}
+
+int UtcDaliGltf2AssetComponentSize(void)
+{
+  DALI_TEST_EQUAL(gltf2::Component::Size(gltf2::Component::BYTE), 1u);
+  DALI_TEST_EQUAL(gltf2::Component::Size(gltf2::Component::UNSIGNED_BYTE), 1u);
+  DALI_TEST_EQUAL(gltf2::Component::Size(gltf2::Component::SHORT), 2u);
+  DALI_TEST_EQUAL(gltf2::Component::Size(gltf2::Component::UNSIGNED_SHORT), 2u);
+  DALI_TEST_EQUAL(gltf2::Component::Size(gltf2::Component::UNSIGNED_INT), 4u);
+  DALI_TEST_EQUAL(gltf2::Component::Size(gltf2::Component::FLOAT), 4u);
+  DALI_TEST_EQUAL(gltf2::Component::Size(gltf2::Component::INVALID), -1);
+
+  END_TEST;
+}
+
+#define FROM_STRING_HELPER(x) FromString(#x, strlen(#x))
+
+#define STRING_CHECK(type, x) DALI_TEST_EQUAL(gltf2::type:: FROM_STRING_HELPER(x), gltf2::type::x)
+
+int UtcDaliGltf2AssetAccessorType(void)
+{
+  STRING_CHECK(AccessorType, SCALAR);
+  STRING_CHECK(AccessorType, VEC2);
+  STRING_CHECK(AccessorType, VEC3);
+  STRING_CHECK(AccessorType, VEC4);
+  STRING_CHECK(AccessorType, MAT2);
+  STRING_CHECK(AccessorType, MAT3);
+  STRING_CHECK(AccessorType, MAT4);
+  DALI_TEST_EQUAL(gltf2::AccessorType::FROM_STRING_HELPER(VEC88), gltf2::AccessorType::INVALID);
+
+  END_TEST;
+}
+
+int UtcDaliGltf2AssetAlphaMode(void)
+{
+  STRING_CHECK(AlphaMode, OPAQUE);
+  STRING_CHECK(AlphaMode, MASK);
+  STRING_CHECK(AlphaMode, BLEND);
+  DALI_TEST_EQUAL(gltf2::AlphaMode::FROM_STRING_HELPER(ALPHA_SCHMALPHA), gltf2::AlphaMode::INVALID);
+
+  END_TEST;
+}
+
+int UtcDaliGltf2AssetAttribute(void)
+{
+  STRING_CHECK(Attribute, POSITION);
+  STRING_CHECK(Attribute, NORMAL);
+  STRING_CHECK(Attribute, TANGENT);
+  STRING_CHECK(Attribute, TEXCOORD_0);
+  STRING_CHECK(Attribute, TEXCOORD_1);
+  STRING_CHECK(Attribute, COLOR_0);
+  STRING_CHECK(Attribute, JOINTS_0);
+  STRING_CHECK(Attribute, WEIGHTS_0);
+  DALI_TEST_EQUAL(gltf2::Attribute::FROM_STRING_HELPER(VISCOSITY), gltf2::Attribute::INVALID);
+
+  END_TEST;
+}
+
+int UtcDaliGltf2AssetAnimationSamplerInterpolation(void)
+{
+  STRING_CHECK(Animation::Sampler::Interpolation, STEP);
+  STRING_CHECK(Animation::Sampler::Interpolation, LINEAR);
+  STRING_CHECK(Animation::Sampler::Interpolation, CUBICSPLINE);
+  DALI_TEST_EQUAL(gltf2::Animation::Sampler::Interpolation::FROM_STRING_HELPER(EASE_IN_OUT), gltf2::Animation::Sampler::Interpolation::INVALID);
+
+  END_TEST;
+}
+
+int UtcDaliGltf2AssetAnimationChannelTarget(void)
+{
+  STRING_CHECK(Animation::Channel::Target, TRANSLATION);
+  STRING_CHECK(Animation::Channel::Target, ROTATION);
+  STRING_CHECK(Animation::Channel::Target, SCALE);
+  STRING_CHECK(Animation::Channel::Target, WEIGHTS);
+  DALI_TEST_EQUAL(gltf2::Animation::Channel::Target::FROM_STRING_HELPER(FLUFFINESS), gltf2::Animation::Channel::Target::INVALID);
+
+  END_TEST;
+}
+
+int UtcDaliGltf2AssetAccessorSparse(void)
+{
+  gltf2::Accessor acc;
+  DALI_TEST_CHECK(!acc.mSparse);
+
+  std::vector<gltf2::BufferView> bufferViews;
+
+  gltf2::Accessor::Sparse sparse{ 256u };
+  sparse.mIndices.mBufferView = gltf2::Ref<gltf2::BufferView>(bufferViews, 5u);
+  sparse.mIndices.mComponentType = gltf2::Component::FLOAT;
+  sparse.mValues.mBufferView = gltf2::Ref<gltf2::BufferView>(bufferViews, 284u);
+  sparse.mValues.mByteOffset = 16532;
+  acc.SetSparse(sparse);
+
+  DALI_TEST_EQUAL(acc.mSparse->mCount, sparse.mCount);
+  DALI_TEST_EQUAL(acc.mSparse->mIndices.mBufferView, sparse.mIndices.mBufferView);
+  DALI_TEST_EQUAL(acc.mSparse->mIndices.mByteOffset, sparse.mIndices.mByteOffset);
+  DALI_TEST_EQUAL(acc.mSparse->mIndices.mComponentType, sparse.mIndices.mComponentType);
+  DALI_TEST_EQUAL(acc.mSparse->mValues.mBufferView, sparse.mValues.mBufferView);
+  DALI_TEST_EQUAL(acc.mSparse->mValues.mByteOffset, sparse.mValues.mByteOffset);
+
+  END_TEST;
+}
+
diff --git a/automated-tests/src/dali-scene-loader-internal/utc-Dali-Hash.cpp b/automated-tests/src/dali-scene-loader-internal/utc-Dali-Hash.cpp
new file mode 100644 (file)
index 0000000..efd5053
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/internal/hash.h"
+#include <dali-test-suite-utils.h>
+#include <string>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+int UtcDaliHash(void)
+{
+  DALI_TEST_EQUAL(Hash(0).Add(true), 0);
+  DALI_TEST_EQUAL(Hash(0).Add(false), 1);
+
+  int32_t myInt32 = std::numeric_limits<int32_t>::min();
+  DALI_TEST_EQUAL(Hash(0).Add(myInt32), myInt32);
+
+  uint32_t myUint32 = std::numeric_limits<uint32_t>::max();
+  DALI_TEST_EQUAL(Hash(0).Add(myUint32), myUint32);
+
+  uint64_t myUint64 = std::numeric_limits<uint64_t>::max();
+  DALI_TEST_EQUAL(uint64_t(Hash(0).Add(myUint64)), myUint64);
+
+  constexpr uint32_t multiplier = 31;
+  uint64_t expected = 0;
+  float f = 1928.46852;
+  for (auto i0 = reinterpret_cast<uint8_t const*>(&f), i1 = i0 + sizeof(f); i0 != i1; ++i0)
+  {
+    expected = expected * multiplier + *i0;
+  }
+
+  DALI_TEST_EQUAL(uint64_t(Hash(0).Add(f)), expected);
+
+  END_TEST;
+}
+
diff --git a/automated-tests/src/dali-scene-loader-internal/utc-Dali-JsonReader.cpp b/automated-tests/src/dali-scene-loader-internal/utc-Dali-JsonReader.cpp
new file mode 100644 (file)
index 0000000..6b7e000
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/internal/json-reader.h"
+#include <dali-test-suite-utils.h>
+#include <string>
+
+using namespace Dali;
+
+#define JSON_STRING(x) #x, strlen(#x)
+
+int UtcDaliJsonReaderStrCmp(void)
+{
+  json_string_s jstr[] {
+    { JSON_STRING(hello) },
+    { JSON_STRING(hellew) },
+  };
+  DALI_TEST_EQUAL(json::StrCmp(jstr[0], "hello"), 0);
+  DALI_TEST_EQUAL(json::StrCmp(jstr[1], "hello"), 'e' - 'o');
+
+  END_TEST;
+}
+
+int UtcDaliJsonReaderValidateThrow(void)
+{
+  json_value_s jval { nullptr, json_type_array };
+  DALI_TEST_THROWS(json::Validate(jval, json_type_object), std::runtime_error);
+  json::Validate(jval, json_type_array);
+
+  END_TEST;
+}
+
+int UtcDaliJsonReaderFindObjectChild(void)
+{
+  json_string_s jkey{ JSON_STRING(fudgeFactor) };
+  json_number_s jActualValue{ JSON_STRING(5.2) };
+  json_value_s jvalue { &jActualValue, json_type_number };
+  json_object_element_s jobjelem { &jkey, &jvalue, nullptr };
+
+  json_object_s jobj{ &jobjelem, 1 };
+
+  DALI_TEST_EQUAL(json::FindObjectChild(jkey.string, jobj), &jvalue);
+  DALI_TEST_EQUAL(json::FindObjectChild("fudgeFactory", jobj), static_cast<json_value_s*>(nullptr));
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader-internal/utc-Dali-JsonUtil.cpp b/automated-tests/src/dali-scene-loader-internal/utc-Dali-JsonUtil.cpp
new file mode 100644 (file)
index 0000000..41ac039
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/internal/json-util.h"
+#include "dali-toolkit/devel-api/builder/json-parser.h"
+#include <dali-test-suite-utils.h>
+#include <string>
+
+#define STRINGIFY(x) #x
+
+using namespace Dali;
+using namespace Dali::Toolkit;
+using namespace Dali::SceneLoader;
+
+namespace
+{
+const std::string TEST_JSON =
+  "{ \"int\": 17834,"
+  "\"float\": 3.1415628,"
+  "\"bool\": true,"
+  "\"null\": null,"
+  "\"string\": \"hello\","
+  "\"floatArray\": [ 0.0, 0.25, 1.0, 0.75 ],"
+  "\"intArray\": [ 1, 2, 3, 5, 7, 11, 13, -1, -5 ],"
+  "\"mixedArray\": [ 1.99, \"the\", 6, \"brown\", \"fox\" ],"
+  "\"stringArray\": [ \"lorem\", \"ipsum\", \"dolor\", \"sic\", \"amet\" ],"
+  "\"object\": { \"duration\": 4.0, \"delay\": 1.0 },"
+  "\"rgb\": [ 0.5, 0.8, 0.25 ],"
+  "\"disambiguatedFloat\": { \"type\": \"float\", \"value\": 15.8 },"
+  "\"rotation1\": { \"type\": \"rotation\", \"value\": [ 15.0, 90.0, -45.0 ] },"
+  "\"rotation2\": { \"type\": \"rotation\", \"value\": [ 0.707, 0.0, 0.707, 0.0 ] },"
+  "\"matrix\": [ 1.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 1.0, 2.0, 3.0, 4.0 ],"
+  "\"matrix3\": [ 2.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 1.0, 2.0 ],"
+  "\"vector2\": [ 2.0, 1.0 ]"
+  "}";
+
+template <typename T>
+struct Item
+{
+  std::string name;
+  bool readResult;
+  T result;
+};
+
+struct Document
+{
+  Document(const std::string& json)
+  : parser(JsonParser::New())
+  {
+    //DALI_TEST_CHECK(parser.Parse(json));
+    if (!parser.Parse(json))
+    {
+      auto error = parser.GetErrorDescription();
+      printf("Error: %s in {%d, %d}\n", error.c_str(), parser.GetErrorLineNumber(), parser.GetErrorColumn());
+    }
+    root = parser.GetRoot();
+  }
+
+  JsonParser parser;
+  const TreeNode* root;
+};
+
+template <typename T, size_t N>
+bool CompareArrays(const T (&array)[N], const T* p, T epsilon = T(0))
+{
+  for (auto& i: array)
+  {
+    if (std::abs(i - *p) > epsilon)
+    {
+      printf("Element %d mismatched.\n", int32_t(std::distance(array, &i)));
+      return false;
+    }
+    ++p;
+  }
+  return true;
+}
+
+}
+
+int UtcDaliJsonUtilReadBool(void)
+{
+  bool value = false;
+  DALI_TEST_CHECK(!ReadBool(nullptr, value));
+  DALI_TEST_EQUAL(value, false); // unchanged
+
+  Document doc{ TEST_JSON };
+
+  for (auto& i : {
+    Item<bool>{ "bool",       true, true },
+    Item<bool>{ "int",        false, true }, // value unchanged
+    Item<bool>{ "float",      false, true },
+    Item<bool>{ "null",       false, true },
+    Item<bool>{ "floatArray", false, true },
+    Item<bool>{ "intArray",   false, true },
+    Item<bool>{ "object",     false, true },
+  })
+  {
+    bool readResult = ReadBool(doc.root->GetChild(i.name), value);
+    DALI_TEST_EQUAL(readResult, i.readResult);
+    if (readResult)
+    {
+      DALI_TEST_EQUAL(value, i.result);
+    }
+  }
+
+  END_TEST;
+}
+
+int UtcDaliJsonUtilReadInt(void)
+{
+  int value = 0xbadbeef;
+  DALI_TEST_CHECK(!ReadInt(nullptr, value));
+  DALI_TEST_EQUAL(value, 0xbadbeef);
+
+  Document doc{ TEST_JSON };
+
+  for (auto& i : {
+    Item<int>{ "bool",       false, 0xbadbeef }, // unchanged from initial
+    Item<int>{ "int",        true, 17834 },
+    Item<int>{ "float",      true, 3 },
+    Item<int>{ "null",       false, 3 },
+    Item<int>{ "floatArray", false, 3 },
+    Item<int>{ "intArray",   false, 3 },
+    Item<int>{ "object",     false, 3 },
+  })
+  {
+    bool readResult = ReadInt(doc.root->GetChild(i.name), value);
+    DALI_TEST_EQUAL(readResult, i.readResult);
+    if (readResult)
+    {
+      DALI_TEST_EQUAL(value, i.result);
+    }
+  }
+
+  END_TEST;
+}
+
+int UtcDaliJsonUtilReadFloat(void)
+{
+  float value = 10.101f;
+  DALI_TEST_CHECK(!ReadFloat(nullptr, value));
+  DALI_TEST_EQUAL(value, 10.101f);
+
+  Document doc{ TEST_JSON };
+
+  for (auto& i : {
+    Item<float>{ "bool",       false, 10.101f }, // unchanged from initial
+    Item<float>{ "int",        true, 17834.f },
+    Item<float>{ "float",      true, 3.1415628f },
+    Item<float>{ "null",       false, 3.1415628f },
+    Item<float>{ "floatArray", false, 3.1415628f },
+    Item<float>{ "intArray",   false, 3.1415628f },
+    Item<float>{ "object",     false, 3.1415628f },
+  })
+  {
+    bool readResult = ReadFloat(doc.root->GetChild(i.name), value);
+    DALI_TEST_EQUAL(readResult, i.readResult);
+    if (readResult)
+    {
+      DALI_TEST_EQUAL(value, i.result);
+    }
+  }
+
+  END_TEST;
+}
+
+int UtcDaliJsonUtilNumericalArrays(void)
+{
+  Document doc{ TEST_JSON };
+
+  DALI_TEST_EQUAL(4u, GetNumericalArraySize(doc.root->GetChild("floatArray")));
+  DALI_TEST_EQUAL(9u, GetNumericalArraySize(doc.root->GetChild("intArray")));
+  DALI_TEST_EQUAL(1u, GetNumericalArraySize(doc.root->GetChild("mixedArray")));
+
+  END_TEST;
+}
+
+int UtcDaliJsonUtilReadVectorInt(void)
+{
+  DALI_TEST_CHECK(!ReadVector(nullptr, static_cast<int*>(nullptr), 0));
+
+  Document doc{ TEST_JSON };
+
+  int ints[9];
+  DALI_TEST_CHECK(ReadVector(doc.root->GetChild("floatArray"), ints, 4u));
+  DALI_TEST_CHECK(CompareArrays<int>({ 0, 0, 1, 0 }, ints));
+
+  DALI_TEST_CHECK(ReadVector(doc.root->GetChild("intArray"), ints, 9u));
+  DALI_TEST_CHECK(CompareArrays<int>({ 1, 2, 3, 5, 7, 11, 13, -1, -5 }, ints));
+
+  DALI_TEST_CHECK(ReadVector(doc.root->GetChild("mixedArray"), ints, 1u));
+  DALI_TEST_CHECK(CompareArrays<int>({ 1 }, ints));
+
+  END_TEST;
+}
+
+int UtcDaliJsonUtilReadVectorFloat(void)
+{
+  DALI_TEST_CHECK(!ReadVector(nullptr, static_cast<int*>(nullptr), 0));
+
+  Document doc{ TEST_JSON };
+
+  constexpr float e = 1e-6f;
+  float floats[9];
+  DALI_TEST_CHECK(ReadVector(doc.root->GetChild("floatArray"), floats, 4u));
+  DALI_TEST_CHECK(CompareArrays<float>({ 0.f, 0.25f, 1.f, 0.75f }, floats, e));
+
+  DALI_TEST_CHECK(ReadVector(doc.root->GetChild("intArray"), floats, 9u));
+  DALI_TEST_CHECK(CompareArrays<float>({ 1.f, 2.f, 3.f, 5.f, 7.f, 11.f, 13.f, -1.f, -5.f }, floats, e));
+
+  DALI_TEST_CHECK(ReadVector(doc.root->GetChild("mixedArray"), floats, 1u));
+  DALI_TEST_CHECK(CompareArrays<float>({ 1.99f }, floats, e));
+
+  END_TEST;
+}
+
+int UtcDaliJsonUtilReadColor(void)
+{
+  Vector4 color;
+  DALI_TEST_CHECK(!ReadColor(nullptr, color));
+
+  Document doc{ TEST_JSON };
+  DALI_TEST_CHECK(!ReadColor(doc.root->GetChild("bool"), color));
+  DALI_TEST_CHECK(!ReadColor(doc.root->GetChild("int"), color));
+  DALI_TEST_CHECK(!ReadColor(doc.root->GetChild("float"), color));
+  DALI_TEST_CHECK(!ReadColor(doc.root->GetChild("string"), color));
+  DALI_TEST_CHECK(!ReadColor(doc.root->GetChild("object"), color));
+
+  constexpr float e = 1e-6f;
+  DALI_TEST_CHECK(ReadColor(doc.root->GetChild("floatArray"), color));
+  DALI_TEST_CHECK(CompareArrays<float>({ 0.f, 0.25f, 1.0f, 0.75f }, color.AsFloat(), e));
+
+  DALI_TEST_CHECK(ReadColor(doc.root->GetChild("intArray"), color));
+  DALI_TEST_CHECK(CompareArrays<float>({ 1.f, 2.f, 3.f, 5.f }, color.AsFloat(), e));
+
+  DALI_TEST_CHECK(ReadColor(doc.root->GetChild("rgb"), color));
+  DALI_TEST_CHECK(CompareArrays<float>({ .5f, .8f, .25f, 1.f }, color.AsFloat(), e));
+
+  END_TEST;
+}
+
+int UtcDaliJsonUtilReadTimePeriod(void)
+{
+  TimePeriod value(60.f);
+  DALI_TEST_CHECK(!ReadTimePeriod(nullptr, value));
+  DALI_TEST_EQUAL(value.durationSeconds, 60.f);
+  DALI_TEST_EQUAL(value.delaySeconds, 0.f);
+
+  Document doc{ TEST_JSON };
+  DALI_TEST_CHECK(ReadTimePeriod(doc.root->GetChild("object"), value));
+  DALI_TEST_EQUAL(value.durationSeconds, 4.f);
+  DALI_TEST_EQUAL(value.delaySeconds, 1.f);
+
+  END_TEST;
+}
+
+int UtcDaliJsonUtilReadString(void)
+{
+  std::string value = "bye";
+  DALI_TEST_CHECK(!ReadString(nullptr, value));
+  DALI_TEST_EQUAL(value, "bye");
+
+  Document doc{ TEST_JSON };
+
+  for (auto& i : {
+    Item<std::string>{ "bool",       false, "bye"}, // unchanged from initial
+    Item<std::string>{ "int",        false, "bye"},
+    Item<std::string>{ "float",      false, "bye"},
+    Item<std::string>{ "null",       false, "bye"},
+    Item<std::string>{ "string",     true, "hello"},
+    Item<std::string>{ "floatArray", false, "hello"}, // unchanged
+    Item<std::string>{ "object",     false, "hello"},
+  })
+  {
+    bool readResult = ReadString(doc.root->GetChild(i.name), value);
+    DALI_TEST_EQUAL(readResult, i.readResult);
+    if (readResult)
+    {
+      DALI_TEST_EQUAL(value, i.result);
+    }
+  }
+
+  END_TEST;
+}
+
+int UtcDaliJsonUtilReadStringVector(void)
+{
+  std::vector<std::string> strings;
+  DALI_TEST_CHECK(!ReadStringVector(nullptr, strings));
+  DALI_TEST_CHECK(strings.empty());
+
+  Document doc{ TEST_JSON };
+
+  DALI_TEST_CHECK(!ReadStringVector(doc.root->GetChild("floatArray"), strings));
+  DALI_TEST_CHECK(strings.empty());
+
+  DALI_TEST_CHECK(!ReadStringVector(doc.root->GetChild("intArray"), strings));
+  DALI_TEST_CHECK(strings.empty());
+
+  DALI_TEST_CHECK(!ReadStringVector(doc.root->GetChild("mixedArray"), strings));
+  DALI_TEST_CHECK(strings.empty());
+
+  DALI_TEST_CHECK(ReadStringVector(doc.root->GetChild("stringArray"), strings));
+  DALI_TEST_EQUAL(strings.size(), 5u);
+
+  auto iStrings = strings.begin();
+  for (auto& i : { "lorem", "ipsum", "dolor", "sic", "amet" })
+  {
+    DALI_TEST_EQUAL(*iStrings, i);
+    ++iStrings;
+  }
+
+  END_TEST;
+}
+
+int UtcDaliJsonUtilReadAndReturnPropertyValue(void)
+{
+  Document doc{ TEST_JSON };
+  DALI_TEST_EQUAL(ReadPropertyValue(*doc.root->GetChild("disambiguatedFloat")).Get<float>(), 15.8f);
+
+  DALI_TEST_EQUAL(ReadPropertyValue(*doc.root->GetChild("rotation1")).Get<Quaternion>(),
+    Quaternion(Radian(Degree(15.0)), Radian(Degree(90.0)), Radian(Degree(-45.f))));
+  DALI_TEST_EQUAL(ReadPropertyValue(*doc.root->GetChild("rotation2")).Get<Quaternion>(),
+    Quaternion(Vector4(0.707f, 0.f, 0.707f, 0.f)));
+
+  DALI_TEST_EQUAL(ReadPropertyValue(*doc.root->GetChild("bool")).Get<bool>(), true);
+  DALI_TEST_EQUAL(ReadPropertyValue(*doc.root->GetChild("int")).Get<int32_t>(), 17834);
+
+  const float floats[]{ 1.f, 0.f, 0.f, 0.f, 0.f, 2.f, 0.f, 0.f, 0.f, 0.f, 3.f, 0.f, 1.f, 2.f, 3.f, 4.f };
+  DALI_TEST_EQUAL(ReadPropertyValue(*doc.root->GetChild("matrix")).Get<Matrix>(), Matrix(floats));
+  DALI_TEST_EQUAL(ReadPropertyValue(*doc.root->GetChild("matrix3")).Get<Matrix3>(), Matrix3(floats[5],
+    floats[6], floats[7], floats[8], floats[9], floats[10], floats[11], floats[12], floats[13]));
+  DALI_TEST_EQUAL(ReadPropertyValue(*doc.root->GetChild("floatArray")).Get<Vector4>(),
+    Vector4(0.f, .25f, 1.f, .75f));
+  DALI_TEST_EQUAL(ReadPropertyValue(*doc.root->GetChild("rgb")).Get<Vector3>(),
+    Vector3(.5f, .8f, .25f));
+  DALI_TEST_EQUAL(ReadPropertyValue(*doc.root->GetChild("vector2")).Get<Vector2>(),
+    Vector2(2.f, 1.f));
+  DALI_TEST_EQUAL(ReadPropertyValue(*doc.root->GetChild("object")->GetChild("duration")).Get<float>(), 4.f );
+
+  END_TEST;
+}
+
+namespace
+{
+template <typename T>
+void CheckEqualityAs(Property::Value lhs, Property::Value rhs)
+{
+  DALI_TEST_EQUAL(lhs.Get<T>(), rhs.Get<T>());
+}
+
+}
+
+int UtcDaliJsonUtilReadPropertyValue(void)
+{
+  struct TypeNameValue
+  {
+    Property::Type type;
+    std::string name;
+    Property::Value value;
+    void(*compareFn)(Property::Value, Property::Value);
+  };
+
+  Document doc{ TEST_JSON };
+
+  const float floats[]{ 1.f, 0.f, 0.f, 0.f, 0.f, 2.f, 0.f, 0.f, 0.f, 0.f, 3.f, 0.f, 1.f, 2.f, 3.f, 4.f };
+  const TypeNameValue typeNameValues[] {
+    { Property::BOOLEAN, "bool", true, CheckEqualityAs<bool> },
+    { Property::FLOAT, "float", 3.1415628f, CheckEqualityAs<float> },
+    { Property::INTEGER, "int", 17834, CheckEqualityAs<int32_t> },
+    { Property::VECTOR2, "vector2", Vector2(2.f, 1.f), CheckEqualityAs<Vector2> },
+    { Property::VECTOR3, "rgb", Vector3(.5f, .8f, .25f), CheckEqualityAs<Vector3> },
+    { Property::VECTOR4, "floatArray", Vector4(.0f, .25f, 1.f, .75f), CheckEqualityAs<Vector4> },
+    { Property::MATRIX3, "matrix3", Matrix3(2.f, 0.f, 0.f, 0.f, 0.f, 3.f, 0.f, 1.f, 2.f),
+      CheckEqualityAs<Matrix3> },
+    { Property::MATRIX, "matrix", Matrix(floats), CheckEqualityAs<Matrix> },
+    { Property::RECTANGLE, "intArray", Rect<int>(1, 2, 3, 5), CheckEqualityAs<Rect<int>> },
+    { Property::EXTENTS, "intArray", Extents(1, 2, 3, 5), CheckEqualityAs<Extents> },
+  };
+  for(auto& i: typeNameValues)
+  {
+    std::cout << i.value << std::endl;
+    i.compareFn(ReadPropertyValue(i.type, *doc.root->GetChild(i.name)), i.value);
+  }
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/CMakeLists.txt b/automated-tests/src/dali-scene-loader/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..348f29c
--- /dev/null
@@ -0,0 +1,102 @@
+SET(PKG_NAME "dali-scene-loader")
+
+SET(EXEC_NAME "tct-${PKG_NAME}-core")
+SET(RPM_NAME "core-${PKG_NAME}-tests")
+
+SET(CAPI_LIB "dali-scene-loader")
+
+# List of test case sources (Only these get parsed for test cases)
+SET(TC_SOURCES
+  utc-Dali-AlphaFunctionHelper.cpp
+  utc-Dali-AnimationDefinition.cpp
+  utc-Dali-AnimatedProperty.cpp
+  utc-Dali-CameraParameters.cpp
+  utc-Dali-DliLoader.cpp
+  utc-Dali-EnvironmentDefinition.cpp
+  utc-Dali-Gltf2Loader.cpp
+  utc-Dali-KtxLoader.cpp
+  utc-Dali-MatrixStack.cpp
+  utc-Dali-NodeDefinition.cpp
+  utc-Dali-RendererState.cpp
+  utc-Dali-ResourceBundle.cpp
+  utc-Dali-SceneDefinition.cpp
+  utc-Dali-ShaderDefinition.cpp
+  utc-Dali-ShaderDefinitionFactory.cpp
+  utc-Dali-StringCallback.cpp
+  utc-Dali-Utils.cpp
+  utc-Dali-ViewProjection.cpp
+)
+
+# List of test harness files (Won't get parsed for test cases)
+SET(TEST_HARNESS_DIR "../dali-toolkit/dali-toolkit-test-utils")
+
+SET(TEST_HARNESS_SOURCES
+  ${TEST_HARNESS_DIR}/toolkit-adaptor.cpp
+  ${TEST_HARNESS_DIR}/toolkit-application.cpp
+  ${TEST_HARNESS_DIR}/toolkit-event-thread-callback.cpp
+  ${TEST_HARNESS_DIR}/toolkit-environment-variable.cpp
+  ${TEST_HARNESS_DIR}/toolkit-input-method-context.cpp
+  ${TEST_HARNESS_DIR}/toolkit-input-method-options.cpp
+  ${TEST_HARNESS_DIR}/toolkit-lifecycle-controller.cpp
+  ${TEST_HARNESS_DIR}/toolkit-orientation.cpp
+  ${TEST_HARNESS_DIR}/toolkit-style-monitor.cpp
+  ${TEST_HARNESS_DIR}/toolkit-test-application.cpp
+  ${TEST_HARNESS_DIR}/toolkit-timer.cpp
+  ${TEST_HARNESS_DIR}/toolkit-trigger-event-factory.cpp
+  ${TEST_HARNESS_DIR}/toolkit-window.cpp
+  ${TEST_HARNESS_DIR}/toolkit-scene-holder.cpp
+  ${TEST_HARNESS_DIR}/dali-test-suite-utils.cpp
+  ${TEST_HARNESS_DIR}/dali-toolkit-test-suite-utils.cpp
+  ${TEST_HARNESS_DIR}/dummy-control.cpp
+  ${TEST_HARNESS_DIR}/mesh-builder.cpp
+  ${TEST_HARNESS_DIR}/test-actor-utils.cpp
+  ${TEST_HARNESS_DIR}/test-animation-data.cpp
+  ${TEST_HARNESS_DIR}/test-application.cpp
+  ${TEST_HARNESS_DIR}/test-button.cpp
+  ${TEST_HARNESS_DIR}/test-harness.cpp
+  ${TEST_HARNESS_DIR}/test-gesture-generator.cpp
+  ${TEST_HARNESS_DIR}/test-gl-abstraction.cpp
+  ${TEST_HARNESS_DIR}/test-gl-sync-abstraction.cpp
+  ${TEST_HARNESS_DIR}/test-platform-abstraction.cpp
+  ${TEST_HARNESS_DIR}/test-render-controller.cpp
+  ${TEST_HARNESS_DIR}/test-trace-call-stack.cpp
+)
+
+PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED
+  dali2-core
+  dali2-adaptor
+  dali2-toolkit
+  dali2-scene-loader
+)
+
+ADD_COMPILE_OPTIONS( -O0 -ggdb --coverage -Wall -Werror -DDEBUG_ENABLED)
+ADD_COMPILE_OPTIONS( ${${CAPI_LIB}_CFLAGS_OTHER} )
+
+ADD_DEFINITIONS(-DTEST_RESOURCE_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/../../resources\" )
+
+FOREACH(directory ${${CAPI_LIB}_LIBRARY_DIRS})
+    SET(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -L${directory}")
+ENDFOREACH(directory ${CAPI_LIB_LIBRARY_DIRS})
+
+INCLUDE_DIRECTORIES(
+    ../../../
+    ${${CAPI_LIB}_INCLUDE_DIRS}
+    ../dali-toolkit/dali-toolkit-test-utils
+)
+
+ADD_CUSTOM_COMMAND(
+  COMMAND ${SCRIPT_DIR}/tcheadgen.sh ${EXEC_NAME}.h ${TC_SOURCES}
+  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  OUTPUT ${EXEC_NAME}.h
+  COMMENT "Generating test tables"
+  )
+
+ADD_EXECUTABLE(${EXEC_NAME} ${EXEC_NAME}.h ${EXEC_NAME}.cpp ${TC_SOURCES} ${TEST_HARNESS_SOURCES})
+TARGET_LINK_LIBRARIES(${EXEC_NAME}
+    ${${CAPI_LIB}_LIBRARIES}
+    -lpthread --coverage
+)
+
+INSTALL(PROGRAMS ${EXEC_NAME}
+    DESTINATION ${BIN_DIR}/${EXEC_NAME}
+)
diff --git a/automated-tests/src/dali-scene-loader/tct-dali-scene-loader-core.cpp b/automated-tests/src/dali-scene-loader/tct-dali-scene-loader-core.cpp
new file mode 100644 (file)
index 0000000..90b7a2c
--- /dev/null
@@ -0,0 +1,51 @@
+#include <string.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <test-harness.h>
+#include "tct-dali-scene-loader-core.h"
+
+int main(int argc, char * const argv[])
+{
+  int result = TestHarness::EXIT_STATUS_BAD_ARGUMENT;
+
+  const char* optString = "sf";
+  bool optRerunFailed(true);
+  bool optRunSerially(false);
+
+  int nextOpt = 0;
+  do
+  {
+    nextOpt = getopt( argc, argv, optString );
+    switch(nextOpt)
+    {
+      case 'f':
+        optRerunFailed = false;
+        break;
+      case 's':
+        optRunSerially = true;
+        break;
+      case '?':
+        TestHarness::Usage(argv[0]);
+        exit(TestHarness::EXIT_STATUS_BAD_ARGUMENT);
+        break;
+    }
+  } while( nextOpt != -1 );
+
+  if( optind == argc ) // no testcase name in argument list
+  {
+    if( optRunSerially )
+    {
+      result = TestHarness::RunAll( argv[0], tc_array );
+    }
+    else
+    {
+      result = TestHarness::RunAllInParallel( argv[0], tc_array, optRerunFailed );
+    }
+  }
+  else
+  {
+    // optind is index of next argument - interpret as testcase name
+    result = TestHarness::FindAndRunTestCase(tc_array, argv[optind]);
+  }
+  return result;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-AlphaFunctionHelper.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-AlphaFunctionHelper.cpp
new file mode 100644 (file)
index 0000000..da40b27
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/alpha-function-helper.h"
+#include <dali-test-suite-utils.h>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+#define ALPHA_FN_PAIR(x) { #x, AlphaFunction::x }
+
+const std::pair<std::string, AlphaFunction::BuiltinFunction> BUILTIN_FUNCTIONS[] {
+  ALPHA_FN_PAIR(DEFAULT),
+  ALPHA_FN_PAIR(LINEAR),
+  ALPHA_FN_PAIR(REVERSE),
+  ALPHA_FN_PAIR(EASE_IN),
+  ALPHA_FN_PAIR(EASE_OUT),
+  ALPHA_FN_PAIR(EASE_IN_OUT),
+  ALPHA_FN_PAIR(EASE_IN_SQUARE),
+  ALPHA_FN_PAIR(EASE_OUT_SQUARE),
+  ALPHA_FN_PAIR(EASE_IN_SINE),
+  ALPHA_FN_PAIR(EASE_OUT_SINE),
+  ALPHA_FN_PAIR(EASE_IN_OUT_SINE),
+  ALPHA_FN_PAIR(BOUNCE),
+  ALPHA_FN_PAIR(SIN),
+  ALPHA_FN_PAIR(EASE_OUT_BACK),
+};
+
+int UtcDaliAlphaFunctionHelperGet(void)
+{
+  bool found;
+  for (auto& a: BUILTIN_FUNCTIONS)
+  {
+    auto result = GetAlphaFunction(a.first, &found);
+    DALI_TEST_EQUAL(result.GetBuiltinFunction(), a.second);
+    DALI_TEST_EQUAL(result.GetMode(), AlphaFunction::BUILTIN_FUNCTION);
+    DALI_TEST_CHECK(found);
+  }
+
+  auto result = GetAlphaFunction("made up function", &found);
+  DALI_TEST_EQUAL(result.GetBuiltinFunction(), AlphaFunction::DEFAULT);
+  DALI_TEST_EQUAL(result.GetMode(), AlphaFunction::BUILTIN_FUNCTION);
+  DALI_TEST_CHECK(!found);
+
+  END_TEST;
+}
+
+int UtcDaliAlphaFunctionHelperRegister(void)
+{
+  for (auto& a: BUILTIN_FUNCTIONS)
+  {
+    DALI_TEST_ASSERTION(RegisterAlphaFunction(a.first, AlphaFunction()), "given key already exists");
+  }
+
+  AlphaFunctionPrototype testFn = [](float f) {
+    return f > .5f ? 1.f : 0.f;
+  };
+  RegisterAlphaFunction("step", AlphaFunction(testFn));
+
+  bool found;
+  auto result = GetAlphaFunction("step", &found);
+  DALI_TEST_EQUAL(result.GetMode(), AlphaFunction::CUSTOM_FUNCTION);
+  DALI_TEST_EQUAL(result.GetCustomFunction(), testFn);
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-AnimatedProperty.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-AnimatedProperty.cpp
new file mode 100644 (file)
index 0000000..c660a1d
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/animated-property.h"
+#include <dali-test-suite-utils.h>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+int UtcDaliAnimatedPropertyGetPropertyType(void)
+{
+  TestApplication app;
+  auto actor = Actor::New();
+  actor.SetProperty(Actor::Property::NAME, "ChristopherPlummer");
+
+  AnimatedProperty animProp {
+   "ChristopherPlummer",
+   "position",
+   KeyFrames(),
+   std::unique_ptr<AnimatedProperty::Value>{ new AnimatedProperty::Value{
+     Property::Value{ Vector3::XAXIS * 100.f },
+     true
+   } },
+  };
+
+  Property expected(actor, Actor::Property::POSITION);
+  Property result = animProp.GetProperty(actor);
+  DALI_TEST_EQUAL(result.object, expected.object);
+  DALI_TEST_EQUAL(result.propertyIndex, expected.propertyIndex);
+  DALI_TEST_EQUAL(result.componentIndex, expected.componentIndex);
+  DALI_TEST_EQUAL(animProp.GetPropertyType(actor), Property::VECTOR3);
+
+  END_TEST;
+}
+
+
+
+
+
+
+
+
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-AnimationDefinition.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-AnimationDefinition.cpp
new file mode 100644 (file)
index 0000000..e27fb36
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/animation-definition.h"
+#include <dali-test-suite-utils.h>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+int UtcDaliAnimationDefinitionStopForModification(void)
+{
+  TestApplication app;
+  auto anim = Animation::New(15.f);
+  anim.Play();
+
+  auto oldEndAction = AnimationDefinition::StopForModification(anim);
+  DALI_TEST_EQUAL(anim.GetState(), Animation::STOPPED);
+  DALI_TEST_EQUAL(oldEndAction, Animation::BAKE);
+  DALI_TEST_EQUAL(anim.GetEndAction(), Animation::DISCARD);
+
+  END_TEST;
+}
+
+int UtcDaliAnimationDefinitionReAnimate(void)
+{
+  TestApplication app;
+  auto actor = Actor::New();
+  actor.SetProperty(Actor::Property::NAME, "ChristopherPlummer");
+
+  auto getActor = [&actor](const std::string& name) {
+    return actor.FindChildByName(name);
+  };
+
+  for (bool b: { false, true })
+  {
+    AnimationDefinition animDef;
+    animDef.mName = "WalkRight";
+    animDef.mDuration = 10.f;
+    animDef.mLoopCount = 2;
+    animDef.mEndAction = Animation::BAKE_FINAL;
+    animDef.mSpeedFactor = .7f;
+    animDef.mProperties.push_back(AnimatedProperty{
+     "ChristopherPlummer",
+     "position",
+     KeyFrames(),
+     std::unique_ptr<AnimatedProperty::Value>{ new AnimatedProperty::Value{
+       Property::Value{ Vector3::XAXIS * 100.f },
+       b
+     } },
+     AlphaFunction::EASE_OUT,
+     TimePeriod(animDef.mDuration)
+    });
+
+    auto anim = animDef.ReAnimate(getActor);
+    DALI_TEST_EQUAL(anim.GetDuration(), animDef.mDuration);
+    DALI_TEST_EQUAL(anim.GetEndAction(), animDef.mEndAction);
+    DALI_TEST_EQUAL(anim.GetSpeedFactor(), animDef.mSpeedFactor);
+    DALI_TEST_EQUAL(anim.GetLoopCount(), animDef.mLoopCount);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliAnimationDefinitionReAnimateKeyFrames(void)
+{
+  TestApplication app;
+  auto actor = Actor::New();
+  actor.SetProperty(Actor::Property::NAME, "ChristopherPlummer");
+
+  auto getActor = [&actor](const std::string& name) {
+    return actor.FindChildByName(name);
+  };
+
+  KeyFrames kf = KeyFrames::New();
+  kf.Add(0.f, Vector3::ZERO);
+  kf.Add(1.f, Vector3::XAXIS * 100.f);
+
+  AnimationDefinition animDef;
+  animDef.mName = "WalkRight";
+  animDef.mDuration = 10.f;
+  animDef.mLoopCount = 2;
+  animDef.mEndAction = Animation::BAKE_FINAL;
+  animDef.mSpeedFactor = .7f;
+  animDef.mProperties.push_back(AnimatedProperty{
+   "ChristopherPlummer",
+   "position",
+   kf,
+   nullptr,
+   AlphaFunction::EASE_OUT,
+   TimePeriod(animDef.mDuration)
+  });
+
+  auto anim = animDef.ReAnimate(getActor);
+  DALI_TEST_EQUAL(anim.GetDuration(), animDef.mDuration);
+  DALI_TEST_EQUAL(anim.GetEndAction(), animDef.mEndAction);
+  DALI_TEST_EQUAL(anim.GetSpeedFactor(), animDef.mSpeedFactor);
+  DALI_TEST_EQUAL(anim.GetLoopCount(), animDef.mLoopCount);
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-CameraParameters.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-CameraParameters.cpp
new file mode 100644 (file)
index 0000000..5ad4a40
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/camera-parameters.h"
+#include <dali-test-suite-utils.h>
+#include <string_view>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+int UtcDaliCameraParameters(void)
+{
+  Quaternion qView = Quaternion(Radian(Degree(180.f)), Vector3::YAXIS);
+  CameraParameters camParams;
+  camParams.matrix.SetTransformComponents(Vector3::ONE * 2.f,
+    qView,
+    Vector3::ZAXIS * -100.f);
+  camParams.orthographicSize = Vector4{ -1.f, 1.f, -1.f, 1.f };
+  camParams.yFov = Radian(M_PI * .5).radian;
+  camParams.zNear = 1.f;
+  camParams.zFar = 1000.f;
+
+  Vector3 scale;
+  Quaternion orientation;
+  Vector3 position;
+  camParams.CalculateTransformComponents(position, orientation, scale);
+  DALI_TEST_EQUAL(scale, Vector3::ONE * 2.f);
+  DALI_TEST_EQUAL(orientation, Quaternion::IDENTITY); // 2 180 degrees rotations along y
+  DALI_TEST_EQUAL(position, Vector3::ZAXIS * -100.f);
+
+  TestApplication app;
+  CameraActor camera = CameraActor::New();
+  for (auto i : { false, true })
+  {
+    camParams.isPerspective = i;
+
+    auto viewProjection = camParams.GetViewProjection();
+    Matrix view{ false };
+    Matrix::Multiply(view, Matrix(qView), camParams.matrix);
+    view.Invert();
+    DALI_TEST_EQUAL(viewProjection.GetView(), view);
+
+    camParams.ConfigureCamera(camera);
+    DALI_TEST_EQUAL(camParams.zNear, camera.GetNearClippingPlane());
+    DALI_TEST_EQUAL(camParams.zFar, camera.GetFarClippingPlane());
+
+    DALI_TEST_EQUAL(camera.GetInvertYAxis(), true);
+    DALI_TEST_EQUAL(camera.GetProperty(Actor::Property::POSITION).Get<Vector3>(), position);
+    DALI_TEST_EQUAL(camera.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>(), orientation);
+    DALI_TEST_EQUAL(camera.GetProperty(Actor::Property::SCALE).Get<Vector3>(), scale);
+  }
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-DliLoader.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-DliLoader.cpp
new file mode 100644 (file)
index 0000000..d09d2da
--- /dev/null
@@ -0,0 +1,641 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/dli-loader.h"
+#include "dali-scene-loader/public-api/resource-bundle.h"
+#include "dali-scene-loader/public-api/scene-definition.h"
+#include "dali-scene-loader/public-api/load-result.h"
+#include "dali-scene-loader/internal/json-util.h"
+#include <dali-test-suite-utils.h>
+#include <string_view>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+namespace
+{
+
+void ConfigureBlendShapeShaders(ResourceBundle& resources, const SceneDefinition& scene, Actor root,
+  std::vector<BlendshapeShaderConfigurationRequest>&& requests)
+{
+  std::vector<std::string> errors;
+  auto onError = [&errors](const std::string& msg) {
+    errors.push_back(msg);
+  };
+
+  if (!scene.ConfigureBlendshapeShaders(resources, root, std::move(requests), onError))
+  {
+    ExceptionFlinger flinger(ASSERT_LOCATION);
+    for (auto& msg : errors)
+    {
+      flinger << msg << '\n';
+    }
+  }
+}
+
+struct Context
+{
+  ResourceBundle::PathProvider pathProvider = [](ResourceType::Value type) {
+    return TEST_RESOURCE_DIR "/";
+  };
+
+  ResourceBundle resources;
+  SceneDefinition scene;
+  std::vector<CameraParameters> cameraParameters;
+  std::vector<LightParameters> lights;
+  std::vector<AnimationDefinition> animations;
+  std::vector<AnimationGroupDefinition> animGroups;
+
+  LoadResult output {
+    resources,
+    scene,
+    animations,
+    animGroups,
+    cameraParameters,
+    lights
+  };
+
+  DliLoader::InputParams input {
+    pathProvider(ResourceType::Mesh),
+    nullptr,
+    {},
+    {},
+    nullptr,
+  };
+  DliLoader::LoadParams loadParams{ input, output };
+
+  std::vector<std::string> errors;
+  DliLoader loader;
+
+  StringCallback onError = [this](const std::string& error) {
+    errors.push_back(error);
+    printf("%s\n", error.c_str());
+  };
+
+  Context()
+  {
+    loader.SetErrorCallback(onError);
+  }
+};
+
+bool StringHasTokens(const char* string, const std::vector<const char*>& tokens)
+{
+  for (auto& token: tokens)
+  {
+    auto result = strstr(string, token);
+    if(nullptr == result)
+    {
+      return false;
+    }
+    string = result + strlen(token);
+  }
+  return true;
+}
+
+}
+
+int UtcDaliDliLoaderLoadSceneNotFound(void)
+{
+  Context ctx;
+
+  DALI_TEST_EQUAL(ctx.loader.LoadScene("does_not_exist.dli", ctx.loadParams), false);
+
+  auto error = ctx.loader.GetParseError();
+  DALI_TEST_CHECK(StringHasTokens(error.c_str(), { "Empty source buffer to parse." }));
+
+  END_TEST;
+}
+
+int UtcDaliDliLoaderLoadSceneFailParse(void)
+{
+  Context ctx;
+
+  auto path = ctx.pathProvider(ResourceType::Mesh) + "invalid.gltf";
+  DALI_TEST_EQUAL(ctx.loader.LoadScene(path, ctx.loadParams), false);
+
+  auto error = ctx.loader.GetParseError();
+  DALI_TEST_CHECK(StringHasTokens(error.c_str(), { "Unexpected character." }));
+
+  END_TEST;
+}
+
+int UtcDaliDliLoaderLoadSceneAssertions(void)
+{
+  const std::pair<std::string, std::string> pathExceptionPairs[] {
+     // from RequireChild()
+    { "scenes-nodes-missing", "Failed to find child node" },
+    { "scenes-missing", "Failed to find child node" },
+    { "nodes-missing", "Failed to find child node" },
+    // from ParseSceneInternal()
+    { "scene-out-of-bounds", "out of bounds" },
+    { "nodes-invalid-type", "invalid type; array required" },
+    { "nodes-array-empty", "must define a node id" },
+    { "root-id-invalid", "invalid value for root node index" },
+    { "root-id-out-of-bounds", "out of bounds" },
+    { "root-node-invalid-type", "invalid JSON type; object required" },
+    // from ParseSkeletons()
+    { "skeleton-node-missing", "Missing required attribute" },
+    { "skeleton-root-not-found", "not defined" },
+    // from ParseShaders()
+    { "shader-vertex-missing", "Missing vertex / fragment shader" },
+    { "shader-fragment-missing", "Missing vertex / fragment shader" },
+    // from ParseMeshes()
+    { "mesh-uri-missing", "Missing required attribute" },
+    { "mesh-indices-read-fail", "Failed to read indices" },
+    { "mesh-positions-read-fail", "Failed to read positions" },
+    // from ParseMaterials()
+    { "material-environment-out-of-bounds", "out of bounds" },
+    // from ParseNodes()
+    { "node-model-mesh-missing", "Missing mesh" },
+    { "node-arc-mesh-missing", "Missing mesh" },
+    { "node-animated-image-mesh-missing", "Missing mesh" },
+    { "node-renderable-mesh-invalid-type", "Invalid Mesh index type" },
+    { "node-renderable-mesh-out-of-bounds", "out of bounds" },
+    { "node-child-invalid-type", "invalid index type" },
+    { "node-name-already-used", "name already used" },
+    // from ParseAnimations()
+    { "animation-failed-to-open", "Failed to open animation data" }
+  };
+  for (auto& i: pathExceptionPairs)
+  {
+    Context ctx;
+
+    auto path = ctx.pathProvider(ResourceType::Mesh) + "dli/" + i.first + ".dli";
+    printf("\n\n%s: %s\n", path.c_str(), i.second.c_str());
+    DALI_TEST_ASSERTION(ctx.loader.LoadScene(path, ctx.loadParams), i.second.c_str());
+  }
+
+  END_TEST;
+}
+
+int UtcDaliDliLoaderLoadSceneExercise(void)
+{
+  Context ctx;
+
+  auto path = ctx.pathProvider(ResourceType::Mesh) + "exercise.dli";
+  DALI_TEST_CHECK(ctx.loader.LoadScene(path, ctx.loadParams));
+  DALI_TEST_CHECK(ctx.errors.empty());
+
+  auto& scene = ctx.scene;
+  auto& roots = scene.GetRoots();
+  DALI_TEST_EQUAL(roots.size(), 2u);
+  DALI_TEST_EQUAL(scene.GetNode(roots[0])->mName, "Backdrop"); // default scene is scene 1 - this one.
+  DALI_TEST_EQUAL(scene.GetNode(roots[1])->mName, "ExerciseDemo");
+
+  DALI_TEST_EQUAL(scene.GetNodeCount(), 96u);
+
+  auto& resources = ctx.resources;
+  DALI_TEST_EQUAL(resources.mMeshes.size(), 11u);
+  DALI_TEST_EQUAL(resources.mMaterials.size(), 13u);
+  DALI_TEST_EQUAL(resources.mShaders.size(), 5u);
+  DALI_TEST_EQUAL(resources.mEnvironmentMaps.size(), 2u);
+  DALI_TEST_EQUAL(resources.mSkeletons.size(), 1u);
+
+  DALI_TEST_EQUAL(ctx.cameraParameters.size(), 1u);
+  DALI_TEST_EQUAL(ctx.lights.size(), 1u);
+  DALI_TEST_EQUAL(ctx.animations.size(), 18u);
+  DALI_TEST_EQUAL(ctx.animGroups.size(), 16u);
+
+  ViewProjection viewProjection;
+  Transforms xforms {
+    MatrixStack{},
+    viewProjection
+  };
+  NodeDefinition::CreateParams nodeParams{
+    resources,
+    xforms,
+  };
+
+  Customization::Choices choices;
+
+  TestApplication app;
+
+  Actor root = Actor::New();
+  SetActorCentered(root);
+  for (auto iRoot : scene.GetRoots())
+  {
+    auto resourceRefs = resources.CreateRefCounter();
+    scene.CountResourceRefs(iRoot, choices, resourceRefs);
+    resources.CountEnvironmentReferences(resourceRefs);
+    resources.LoadResources(resourceRefs, ctx.pathProvider);
+    if (auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
+    {
+      scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
+      scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
+      ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
+      scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
+      root.Add(actor);
+    }
+  }
+
+  DALI_TEST_EQUAL(root.GetChildCount(), 2u);
+  DALI_TEST_EQUAL(root.GetChildAt(0).GetProperty(Actor::Property::NAME).Get<std::string>(), "Backdrop");
+  DALI_TEST_EQUAL(root.GetChildAt(1).GetProperty(Actor::Property::NAME).Get<std::string>(), "ExerciseDemo");
+
+  END_TEST;
+}
+
+int UtcDaliDliLoaderLoadSceneMorph(void)
+{
+  Context ctx;
+
+  std::vector<std::string> metadata;
+  uint32_t metadataCount = 0;
+  ctx.input.mPreNodeCategoryProcessors.push_back({ "metadata",
+    [&](const Property::Array& array, StringCallback) {
+      std::string key, value;
+      for (uint32_t i0 = 0, i1 = array.Count(); i0 < i1; ++i0)
+      {
+        auto& data = array.GetElementAt(i0);
+        DALI_TEST_EQUAL(data.GetType(), Property::MAP);
+
+        auto map = data.GetMap();
+        auto key = map->Find("key");
+        auto value = map->Find("value");
+        DALI_TEST_EQUAL(key->GetType(), Property::STRING);
+        DALI_TEST_EQUAL(value->GetType(), Property::STRING);
+        metadata.push_back(key->Get<std::string>() + ":" + value->Get<std::string>());
+
+        ++metadataCount;
+      }
+    }
+  });
+
+  std::vector<std::string> behaviors;
+  uint32_t behaviorCount = 0;
+  ctx.input.mPostNodeCategoryProcessors.push_back({ "behaviors",
+    [&](const Property::Array& array, StringCallback) {
+      for (uint32_t i0 = 0, i1 = array.Count(); i0 < i1; ++i0)
+      {
+        auto& data = array.GetElementAt(i0);
+        DALI_TEST_EQUAL(data.GetType(), Property::MAP);
+
+        auto map = data.GetMap();
+        auto event = map->Find("event");
+        auto url = map->Find("url");
+        DALI_TEST_EQUAL(event->GetType(), Property::STRING);
+        DALI_TEST_EQUAL(url->GetType(), Property::STRING);
+        behaviors.push_back(event->Get<std::string>() + ":" + url->Get<std::string>());
+
+        ++behaviorCount;
+      }
+    }
+  });
+
+  size_t numNodes = 0;
+  ctx.input.mNodePropertyProcessor = [&](const NodeDefinition&, const Property::Map&, StringCallback) {
+    ++numNodes;
+  };
+
+  auto path = ctx.pathProvider(ResourceType::Mesh) + "morph.dli";
+  DALI_TEST_CHECK(ctx.loader.LoadScene(path, ctx.loadParams));
+  DALI_TEST_CHECK(ctx.errors.empty());
+
+  auto& scene = ctx.scene;
+  auto& roots = scene.GetRoots();
+  DALI_TEST_EQUAL(roots.size(), 1u);
+  DALI_TEST_EQUAL(scene.GetNode(roots[0])->mName, "HeadTest_002");
+
+  DALI_TEST_EQUAL(numNodes, 3u);
+  DALI_TEST_EQUAL(scene.GetNodeCount(), numNodes);
+
+  auto& resources = ctx.resources;
+  DALI_TEST_EQUAL(resources.mMeshes.size(), 2u);
+  DALI_TEST_EQUAL(resources.mMaterials.size(), 1u);
+  DALI_TEST_EQUAL(resources.mShaders.size(), 5u);
+  DALI_TEST_EQUAL(resources.mEnvironmentMaps.size(), 2u);
+  DALI_TEST_EQUAL(resources.mSkeletons.size(), 0u);
+
+  DALI_TEST_EQUAL(ctx.cameraParameters.size(), 1u);
+  DALI_TEST_EQUAL(ctx.lights.size(), 1u);
+  DALI_TEST_EQUAL(ctx.animations.size(), 1u);
+  DALI_TEST_EQUAL(ctx.animGroups.size(), 0u);
+
+  DALI_TEST_EQUAL(metadata.size(), 4u);
+  DALI_TEST_EQUAL(behaviors.size(), 1u);
+
+  ViewProjection viewProjection;
+  Transforms xforms {
+    MatrixStack{},
+    viewProjection
+  };
+  NodeDefinition::CreateParams nodeParams{
+    resources,
+    xforms,
+  };
+
+  Customization::Choices choices;
+
+  TestApplication app;
+
+  Actor root = Actor::New();
+  SetActorCentered(root);
+  for (auto iRoot : scene.GetRoots())
+  {
+    auto resourceRefs = resources.CreateRefCounter();
+    scene.CountResourceRefs(iRoot, choices, resourceRefs);
+    resources.CountEnvironmentReferences(resourceRefs);
+    resources.LoadResources(resourceRefs, ctx.pathProvider);
+    if (auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
+    {
+      scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
+      scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
+      ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
+      scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
+      root.Add(actor);
+    }
+  }
+
+  DALI_TEST_EQUAL(root.GetChildCount(), 1u);
+  DALI_TEST_EQUAL(root.GetChildAt(0).GetProperty(Actor::Property::NAME).Get<std::string>(), "HeadTest_002");
+
+  END_TEST;
+}
+
+int UtcDaliDliLoaderLoadSceneArc(void)
+{
+  Context ctx;
+
+  auto path = ctx.pathProvider(ResourceType::Mesh) + "arc.dli";
+  DALI_TEST_CHECK(ctx.loader.LoadScene(path, ctx.loadParams));
+  DALI_TEST_CHECK(ctx.errors.empty());
+
+  auto& scene = ctx.scene;
+  auto& roots = scene.GetRoots();
+  DALI_TEST_EQUAL(roots.size(), 1u);
+  DALI_TEST_EQUAL(scene.GetNode(roots[0])->mName, "root");
+
+  DALI_TEST_EQUAL(scene.GetNodeCount(), 2u);
+
+  auto& resources = ctx.resources;
+  DALI_TEST_EQUAL(resources.mMeshes.size(), 1u);
+  DALI_TEST_EQUAL(resources.mMaterials.size(), 1u);
+  DALI_TEST_EQUAL(resources.mShaders.size(), 1u);
+  DALI_TEST_EQUAL(resources.mEnvironmentMaps.size(), 1u);
+  DALI_TEST_EQUAL(resources.mSkeletons.size(), 0u);
+
+  DALI_TEST_EQUAL(ctx.cameraParameters.size(), 0u);
+  DALI_TEST_EQUAL(ctx.lights.size(), 0u);
+  DALI_TEST_EQUAL(ctx.animations.size(), 0u);
+  DALI_TEST_EQUAL(ctx.animGroups.size(), 0u);
+
+  ViewProjection viewProjection;
+  Transforms xforms {
+    MatrixStack{},
+    viewProjection
+  };
+  NodeDefinition::CreateParams nodeParams{
+    resources,
+    xforms,
+  };
+
+  Customization::Choices choices;
+
+  TestApplication app;
+
+  Actor root = Actor::New();
+  SetActorCentered(root);
+  for (auto iRoot : scene.GetRoots())
+  {
+    auto resourceRefs = resources.CreateRefCounter();
+    scene.CountResourceRefs(iRoot, choices, resourceRefs);
+    resources.CountEnvironmentReferences(resourceRefs);
+    resources.LoadResources(resourceRefs, ctx.pathProvider);
+    if (auto actor = scene.CreateNodes(iRoot, choices, nodeParams))
+    {
+      scene.ConfigureSkeletonJoints(iRoot, resources.mSkeletons, actor);
+      scene.ConfigureSkinningShaders(resources, actor, std::move(nodeParams.mSkinnables));
+      ConfigureBlendShapeShaders(resources, scene, actor, std::move(nodeParams.mBlendshapeRequests));
+      scene.ApplyConstraints(actor, std::move(nodeParams.mConstrainables));
+      root.Add(actor);
+    }
+  }
+
+  DALI_TEST_EQUAL(root.GetChildCount(), 1u);
+  DALI_TEST_EQUAL(root.GetChildAt(0).GetProperty(Actor::Property::NAME).Get<std::string>(), "root");
+
+  END_TEST;
+}
+
+int UtcDaliDliLoaderLoadSceneShaderUniforms(void)
+{
+  Context ctx;
+
+  auto path = ctx.pathProvider(ResourceType::Mesh) + "dli/shader-uniforms.dli";
+  DALI_TEST_CHECK(ctx.loader.LoadScene(path, ctx.loadParams));
+  DALI_TEST_EQUAL(ctx.errors.size(), 1u);
+  DALI_TEST_CHECK(ctx.errors[0].find("failed to infer type") != std::string::npos);
+
+  auto& scene = ctx.scene;
+  auto& roots = scene.GetRoots();
+  DALI_TEST_EQUAL(roots.size(), 1u);
+  DALI_TEST_EQUAL(scene.GetNode(roots[0])->mName, "root");
+
+  DALI_TEST_EQUAL(scene.GetNodeCount(), 1u);
+
+  auto& resources = ctx.resources;
+  DALI_TEST_EQUAL(resources.mMeshes.size(), 0u);
+  DALI_TEST_EQUAL(resources.mMaterials.size(), 0u);
+  DALI_TEST_EQUAL(resources.mShaders.size(), 1u);
+  DALI_TEST_EQUAL(resources.mEnvironmentMaps.size(), 0u);
+  DALI_TEST_EQUAL(resources.mSkeletons.size(), 0u);
+
+  auto raw = resources.mShaders[0].first.LoadRaw(ctx.pathProvider(ResourceType::Shader));
+
+  TestApplication app;
+
+  auto shader = resources.mShaders[0].first.Load(std::move(raw));
+  DALI_TEST_EQUAL(shader.GetProperty(shader.GetPropertyIndex("uBool")).Get<float>(), 1.0f);
+  DALI_TEST_EQUAL(shader.GetProperty(shader.GetPropertyIndex("uInt")).Get<float>(), 255.0f);
+  DALI_TEST_EQUAL(shader.GetProperty(shader.GetPropertyIndex("uFloat")).Get<float>(), -0.5f);
+  DALI_TEST_EQUAL(shader.GetProperty(shader.GetPropertyIndex("uVec2")).Get<Vector2>(), Vector2(100.0f, -100.0f));
+  DALI_TEST_EQUAL(shader.GetProperty(shader.GetPropertyIndex("uVec3")).Get<Vector3>(), Vector3(50.0f, 0.f, -200.0f));
+  DALI_TEST_EQUAL(shader.GetProperty(shader.GetPropertyIndex("uVec4")).Get<Vector4>(), Vector4(0.1774f, 1.0f, 0.5333f, 0.7997f));
+  DALI_TEST_EQUAL(shader.GetProperty(shader.GetPropertyIndex("uMat3")).Get<Matrix3>(), Matrix3(9.0f, 8.0f, 7.0f, 6.0f, 5.0f, 4.0f, 3.0f, 2.0f, 1.0f));
+
+  Matrix expectedMatrix;
+  expectedMatrix.SetTransformComponents(Vector3::ONE * 8.0, Quaternion::IDENTITY, Vector3::ZERO);
+  DALI_TEST_EQUAL(shader.GetProperty(shader.GetPropertyIndex("uMat4")).Get<Matrix>(), expectedMatrix);
+
+  END_TEST;
+}
+
+int UtcDaliDliLoaderLoadSceneExtras(void)
+{
+  Context ctx;
+
+  auto path = ctx.pathProvider(ResourceType::Mesh) + "dli/extras.dli";
+  DALI_TEST_CHECK(ctx.loader.LoadScene(path, ctx.loadParams));
+  DALI_TEST_EQUAL(ctx.errors.size(), 3u);
+  DALI_TEST_CHECK(ctx.errors[0].find("already defined; overriding") != std::string::npos);
+  DALI_TEST_CHECK(ctx.errors[1].find("empty string is invalid for name") != std::string::npos);
+  DALI_TEST_CHECK(ctx.errors[2].find("failed to interpret value") != std::string::npos);
+
+  auto& scene = ctx.scene;
+  auto& roots = scene.GetRoots();
+  DALI_TEST_EQUAL(roots.size(), 1u);
+  DALI_TEST_EQUAL(scene.GetNode(roots[0])->mName, "root");
+
+  DALI_TEST_EQUAL(scene.GetNodeCount(), 1u);
+
+  ViewProjection viewProjection;
+  Transforms xforms {
+    MatrixStack{},
+    viewProjection
+  };
+  auto& resources = ctx.resources;
+  NodeDefinition::CreateParams nodeParams{
+    resources,
+    xforms,
+  };
+
+  Customization::Choices choices;
+
+  TestApplication app;
+  Actor actor = scene.CreateNodes(0, choices, nodeParams);
+
+  DALI_TEST_EQUAL(actor.GetProperty(actor.GetPropertyIndex("fudgeFactor")).Get<float>(), 9000.1f);
+  DALI_TEST_EQUAL(actor.GetProperty(actor.GetPropertyIndex("fudgeVector")).Get<Vector2>(), Vector2(-.25f, 17.f));
+  DALI_TEST_EQUAL(actor.GetProperty(actor.GetPropertyIndex("isThisTheRealLife")).Get<bool>(), true);
+  DALI_TEST_EQUAL(actor.GetProperty(actor.GetPropertyIndex("isThisJustFantasy")).Get<bool>(), false);
+  DALI_TEST_EQUAL(actor.GetProperty(actor.GetPropertyIndex("velocity")).Get<Vector3>(), Vector3(.1f, 58.f, -.2f));
+  DALI_TEST_EQUAL(actor.GetProperty(actor.GetPropertyIndex("frameOfReference")).Get<Matrix>(), Matrix::IDENTITY);
+
+  END_TEST;
+}
+
+int UtcDaliDliLoaderLoadSceneConstraints(void)
+{
+  Context ctx;
+
+  auto path = ctx.pathProvider(ResourceType::Mesh) + "dli/constraints.dli";
+  DALI_TEST_CHECK(ctx.loader.LoadScene(path, ctx.loadParams));
+  DALI_TEST_EQUAL(ctx.errors.size(), 1u);
+  DALI_TEST_CHECK(ctx.errors[0].find("invalid", ctx.errors[0].find("node ID")) != std::string::npos);
+
+  auto& scene = ctx.scene;
+  auto& roots = scene.GetRoots();
+  DALI_TEST_EQUAL(roots.size(), 1u);
+  DALI_TEST_EQUAL(scene.GetNode(0)->mName, "root");
+  DALI_TEST_EQUAL(scene.GetNode(1)->mName, "Alice");
+  DALI_TEST_EQUAL(scene.GetNode(2)->mName, "Bob");
+  DALI_TEST_EQUAL(scene.GetNode(3)->mName, "Charlie");
+
+  DALI_TEST_EQUAL(scene.GetNodeCount(), 4u);
+
+  ViewProjection viewProjection;
+  Transforms xforms {
+    MatrixStack{},
+    viewProjection
+  };
+  auto& resources = ctx.resources;
+  NodeDefinition::CreateParams nodeParams{
+    resources,
+    xforms,
+  };
+
+  Customization::Choices choices;
+
+  TestApplication app;
+
+  Actor root = scene.CreateNodes(0, choices, nodeParams);
+  Actor alice = root.FindChildByName("Alice");
+  Actor bob = root.FindChildByName("Bob");
+  Actor charlie = root.FindChildByName("Charlie");
+
+  DALI_TEST_EQUAL(nodeParams.mConstrainables.size(), 3u);
+  DALI_TEST_EQUAL(bob.GetProperty(bob.GetPropertyIndex("angularVelocity")).Get<Vector2>(), Vector2(-0.5, 0.0004));
+
+  ctx.errors.clear();
+  scene.ApplyConstraints(root, std::move(nodeParams.mConstrainables), ctx.onError);
+  DALI_TEST_CHECK(ctx.errors.empty());
+
+  app.GetScene().Add(root);
+  app.SendNotification();
+  app.Render();
+  app.SendNotification();
+  app.Render();
+
+  DALI_TEST_EQUAL(charlie.GetCurrentProperty(Actor::Property::ORIENTATION), alice.GetProperty(Actor::Property::ORIENTATION));
+  DALI_TEST_EQUAL(charlie.GetCurrentProperty(Actor::Property::POSITION), bob.GetProperty(Actor::Property::POSITION));
+  DALI_TEST_EQUAL(charlie.GetCurrentProperty(charlie.GetPropertyIndex("angularVelocity")), bob.GetProperty(bob.GetPropertyIndex("angularVelocity")));
+
+  END_TEST;
+}
+
+int UtcDaliDliLoaderNodeProcessor(void)
+{
+  Context ctx;
+
+  std::vector<Property::Map> nodeMaps;
+  ctx.input.mNodePropertyProcessor = [&](const NodeDefinition&, Property::Map&& map, StringCallback) {
+    nodeMaps.push_back(map);
+  };
+
+  auto path = ctx.pathProvider(ResourceType::Mesh) + "dli/node-processor.dli";
+  DALI_TEST_CHECK(ctx.loader.LoadScene(path, ctx.loadParams));
+
+  DALI_TEST_EQUAL(nodeMaps.size(), 2u);
+  DALI_TEST_EQUAL(nodeMaps[0].Count(), 5u);
+  DALI_TEST_EQUAL(nodeMaps[0].Find("name")->Get<std::string>(), "rootA");
+  DALI_TEST_EQUAL(nodeMaps[0].Find("nickname")->Get<std::string>(), "same as name");
+  DALI_TEST_EQUAL(nodeMaps[0].Find("favourite number")->Get<int32_t>(), 63478);
+
+  auto propArray = nodeMaps[0].Find("array");
+  DALI_TEST_EQUAL(propArray->GetType(), Property::ARRAY);
+
+  auto array = propArray->GetArray();
+  DALI_TEST_EQUAL(array->Count(), 5);
+  DALI_TEST_EQUAL(array->GetElementAt(0).Get<int32_t>(), 1);
+  DALI_TEST_EQUAL(array->GetElementAt(1).Get<int32_t>(), 2);
+  DALI_TEST_EQUAL(array->GetElementAt(2).Get<int32_t>(), 4);
+  DALI_TEST_EQUAL(array->GetElementAt(3).Get<int32_t>(), 8);
+  DALI_TEST_EQUAL(array->GetElementAt(4).Get<int32_t>(), -500);
+
+  auto propObject = nodeMaps[0].Find("object");
+  DALI_TEST_EQUAL(propObject->GetType(), Property::MAP);
+
+  auto object = propObject->GetMap();
+  DALI_TEST_EQUAL(object->Count(), 5);
+  DALI_TEST_EQUAL(object->Find("physics")->Get<bool>(), true);
+  DALI_TEST_EQUAL(object->Find("elasticity")->Get<float>(), .27f);
+  DALI_TEST_EQUAL(object->Find("drag")->Get<float>(), .91f);
+
+  auto propInnerArray = object->Find("inner array");
+  DALI_TEST_EQUAL(propInnerArray->GetType(), Property::ARRAY);
+
+  auto innerArray = propInnerArray->GetArray();
+  DALI_TEST_EQUAL(innerArray->Count(), 3);
+  DALI_TEST_EQUAL(innerArray->GetElementAt(0).Get<std::string>(), "why");
+  DALI_TEST_EQUAL(innerArray->GetElementAt(1).Get<std::string>(), "not");
+  DALI_TEST_EQUAL(innerArray->GetElementAt(2).Get<bool>(), false);
+
+  auto propInnerObject = object->Find("inner object");
+  DALI_TEST_EQUAL(propInnerObject->GetType(), Property::MAP);
+
+  auto innerObject = propInnerObject->GetMap();
+  DALI_TEST_EQUAL(innerObject->Count(), 1);
+  DALI_TEST_EQUAL(innerObject->Find("supported")->Get<bool>(), true);
+
+  DALI_TEST_EQUAL(nodeMaps[1].Count(), 1u);
+  DALI_TEST_EQUAL(nodeMaps[1].Find("name")->Get<std::string>(), "rootB");
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-EnvironmentDefinition.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-EnvironmentDefinition.cpp
new file mode 100644 (file)
index 0000000..e151c6a
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/environment-definition.h"
+#include <dali-test-suite-utils.h>
+#include <string_view>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+int UtcDaliEnvironmentDefinitionLoadRawDefault(void)
+{
+  EnvironmentDefinition envDef;
+  auto rawData = envDef.LoadRaw("");
+
+  DALI_TEST_EQUAL(rawData.mDiffuse.data.size(), 6u);
+  for (auto& face: rawData.mDiffuse.data)
+  {
+    DALI_TEST_EQUAL(face.size(), 1u);
+  }
+
+  DALI_TEST_EQUAL(rawData.mSpecular.data.size(), 6u);
+  for (auto& face: rawData.mSpecular.data)
+  {
+    DALI_TEST_EQUAL(face.size(), 1u);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliEnvironmentDefinitionLoadRawFail(void)
+{
+  for (auto name: { "nonexistent.ktx", TEST_RESOURCE_DIR "Cobe.obj" , TEST_RESOURCE_DIR "truncated.ktx" })
+  {
+    EnvironmentDefinition envDef { name, name };
+    DALI_TEST_ASSERTION(envDef.LoadRaw(""), "Failed to load cubemap texture");
+
+    envDef.mDiffuseMapPath = "";
+    DALI_TEST_ASSERTION(envDef.LoadRaw(""), "Failed to load cubemap texture"); // specular map still invalid
+  }
+
+  END_TEST;
+}
+
+int UtcDaliEnvironmentDefinitionLoadRawSuccess(void)
+{
+  EnvironmentDefinition envDef { "forest_irradiance.ktx", "forest_radiance.ktx" };
+  auto rawData = envDef.LoadRaw(TEST_RESOURCE_DIR "/");
+
+  DALI_TEST_EQUAL(rawData.mDiffuse.data.size(), 6u);
+  for (auto& face: rawData.mDiffuse.data)
+  {
+    DALI_TEST_EQUAL(face.size(), 1u);
+    uint32_t size = 64u;
+    for (auto& mipLevel : face)
+    {
+      DALI_TEST_EQUAL(mipLevel.GetPixelFormat(), Pixel::Format::RGB888);
+      DALI_TEST_EQUAL(mipLevel.GetWidth(), size);
+      DALI_TEST_EQUAL(mipLevel.GetHeight(), size);
+      size /= 2;
+    }
+  }
+
+  DALI_TEST_EQUAL(rawData.mSpecular.data.size(), 6u);
+  for (auto& face: rawData.mSpecular.data)
+  {
+    DALI_TEST_EQUAL(face.size(), 5u);
+    uint32_t size = 64u;
+    for (auto& mipLevel : face)
+    {
+      DALI_TEST_EQUAL(mipLevel.GetPixelFormat(), Pixel::Format::RGB888);
+      DALI_TEST_EQUAL(mipLevel.GetWidth(), size);
+      DALI_TEST_EQUAL(mipLevel.GetHeight(), size);
+      size /= 2;
+    }
+  }
+
+  END_TEST;
+}
+
+int UtcDaliEnvironmentDefinitionLoadEmptyRaw(void)
+{
+  EnvironmentDefinition::RawData rawData;
+  EnvironmentDefinition envDef;
+  auto textures = envDef.Load(std::move(rawData));
+  DALI_TEST_CHECK(!textures.mDiffuse);
+  DALI_TEST_CHECK(!textures.mSpecular);
+
+  END_TEST;
+}
+
+namespace
+{
+
+void CheckTextureDefault(Texture texture)
+{
+  DALI_TEST_CHECK(texture);
+  DALI_TEST_EQUAL(texture.GetWidth(), 1u);
+  DALI_TEST_EQUAL(texture.GetHeight(), 1u);
+}
+
+void CheckTextureNotDefault(Texture texture)
+{
+  DALI_TEST_CHECK(texture);
+  DALI_TEST_CHECK(texture.GetWidth() > 1u);
+  DALI_TEST_CHECK(texture.GetHeight() > 1u);
+}
+
+}
+
+int UtcDaliEnvironmentDefinitionLoadDefault(void)
+{
+  EnvironmentDefinition envDef{};
+  auto rawData = envDef.LoadRaw(TEST_RESOURCE_DIR "/");;
+
+  TestApplication app;
+  auto textures = envDef.Load(std::move(rawData));
+
+  CheckTextureDefault(textures.mSpecular);
+  CheckTextureDefault(textures.mDiffuse);
+
+  END_TEST;
+}
+
+int UtcDaliEnvironmentDefinitionLoadDiffuse(void)
+{
+  EnvironmentDefinition envDef{ "forest_irradiance.ktx" };
+  auto rawData = envDef.LoadRaw(TEST_RESOURCE_DIR "/");;
+
+  TestApplication app;
+  auto textures = envDef.Load(std::move(rawData));
+
+  CheckTextureNotDefault(textures.mDiffuse);
+  CheckTextureDefault(textures.mSpecular);
+
+  END_TEST;
+}
+
+int UtcDaliEnvironmentDefinitionLoadSpecular(void)
+{
+  EnvironmentDefinition envDef{ "", "forest_radiance.ktx" };
+  auto rawData = envDef.LoadRaw(TEST_RESOURCE_DIR "/");;
+
+  TestApplication app;
+  auto textures = envDef.Load(std::move(rawData));
+
+  CheckTextureDefault(textures.mDiffuse);
+  CheckTextureNotDefault(textures.mSpecular);
+
+  END_TEST;
+}
+
+int UtcDaliEnvironmentDefinitionLoadBoth(void)
+{
+  EnvironmentDefinition envDef{ "forest_irradiance.ktx", "forest_radiance.ktx" };
+  auto rawData = envDef.LoadRaw(TEST_RESOURCE_DIR "/");;
+
+  TestApplication app;
+  auto textures = envDef.Load(std::move(rawData));
+
+  CheckTextureNotDefault(textures.mDiffuse);
+  CheckTextureNotDefault(textures.mSpecular);
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-Gltf2Loader.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-Gltf2Loader.cpp
new file mode 100644 (file)
index 0000000..3f829dc
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/resource-bundle.h"
+#include "dali-scene-loader/public-api/scene-definition.h"
+#include "dali-scene-loader/public-api/load-result.h"
+#include "dali-scene-loader/public-api/gltf2-loader.h"
+#include "dali-scene-loader/public-api/shader-definition-factory.h"
+#include <dali-test-suite-utils.h>
+#include <string_view>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+#define DALI_TEST_THROW(expression, exception, predicate) \
+  {\
+    bool daliTestThrowSuccess__ = false;\
+    try\
+    {\
+      do { expression; } while(0);\
+      printf("No exception was thrown.\n");\
+    }\
+    catch (std::decay<exception>::type& ex)\
+    {\
+      daliTestThrowSuccess__ = predicate(ex);\
+    }\
+    catch (...)\
+    {\
+      printf("Wrong type of exception thrown.\n");\
+    }\
+    DALI_TEST_CHECK(daliTestThrowSuccess__);\
+  }
+
+namespace
+{
+struct Context
+{
+  ResourceBundle resources;
+  SceneDefinition scene;
+
+  std::vector<AnimationDefinition> animations;
+  std::vector<AnimationGroupDefinition> animationGroups;
+  std::vector<CameraParameters> cameras;
+  std::vector<LightParameters> lights;
+
+  LoadResult loadResult {
+    resources,
+    scene,
+    animations,
+    animationGroups,
+    cameras,
+    lights
+  };
+};
+
+struct ExceptionMessageStartsWith
+{
+  const std::string_view expected;
+
+  bool operator()(const std::runtime_error& e)
+  {
+    const bool success = (0 == strncmp(e.what(), expected.data(), expected.size()));
+    if (!success)
+    {
+      printf("Expected: %s, got: %s.\n", expected.data(), e.what());
+    }
+    return success;
+  }
+};
+
+}
+
+int UtcDaliGltfLoaderFailedToLoad(void)
+{
+  Context ctx;
+
+  ShaderDefinitionFactory sdf;
+  sdf.SetResources(ctx.resources);
+
+  DALI_TEST_THROW(LoadGltfScene("non-existent.gltf", sdf, ctx.loadResult),
+    std::runtime_error,
+    ExceptionMessageStartsWith{"Failed to load"});
+
+  DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
+  DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
+
+  DALI_TEST_EQUAL(0, ctx.resources.mEnvironmentMaps.size());
+  DALI_TEST_EQUAL(0, ctx.resources.mMaterials.size());
+  DALI_TEST_EQUAL(0, ctx.resources.mMeshes.size());
+  DALI_TEST_EQUAL(0, ctx.resources.mShaders.size());
+  DALI_TEST_EQUAL(0, ctx.resources.mSkeletons.size());
+
+  DALI_TEST_EQUAL(0, ctx.cameras.size());
+  DALI_TEST_EQUAL(0, ctx.lights.size());
+  DALI_TEST_EQUAL(0, ctx.animations.size());
+  DALI_TEST_EQUAL(0, ctx.animationGroups.size());
+
+  END_TEST;
+}
+
+int UtcDaliGltfLoaderFailedToParse(void)
+{
+  Context ctx;
+
+  ShaderDefinitionFactory sdf;
+  sdf.SetResources(ctx.resources);
+
+  DALI_TEST_THROW(LoadGltfScene(TEST_RESOURCE_DIR "/invalid.gltf", sdf, ctx.loadResult),
+    std::runtime_error,
+    ExceptionMessageStartsWith{"Failed to parse"});
+
+  DALI_TEST_EQUAL(0, ctx.scene.GetRoots().size());
+  DALI_TEST_EQUAL(0, ctx.scene.GetNodeCount());
+
+  DALI_TEST_EQUAL(0, ctx.resources.mEnvironmentMaps.size());
+  DALI_TEST_EQUAL(0, ctx.resources.mMaterials.size());
+  DALI_TEST_EQUAL(0, ctx.resources.mMeshes.size());
+  DALI_TEST_EQUAL(0, ctx.resources.mShaders.size());
+  DALI_TEST_EQUAL(0, ctx.resources.mSkeletons.size());
+
+  DALI_TEST_EQUAL(0, ctx.cameras.size());
+  DALI_TEST_EQUAL(0, ctx.lights.size());
+  DALI_TEST_EQUAL(0, ctx.animations.size());
+  DALI_TEST_EQUAL(0, ctx.animationGroups.size());
+
+  END_TEST;
+}
+
+int UtcDaliGltfLoaderSuccess1(void)
+{
+  Context ctx;
+
+  ShaderDefinitionFactory sdf;
+  sdf.SetResources(ctx.resources);
+
+  LoadGltfScene(TEST_RESOURCE_DIR "/AnimatedCube.gltf", sdf, ctx.loadResult);
+
+  DALI_TEST_EQUAL(1u, ctx.scene.GetRoots().size());
+  DALI_TEST_EQUAL(6u, ctx.scene.GetNodeCount());
+
+  DALI_TEST_EQUAL(0u, ctx.resources.mEnvironmentMaps.size());
+
+  auto& materials = ctx.resources.mMaterials;
+  DALI_TEST_EQUAL(2u, materials.size());
+  const MaterialDefinition materialGroundTruth[] {
+    {
+      MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
+        MaterialDefinition::NORMAL | MaterialDefinition::TRANSPARENCY | MaterialDefinition::GLTF_CHANNELS |
+        (0x80 << MaterialDefinition::ALPHA_CUTOFF_SHIFT),
+      0,
+      Vector4(1.f, .766f, .336f, 1.f),
+      1.f,
+      0.f,
+      {
+        { MaterialDefinition::ALBEDO,
+          { "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT) } },
+        { MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
+          { "AnimatedCube_MetallicRoughness.png",
+            SamplerFlags::Encode(FilterMode::NEAREST_MIPMAP_LINEAR, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::MIRRORED_REPEAT) } },
+        { MaterialDefinition::NORMAL,
+          { "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT) } },
+      }
+    },
+    {
+      MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
+        MaterialDefinition::NORMAL | MaterialDefinition::GLTF_CHANNELS,
+      0,
+      Vector4(1.f, .766f, .336f, 1.f),
+      1.f,
+      0.f,
+      {
+        { MaterialDefinition::ALBEDO,
+          { "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT) } },
+        { MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS | MaterialDefinition::GLTF_CHANNELS,
+          { "AnimatedCube_MetallicRoughness.png",
+            SamplerFlags::Encode(FilterMode::NEAREST_MIPMAP_LINEAR, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::MIRRORED_REPEAT) } },
+        { MaterialDefinition::NORMAL,
+          { "AnimatedCube_BaseColor.png",
+            SamplerFlags::Encode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR, WrapMode::CLAMP_TO_EDGE, WrapMode::REPEAT) } },
+      }
+    },
+  };
+
+  auto iMaterial = materials.begin();
+  for (auto& m : materialGroundTruth)
+  {
+    printf("material %ld\n", iMaterial - materials.begin());
+    auto& md = iMaterial->first;
+    DALI_TEST_EQUAL(md.mFlags, m.mFlags);
+    DALI_TEST_EQUAL(md.mEnvironmentIdx, m.mEnvironmentIdx);
+    DALI_TEST_EQUAL(md.mColor, m.mColor);
+    DALI_TEST_EQUAL(md.mMetallic, m.mMetallic);
+    DALI_TEST_EQUAL(md.mRoughness, m.mRoughness);
+
+    DALI_TEST_EQUAL(md.mTextureStages.size(), m.mTextureStages.size());
+    auto iTexture = md.mTextureStages.begin();
+    for (auto& ts: m.mTextureStages)
+    {
+      printf("texture %ld\n", iTexture - md.mTextureStages.begin());
+      DALI_TEST_EQUAL(iTexture->mSemantic, ts.mSemantic);
+      DALI_TEST_EQUAL(iTexture->mTexture.mImageUri, ts.mTexture.mImageUri);
+      DALI_TEST_EQUAL(uint32_t(iTexture->mTexture.mSamplerFlags), uint32_t(ts.mTexture.mSamplerFlags)); // don't interpret it as a character
+      ++iTexture;
+    }
+    ++iMaterial;
+  }
+
+  auto& meshes = ctx.resources.mMeshes;
+  DALI_TEST_EQUAL(2u, meshes.size());
+
+  using Blob = MeshDefinition::Blob;
+  using Accessor = MeshDefinition::Accessor;
+  const MeshDefinition meshGroundTruth[] {
+    {
+      0,
+      Geometry::TRIANGLES,
+      "AnimatedCube.bin",
+      Accessor{ Blob{ 0, 0 }, {} },
+      Accessor{ Blob{ 0, 0 }, {} },
+      Accessor{ Blob{ 0, 0 }, {} },
+      Accessor{ Blob{ 0, 0 }, {} },
+      Accessor{ Blob{ 0, 0 }, {} },
+    },
+    {
+      0,
+      Geometry::TRIANGLES,
+      "AnimatedCube.bin",
+      Accessor{ Blob{ 0, 0 }, {} },
+      Accessor{ Blob{ 0, 0 }, {} },
+      Accessor{ Blob{ 0, 0 }, {} },
+      Accessor{ Blob{ 0, 0 }, {} },
+      Accessor{ Blob{ 0, 0 }, {} },
+    },
+  };
+
+  auto iMesh = meshes.begin();
+  for (auto& m : meshGroundTruth)
+  {
+    printf("mesh %ld\n", iMesh - meshes.begin());
+
+    auto& md = iMesh->first;
+    DALI_TEST_EQUAL(md.mFlags, m.mFlags);
+    DALI_TEST_EQUAL(md.mPrimitiveType, m.mPrimitiveType);
+    for (auto mp: {
+      &MeshDefinition::mIndices,
+      &MeshDefinition::mPositions,
+      &MeshDefinition::mNormals,
+      &MeshDefinition::mTexCoords,
+      &MeshDefinition::mTangents,
+      &MeshDefinition::mJoints0,
+      &MeshDefinition::mWeights0
+    })
+    {
+      DALI_TEST_EQUAL((md.*mp).IsDefined(), (m.*mp).IsDefined());
+      DALI_TEST_EQUAL((md.*mp).mBlob.IsDefined(), (m.*mp).mBlob.IsDefined());
+    }
+
+    DALI_TEST_EQUAL(md.mBlendShapeHeader.IsDefined(), m.mBlendShapeHeader.IsDefined());
+
+    ++iMesh;
+  }
+
+  DALI_TEST_EQUAL(2u, ctx.resources.mShaders.size());
+  DALI_TEST_EQUAL(0u, ctx.resources.mSkeletons.size());
+
+  DALI_TEST_EQUAL(3u, ctx.cameras.size());
+  DALI_TEST_EQUAL(0u, ctx.lights.size());
+  DALI_TEST_EQUAL(1u, ctx.animations.size());
+  DALI_TEST_EQUAL(0u, ctx.animationGroups.size());
+
+  END_TEST;
+}
+
+int UtcDaliGltfLoaderSuccessShort(void)
+{
+  TestApplication app;
+
+  const std::string resourcePath = TEST_RESOURCE_DIR "/";
+  auto pathProvider = [resourcePath](ResourceType::Value) {
+    return resourcePath;
+  };
+
+  Customization::Choices choices;
+  for (auto modelName : {
+    "2CylinderEngine",
+    "AnimatedMorphCube",
+    "AnimatedMorphSphere",
+    "AnimatedTriangle",
+    "BoxAnimated",
+    "CesiumMan",
+    "CesiumMilkTruck",
+    "EnvironmentTest",
+    "MetalRoughSpheres",
+    "MorphPrimitivesTest",
+    "SimpleSparseAccessor",
+  })
+  {
+    Context ctx;
+
+    ShaderDefinitionFactory sdf;
+
+    auto& resources = ctx.resources;
+    resources.mEnvironmentMaps.push_back({});
+
+    sdf.SetResources(resources);
+
+    printf("%s\n", modelName);
+    LoadGltfScene(resourcePath + modelName + ".gltf", sdf, ctx.loadResult);
+    DALI_TEST_CHECK(ctx.scene.GetNodeCount() > 0);
+
+    auto& scene = ctx.scene;
+    for (auto iRoot : scene.GetRoots())
+    {
+      struct Visitor: NodeDefinition::IVisitor
+      {
+        struct ResourceReceiver: IResourceReceiver
+        {
+          std::vector<bool> mCounts;
+
+          void Register(ResourceType::Value type, Index id) override
+          {
+            if (type == ResourceType::Mesh)
+            {
+              mCounts[id] = true;
+            }
+          }
+        } receiver;
+
+        void Start(NodeDefinition& n) override
+        {
+          if (n.mRenderable)
+          {
+            n.mRenderable->RegisterResources(receiver);
+          }
+        }
+
+        void Finish(NodeDefinition& n) override
+        {}
+      } visitor;
+      visitor.receiver.mCounts.resize(resources.mMeshes.size(), false);
+
+      scene.Visit(iRoot, choices, visitor);
+      for (uint32_t i0 = 0, i1 = resources.mMeshes.size(); i0 < i1; ++i0)
+      {
+        if (visitor.receiver.mCounts[i0])
+        {
+          auto raw = resources.mMeshes[i0].first.LoadRaw(resourcePath);
+          DALI_TEST_CHECK(!raw.mAttribs.empty());
+
+          resources.mMeshes[i0].second = resources.mMeshes[i0].first.Load(std::move(raw));
+          DALI_TEST_CHECK(resources.mMeshes[i0].second.geometry);
+        }
+      }
+    }
+  }
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-KtxLoader.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-KtxLoader.cpp
new file mode 100644 (file)
index 0000000..15d6b58
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/ktx-loader.h"
+#include <dali-test-suite-utils.h>
+#include <string_view>
+
+#include <fstream>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+int UtcDaliKtxLoaderFailNonexistent(void)
+{
+  CubeData data;
+  DALI_TEST_CHECK(!LoadCubeMapData("non-existent.ktx", data));
+  END_TEST;
+}
+
+int UtcDaliKtxLoaderFailInvalid1(void)
+{
+  CubeData data;
+  DALI_TEST_CHECK(!LoadCubeMapData(TEST_RESOURCE_DIR "/invalid.svg", data)); // file smaller than KTX header
+  END_TEST;
+}
+
+int UtcDaliKtxLoaderFailInvalid2(void)
+{
+  CubeData data;
+  DALI_TEST_CHECK(!LoadCubeMapData(TEST_RESOURCE_DIR "/anim.gif", data)); // not a KTX
+  END_TEST;
+}
+
+int UtcDaliKtxLoaderFailTruncated(void)
+{
+  CubeData data;
+  DALI_TEST_CHECK(!LoadCubeMapData(TEST_RESOURCE_DIR "/truncated.ktx", data));
+  END_TEST;
+}
+
+int UtcDaliKtxLoaderSuccess(void)
+{
+  CubeData cubeData;
+  auto path = TEST_RESOURCE_DIR "/forest_radiance.ktx";
+  DALI_TEST_CHECK(LoadCubeMapData(path, cubeData));
+
+  DALI_TEST_EQUAL(6u, cubeData.data.size());
+  for (auto& face: cubeData.data)
+  {
+    uint32_t size = 64;
+    for (auto& mipData: face)
+    {
+      DALI_TEST_EQUAL(size, mipData.GetWidth());
+      DALI_TEST_EQUAL(size, mipData.GetHeight());
+      DALI_TEST_EQUAL(Pixel::Format::RGB888, mipData.GetPixelFormat());
+      size /= 2;
+    }
+  }
+
+  END_TEST;
+}
+
+int UtcDaliKtxLoaderFormats(void)
+{
+  auto resPath = TEST_RESOURCE_DIR "/";
+#define FORMAT_PATH(x) { #x, Pixel::x }
+#define FORMAT_COMPRESSED_PATH(x) { #x, Pixel::COMPRESSED_ ## x ## _KHR }
+  std::pair<std::string, Pixel::Format> pathFormats[] {
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_4x4),
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_5x4),
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_5x5),
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_6x5),
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_6x6),
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_8x5),
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_8x6),
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_8x8),
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_10x5),
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_10x6),
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_10x10),
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_12x10),
+    FORMAT_COMPRESSED_PATH(RGBA_ASTC_12x12),
+    FORMAT_PATH(RGB16F),
+    FORMAT_PATH(RGB32F),
+    FORMAT_PATH(RGBA8888),
+  };
+  for (auto i : pathFormats)
+  {
+    CubeData cubeData;
+    DALI_TEST_CHECK(LoadCubeMapData(resPath + i.first + ".ktx", cubeData));
+    DALI_TEST_EQUAL(cubeData.data[0][0].GetPixelFormat(), i.second);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliKtxLoaderCubeDataCreateTexture1(void)
+{
+  uint32_t pixelBufferSize = 3;
+  uint8_t* pixelBuffer = new uint8_t[pixelBufferSize];
+
+  CubeData cubeData;
+  cubeData.data.push_back({});
+
+  auto pixelData = PixelData::New(pixelBuffer, pixelBufferSize, 1, 1, Pixel::Format::RGB888, PixelData::DELETE_ARRAY);
+  cubeData.data[0].push_back(pixelData);
+
+  TestApplication app;
+  auto texture = cubeData.CreateTexture();
+
+  DALI_TEST_CHECK(texture);
+  DALI_TEST_EQUAL(1u, texture.GetWidth());
+  DALI_TEST_EQUAL(1u, texture.GetHeight());
+
+  END_TEST;
+}
+
+int UtcDaliKtxLoaderCubeDataCreateTexture2(void)
+{
+  CubeData cubeData;
+  auto path = TEST_RESOURCE_DIR "/forest_radiance.ktx";
+  DALI_TEST_CHECK(LoadCubeMapData(path, cubeData));
+
+  TestApplication app;
+  auto texture = cubeData.CreateTexture();
+
+  DALI_TEST_CHECK(texture);
+  DALI_TEST_EQUAL(64u, texture.GetWidth());
+  DALI_TEST_EQUAL(64u, texture.GetHeight());
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-MatrixStack.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-MatrixStack.cpp
new file mode 100644 (file)
index 0000000..776ded7
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/matrix-stack.h"
+#include <dali-test-suite-utils.h>
+#include <string_view>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+int UtcDaliMatrixStack(void)
+{
+  MatrixStack stack;
+  DALI_TEST_CHECK(stack.IsEmpty());
+
+  Matrix testMatrix;
+  testMatrix.SetTransformComponents(Vector3::ONE * 5.f,
+    Quaternion(Radian(Degree(-45.f)), Vector3::XAXIS),
+    Vector3(100.f, 0.f, -200.f));
+
+  stack.Push(testMatrix);
+  DALI_TEST_CHECK(!stack.IsEmpty());
+  DALI_TEST_EQUAL(stack.Top(), testMatrix);
+
+  stack.Push(Matrix::IDENTITY);
+  DALI_TEST_CHECK(!stack.IsEmpty());
+  DALI_TEST_EQUAL(stack.Top(), testMatrix);
+
+  stack.Push(testMatrix);
+
+  Matrix testMatrix2;
+  Matrix::Multiply(testMatrix2, testMatrix, testMatrix);
+  DALI_TEST_CHECK(!stack.IsEmpty());
+  DALI_TEST_EQUAL(stack.Top(), testMatrix2);
+
+  stack.PopAll();
+  DALI_TEST_CHECK(stack.IsEmpty());
+
+  stack.Push(Matrix::IDENTITY);
+  DALI_TEST_EQUAL(stack.Top(), Matrix::IDENTITY);
+
+  stack.Push(testMatrix);
+  DALI_TEST_EQUAL(stack.Top(), testMatrix);
+
+  stack.Pop();
+  DALI_TEST_EQUAL(stack.Top(), Matrix::IDENTITY);
+  DALI_TEST_CHECK(!stack.IsEmpty());
+
+  stack.Pop();
+  DALI_TEST_CHECK(stack.IsEmpty());
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-NodeDefinition.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-NodeDefinition.cpp
new file mode 100644 (file)
index 0000000..7f1a7cf
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/node-definition.h"
+#include "dali-scene-loader/public-api/view-projection.h"
+#include <toolkit-test-application.h>
+#include <dali-test-suite-utils.h>
+#include <string_view>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+namespace
+{
+
+struct Context
+{
+  ResourceBundle resources;
+
+  ViewProjection viewProjection;
+  Transforms transforms { MatrixStack{}, viewProjection };
+
+  NodeDefinition::CreateParams createParams {
+    resources,
+    transforms
+  };
+};
+
+}
+
+int UtcDaliConstraintDefinitionsCompare(void)
+{
+  ConstraintDefinition cd1{ "orientation", 0 };
+  ConstraintDefinition cd2{ "position", 1 };
+
+  DALI_TEST_CHECK(cd1 < cd2);
+  DALI_TEST_CHECK(!(cd2 < cd1));
+  DALI_TEST_CHECK(!(cd1 < cd1));
+  DALI_TEST_CHECK(!(cd2 < cd2));
+
+  DALI_TEST_CHECK(cd1 == cd1);
+  DALI_TEST_CHECK(cd2 == cd2);
+
+  ConstraintDefinition cd3{ "position", 0 };
+  ConstraintDefinition cd4{ "scale", 1 };
+  ConstraintDefinition cd5{ "position", 1 };
+  DALI_TEST_CHECK(cd2 != cd3);
+  DALI_TEST_CHECK(cd2 != cd4);
+  DALI_TEST_CHECK(cd2 == cd5);
+  DALI_TEST_CHECK(cd5 == cd2);
+
+  END_TEST;
+}
+
+int UtcDaliBlendshapeShaderConfigurationRequestsCompare(void)
+{
+  TestApplication app;
+  BlendshapeShaderConfigurationRequest bsscr1{ "", 0, Shader(nullptr) };
+
+  BlendshapeShaderConfigurationRequest bsscr2{ "", 0, Shader::New(
+    "void main(){ gl_Position = vec4(0.); }",
+    "void main(){ gl_FragColor = vec4(1.); }"
+  ) };
+
+  DALI_TEST_CHECK(bsscr1 < bsscr2);
+  DALI_TEST_CHECK(!(bsscr2 < bsscr1));
+  DALI_TEST_CHECK(!(bsscr1 < bsscr1));
+  DALI_TEST_CHECK(!(bsscr2 < bsscr2));
+
+  END_TEST;
+}
+
+int UtcDaliNodeDefinitionExtrasCompare(void)
+{
+  NodeDefinition::Extra e1{ "alpha", Vector3::XAXIS * 2.f };
+  NodeDefinition::Extra e2{ "beta", 8 };
+
+  DALI_TEST_CHECK(e1 < e2);
+  DALI_TEST_CHECK(!(e1 < e1));
+  DALI_TEST_CHECK(!(e2 < e1));
+  DALI_TEST_CHECK(!(e2 < e2));
+
+  END_TEST;
+}
+
+int UtcDaliNodeDefinitionProperties(void)
+{
+  TestApplication testApp;
+  NodeDefinition nodeDef{
+    "testRootNode",
+    Vector3{ -100.f, 100.f, -500.f },
+    Quaternion{ Radian(Degree(45.f)), Vector3::ZAXIS },
+    Vector3{ 2.f, 4.f, 8.f },
+    Vector3{ 100.f, 50.f, 25.f },
+    false,
+  };
+
+  Quaternion frobnicateFactor(0.f, 1.f, 2.f, 3.f);
+  frobnicateFactor.Normalize(); // because it will be (by DALi) once it's set as a property.
+  nodeDef.mExtras.push_back(NodeDefinition::Extra{ "frobnicateFactor", frobnicateFactor });
+
+  Context ctx;
+  auto actor = nodeDef.CreateActor(ctx.createParams);
+  DALI_TEST_EQUAL(nodeDef.mName, actor.GetProperty(Actor::Property::NAME).Get<std::string>());
+  DALI_TEST_EQUAL(nodeDef.mPosition, actor.GetProperty(Actor::Property::POSITION).Get<Vector3>());
+  DALI_TEST_EQUAL(nodeDef.mOrientation, actor.GetProperty(Actor::Property::ORIENTATION).Get<Quaternion>());
+  DALI_TEST_EQUAL(nodeDef.mScale, actor.GetProperty(Actor::Property::SCALE).Get<Vector3>());
+  DALI_TEST_EQUAL(nodeDef.mSize, actor.GetProperty(Actor::Property::SIZE).Get<Vector3>());
+  DALI_TEST_EQUAL(nodeDef.mIsVisible, actor.GetProperty(Actor::Property::VISIBLE).Get<bool>());
+
+  Property::Index propFrobnicateFactor = actor.GetPropertyIndex("frobnicateFactor");
+  DALI_TEST_CHECK(propFrobnicateFactor != Property::INVALID_INDEX);
+
+  auto frobnicateFactorValue = actor.GetProperty(propFrobnicateFactor);
+  DALI_TEST_EQUAL(Property::ROTATION, frobnicateFactorValue.GetType());
+  DALI_TEST_EQUAL(frobnicateFactorValue.Get<Quaternion>(), frobnicateFactor);
+
+  DALI_TEST_EQUAL(0, actor.GetChildCount());
+  DALI_TEST_EQUAL(0, actor.GetRendererCount());
+
+  END_TEST;
+}
+
+int UtcDaliNodeDefinitionRenderableRegisterResources(void)
+{
+  NodeDefinition nodeDef;
+
+  auto renderable = new NodeDefinition::Renderable();
+  renderable->mShaderIdx = 0;
+  nodeDef.mRenderable.reset(renderable);
+
+  struct : IResourceReceiver
+  {
+    std::vector<Index> shaders;
+    uint32_t otherResources = 0;
+
+    void Register(ResourceType::Value type, Index id) override
+    {
+      switch(type)
+      {
+      case ResourceType::Shader:
+        shaders.push_back(id);
+        break;
+
+      default:
+        ++otherResources;
+      }
+    }
+  } resourceReceiver;
+
+  nodeDef.mRenderable->RegisterResources(resourceReceiver);
+  DALI_TEST_EQUAL(1u, resourceReceiver.shaders.size());
+  DALI_TEST_EQUAL(0, resourceReceiver.shaders[0]);
+  DALI_TEST_EQUAL(0, resourceReceiver.otherResources);
+
+  END_TEST;
+}
+
+int UtcDaliNodeDefinitionRenderableReflectResources(void)
+{
+  NodeDefinition nodeDef;
+
+  auto renderable = new NodeDefinition::Renderable();
+  renderable->mShaderIdx = 0;
+  nodeDef.mRenderable.reset(renderable);
+
+  struct : IResourceReflector
+  {
+    std::vector<Index*> shaders;
+    uint32_t otherResources = 0;
+
+    void Reflect(ResourceType::Value type, Index& id) override
+    {
+      switch(type)
+      {
+      case ResourceType::Shader:
+        shaders.push_back(&id);
+        break;
+
+      default:
+        ++otherResources;
+      }
+    }
+  } resourceReflector;
+
+  nodeDef.mRenderable->ReflectResources(resourceReflector);
+  DALI_TEST_EQUAL(1u, resourceReflector.shaders.size());
+  DALI_TEST_EQUAL(&renderable->mShaderIdx, resourceReflector.shaders[0]);
+  DALI_TEST_EQUAL(0, resourceReflector.otherResources);
+
+  END_TEST;
+}
+
+int UtcDaliNodeDefinitionRenderable(void)
+{
+  TestApplication testApp;
+  NodeDefinition nodeDef;
+
+  auto renderable = new NodeDefinition::Renderable();
+  renderable->mShaderIdx = 0;
+  nodeDef.mRenderable.reset(renderable);
+
+  Context ctx;
+  const auto VSH = "void main() { gl_Position = vec4(0.); }";
+  const auto FSH = "void main() { gl_FragColor = vec4(1.); }";
+  auto shader = Shader::New(VSH, FSH);
+  ctx.resources.mShaders.push_back({ ShaderDefinition{}, shader });
+
+  auto actor = nodeDef.CreateActor(ctx.createParams);
+  DALI_TEST_EQUAL(1, actor.GetRendererCount());
+
+  auto renderer = actor.GetRendererAt(0);
+  DALI_TEST_EQUAL(renderer.GetShader(), shader);
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-RendererState.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-RendererState.cpp
new file mode 100644 (file)
index 0000000..a6f6251
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/renderer-state.h"
+#include "dali-scene-loader/public-api/parse-renderer-state.h"
+#include "dali-scene-loader/public-api/utils.h"
+#include <dali-test-suite-utils.h>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+namespace sl = SceneLoader;
+namespace rs = RendererState;
+
+int UtcDaliRendererStateFromBlendFactors(void)
+{
+  rs::Type state = rs::FromBlendFactors(sl::BlendFactor::ZERO, sl::BlendFactor::ONE,
+    sl::BlendFactor::SRC_COLOR, sl::BlendFactor::ONE_MINUS_SRC_COLOR);
+
+  DALI_TEST_EQUAL((state >> rs::BLEND_FACTOR_BASE_SHIFT) & rs::BLEND_FACTOR_ITEM_MASK, rs::Type(sl::BlendFactor::ZERO));
+  DALI_TEST_EQUAL((state >> (rs::BLEND_FACTOR_BASE_SHIFT + rs::BLEND_FACTOR_ITEM_BITS)) &
+    rs::BLEND_FACTOR_ITEM_MASK, rs::Type(sl::BlendFactor::ONE));
+  DALI_TEST_EQUAL((state >> (rs::BLEND_FACTOR_BASE_SHIFT + rs::BLEND_FACTOR_ITEM_BITS * 2)) &
+    rs::BLEND_FACTOR_ITEM_MASK, rs::Type(sl::BlendFactor::SRC_COLOR));
+  DALI_TEST_EQUAL((state >> (rs::BLEND_FACTOR_BASE_SHIFT + rs::BLEND_FACTOR_ITEM_BITS * 3)) &
+    rs::BLEND_FACTOR_ITEM_MASK, rs::Type(sl::BlendFactor::ONE_MINUS_SRC_COLOR));
+
+  END_TEST;
+}
+
+#define HELP_TEST_RENDERER_STATE(property, resetValue, state, checkValue, renderer)\
+  renderer.SetProperty(property, ~(resetValue));\
+  rs::Apply((state), (renderer));\
+  printf("%s %s vs %s\n", #property, #state, #checkValue);\
+  DALI_TEST_EQUAL(renderer.GetProperty(property).Get<decltype(checkValue)>(), (checkValue));
+
+int UtcDaliRendererStateApply(void)
+{
+  TestApplication app;
+  auto vsh = "void main() { gl_Position = vec4(0.); }";
+  auto fsh = "void main() { gl_FragColor = vec4(1.); }";
+  Geometry geom = Geometry::New();
+  Shader shader = Shader::New(vsh, fsh);
+  Renderer renderer = Renderer::New(geom, shader);
+
+  HELP_TEST_RENDERER_STATE(Renderer::Property::DEPTH_WRITE_MODE, DepthWriteMode::OFF, rs::DEPTH_WRITE, DepthWriteMode::ON, renderer);
+  HELP_TEST_RENDERER_STATE(Renderer::Property::DEPTH_TEST_MODE, DepthTestMode::OFF, rs::DEPTH_TEST, DepthTestMode::ON, renderer);
+
+  HELP_TEST_RENDERER_STATE(Renderer::Property::BLEND_MODE, BlendMode::OFF, rs::ALPHA_BLEND, BlendMode::ON, renderer);
+
+  HELP_TEST_RENDERER_STATE(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::NONE, rs::CULL_FRONT, FaceCullingMode::FRONT, renderer);
+  HELP_TEST_RENDERER_STATE(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::NONE, rs::CULL_BACK, FaceCullingMode::BACK, renderer);
+  HELP_TEST_RENDERER_STATE(Renderer::Property::FACE_CULLING_MODE, FaceCullingMode::NONE, rs::CULL_FRONT | rs::CULL_BACK,
+    FaceCullingMode::FRONT_AND_BACK, renderer);
+
+#define DEPTH_FUNC_PAIR(x) { Comparison::x << rs::DEPTH_FUNCTION_SHIFT, DepthFunction::x }
+  const std::pair<rs::Type, DepthFunction::Type> depthFunctionPairs[] {
+    DEPTH_FUNC_PAIR(NEVER),
+    DEPTH_FUNC_PAIR(ALWAYS),
+    DEPTH_FUNC_PAIR(LESS),
+    DEPTH_FUNC_PAIR(GREATER),
+    DEPTH_FUNC_PAIR(EQUAL),
+    DEPTH_FUNC_PAIR(NOT_EQUAL),
+    DEPTH_FUNC_PAIR(LESS_EQUAL),
+    DEPTH_FUNC_PAIR(GREATER_EQUAL),
+  };
+#undef DEPTH_FUNC_PAIR
+  for (auto& p : depthFunctionPairs)
+  {
+    HELP_TEST_RENDERER_STATE(Renderer::Property::DEPTH_FUNCTION, DepthFunction::LESS, p.first, p.second, renderer);
+  }
+
+#define BLEND_FACTOR_PAIR(x) { sl::BlendFactor::x, BlendFactor::x }
+  const std::pair<rs::Type, BlendFactor::Type> BLEND_FACTORS[] {
+    BLEND_FACTOR_PAIR(ZERO),
+    BLEND_FACTOR_PAIR(ONE),
+    BLEND_FACTOR_PAIR(SRC_COLOR),
+    BLEND_FACTOR_PAIR(ONE_MINUS_SRC_COLOR),
+    BLEND_FACTOR_PAIR(SRC_ALPHA),
+    BLEND_FACTOR_PAIR(ONE_MINUS_SRC_ALPHA),
+    BLEND_FACTOR_PAIR(DST_COLOR),
+    BLEND_FACTOR_PAIR(ONE_MINUS_DST_COLOR),
+    BLEND_FACTOR_PAIR(DST_ALPHA),
+    BLEND_FACTOR_PAIR(ONE_MINUS_DST_ALPHA),
+    BLEND_FACTOR_PAIR(SRC_ALPHA_SATURATE),
+    BLEND_FACTOR_PAIR(CONSTANT_COLOR),
+    BLEND_FACTOR_PAIR(ONE_MINUS_CONSTANT_COLOR),
+    BLEND_FACTOR_PAIR(CONSTANT_ALPHA),
+    BLEND_FACTOR_PAIR(ONE_MINUS_CONSTANT_ALPHA),
+  };
+#undef BLEND_FACTOR_PAIR
+  for (auto property: {
+    Renderer::Property::BLEND_FACTOR_SRC_RGB,
+    Renderer::Property::BLEND_FACTOR_DEST_RGB,
+    Renderer::Property::BLEND_FACTOR_SRC_ALPHA,
+    Renderer::Property::BLEND_FACTOR_DEST_ALPHA,
+  })
+  {
+    const auto itemShift = rs::BLEND_FACTOR_ITEM_BITS * (property - Renderer::Property::BLEND_FACTOR_SRC_RGB);
+    const auto shift = rs::BLEND_FACTOR_BASE_SHIFT + itemShift;
+    for (auto& value: BLEND_FACTORS)
+    {
+      HELP_TEST_RENDERER_STATE(property, BlendFactor::ZERO, value.first << shift, value.second, renderer);
+    }
+  }
+
+#define RENDER_MODE_PAIR(x) { BufferMode::x << rs::BUFFER_MODE_SHIFT, RenderMode::x }
+  std::pair<rs::Type, RenderMode::Type> renderModePairs[] {
+    // same as our reset value: RENDER_MODE_PAIR(NONE),
+    RENDER_MODE_PAIR(AUTO),
+    RENDER_MODE_PAIR(COLOR),
+    RENDER_MODE_PAIR(STENCIL),
+    RENDER_MODE_PAIR(COLOR_STENCIL),
+  };
+#undef RENDER_MODE_PAIR
+  for (auto& p: renderModePairs)
+  {
+    HELP_TEST_RENDERER_STATE(Renderer::Property::RENDER_MODE, RenderMode::NONE, p.first, p.second, renderer);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliRendererStateParseEmpty(void)
+{
+  std::string error;
+  auto onError = [&](const std::string& e) { error = e; };
+
+  DALI_TEST_EQUAL(rs::Parse("", 0, onError), uint32_t(rs::NONE));
+  DALI_TEST_CHECK(error.empty());
+  END_TEST;
+}
+
+int UtcDaliRendererStateParseInvalid(void)
+{
+  std::string error;
+  auto onError = [&](const std::string& e) { error = e; };
+
+  DALI_TEST_EQUAL(rs::Parse("definitelyNotAValidRendererState", 0, onError), uint32_t(rs::NONE));
+  DALI_TEST_CHECK(strstr(error.c_str(), "Not a valid RendererState") != nullptr);
+  END_TEST;
+}
+
+namespace
+{
+struct Option
+{
+  std::string_view name;
+  rs::Type expected;
+
+  void Apply(std::ostream& stream, uint8_t shift, uint32_t& result) const
+  {
+    stream << name;
+    result |= expected << shift;
+  }
+};
+
+struct StateGenerator
+{
+  std::string_view name;
+
+  uint8_t shift;
+  std::vector<Option> permutations;
+};
+
+#define STRING_STATE_PAIR(x, y) { #x, y::x }
+const decltype(StateGenerator::permutations) BLEND_FACTORS {
+  { "", sl::BlendFactor::OMIT },
+  STRING_STATE_PAIR(ZERO, sl::BlendFactor),
+  STRING_STATE_PAIR(ONE, sl::BlendFactor),
+  STRING_STATE_PAIR(SRC_COLOR, sl::BlendFactor),
+  STRING_STATE_PAIR(ONE_MINUS_SRC_COLOR, sl::BlendFactor),
+  STRING_STATE_PAIR(SRC_ALPHA, sl::BlendFactor),
+  STRING_STATE_PAIR(ONE_MINUS_SRC_ALPHA, sl::BlendFactor),
+  STRING_STATE_PAIR(DST_COLOR, sl::BlendFactor),
+  STRING_STATE_PAIR(ONE_MINUS_DST_COLOR, sl::BlendFactor),
+  STRING_STATE_PAIR(DST_ALPHA, sl::BlendFactor),
+  STRING_STATE_PAIR(ONE_MINUS_DST_ALPHA, sl::BlendFactor),
+  STRING_STATE_PAIR(SRC_ALPHA_SATURATE, sl::BlendFactor),
+  STRING_STATE_PAIR(CONSTANT_COLOR, sl::BlendFactor),
+  STRING_STATE_PAIR(ONE_MINUS_CONSTANT_COLOR, sl::BlendFactor),
+  STRING_STATE_PAIR(CONSTANT_ALPHA, sl::BlendFactor),
+  STRING_STATE_PAIR(ONE_MINUS_CONSTANT_ALPHA, sl::BlendFactor),
+};
+
+const StateGenerator PERMUTATORS[] {
+  { "DEPTH_FUNC:", rs::DEPTH_FUNCTION_SHIFT, {
+    { "", Comparison::OMIT },
+    STRING_STATE_PAIR(NEVER, Comparison),
+    STRING_STATE_PAIR(ALWAYS, Comparison),
+    STRING_STATE_PAIR(LESS, Comparison),
+    STRING_STATE_PAIR(GREATER, Comparison),
+    STRING_STATE_PAIR(EQUAL, Comparison),
+    STRING_STATE_PAIR(NOT_EQUAL, Comparison),
+    STRING_STATE_PAIR(LESS_EQUAL, Comparison),
+    STRING_STATE_PAIR(GREATER_EQUAL, Comparison),
+  } },
+  { "BLEND_SRC_RGB:", rs::BLEND_FACTOR_BASE_SHIFT, BLEND_FACTORS },
+  { "BLEND_DST_RGB:", rs::BLEND_FACTOR_BASE_SHIFT + rs::BLEND_FACTOR_ITEM_BITS, BLEND_FACTORS  },
+  { "BLEND_SRC_ALPHA:", rs::BLEND_FACTOR_BASE_SHIFT + rs::BLEND_FACTOR_ITEM_BITS * 2, BLEND_FACTORS },
+  { "BLEND_DST_ALPHA:", rs::BLEND_FACTOR_BASE_SHIFT + rs::BLEND_FACTOR_ITEM_BITS * 3, BLEND_FACTORS },
+  { "BUFFER_MODE:", rs::BUFFER_MODE_SHIFT, {
+    { "", BufferMode::OMIT },
+    STRING_STATE_PAIR(NONE, BufferMode),
+    STRING_STATE_PAIR(AUTO, BufferMode),
+    STRING_STATE_PAIR(COLOR, BufferMode),
+    STRING_STATE_PAIR(STENCIL, BufferMode),
+    STRING_STATE_PAIR(COLOR_STENCIL, BufferMode),
+  } },
+  // binary options
+  { "", 0, { STRING_STATE_PAIR(DEPTH_WRITE, rs) } },
+  { "", 0, { STRING_STATE_PAIR(DEPTH_TEST, rs) } },
+  { "", 0, { STRING_STATE_PAIR(CULL_FRONT, rs) } },
+  { "", 0, { STRING_STATE_PAIR(CULL_BACK, rs) } },
+  { "", 0, { STRING_STATE_PAIR(ALPHA_BLEND, rs) } },
+};
+
+#undef STRING_STATE_PAIR
+}
+
+int UtcDaliRendererStateParseIndividual(void)
+{
+  std::string error;
+  auto onError = [&](const std::string& e) { error = e; };
+
+  char buffer[512] {};
+  for (auto& p: PERMUTATORS)
+  {
+    for (auto& o: p.permutations)
+    {
+      StreamBuffer streamBuf(buffer, sizeof(buffer));
+      std::ostream stream(&streamBuf);
+
+      stream << p.name;
+
+      uint32_t expected = 0x0;
+      o.Apply(stream, p.shift, expected);
+      stream << '\0';
+
+      printf("%s -> %d\n", buffer, expected);
+      DALI_TEST_EQUAL(rs::Parse(buffer, 0, onError), expected);
+      DALI_TEST_CHECK(error.empty());
+    }
+  }
+
+  END_TEST;
+}
+
+int UtcDaliRendererStateParseCombined(void)
+{
+  constexpr uint32_t count = std::extent<decltype(PERMUTATORS)>::value;
+  constexpr uint32_t prime = 13;
+  static_assert(prime > count);
+  constexpr uint32_t skip = 3 * count * count + 7 * count + 1;
+  static_assert(skip % prime != 0);
+
+  std::string error;
+  auto onError = [&](const std::string& e) { error = e; };
+
+  std::vector<rs::Type> expectedValues;
+
+  char buffer[512] {};
+  for (uint32_t i = 0; i < count; ++i)
+  {
+    StreamBuffer streamBuf(buffer, sizeof(buffer));
+    std::ostream stream(&streamBuf);
+
+    uint32_t expected = 0x0;
+
+    uint32_t iTmp = i;
+    for (uint32_t j = 0; j < count; ++j)
+    {
+      iTmp = (iTmp + skip) % count;
+      DALI_TEST_CHECK(iTmp < count);
+
+      if (expected)
+      {
+        stream << "|";
+      }
+
+      auto& perm = PERMUTATORS[iTmp];
+      stream << perm.name;
+      perm.permutations.back().Apply(stream, perm.shift, expected);
+    }
+
+    stream << '\0';
+
+    DALI_TEST_EQUAL(rs::Parse(buffer, 0, onError), expected);
+    DALI_TEST_CHECK(error.empty());
+
+    auto iFind = std::lower_bound(expectedValues.begin(), expectedValues.end(), expected);
+    if (iFind == expectedValues.end() || *iFind != expected)
+    {
+      expectedValues.insert(iFind, expected);
+    }
+    DALI_TEST_EQUAL(expectedValues.size(), 1u);
+  }
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-ResourceBundle.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-ResourceBundle.cpp
new file mode 100644 (file)
index 0000000..8f03828
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/resource-bundle.h"
+#include "dali-scene-loader/public-api/utils.h"
+#include <dali-test-suite-utils.h>
+#include <string_view>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+int UtcDaliResourceRefCounts(void)
+{
+  ResourceBundle resourceBundle;
+  resourceBundle.mEnvironmentMaps.resize(4);
+  resourceBundle.mShaders.resize(13);
+  resourceBundle.mMeshes.resize(17);
+  resourceBundle.mMaterials.resize(19);
+
+  int i = 0;
+  std::vector<int> testEnvironmentReferences(resourceBundle.mEnvironmentMaps.size());
+  for (auto& m : resourceBundle.mMaterials)
+  {
+    Index iEnv = 0;
+    iEnv += (i % 3) == 0;
+    iEnv += ((i % 4) == 0) * 2;
+    m.first.mEnvironmentIdx = iEnv;
+
+    printf("%d : %d, ", i, iEnv);
+
+    ++testEnvironmentReferences[iEnv];
+    ++i;
+  }
+
+  auto counter = resourceBundle.CreateRefCounter();
+  DALI_TEST_EQUAL(counter[ResourceType::Environment].Size(), resourceBundle.mEnvironmentMaps.size());
+  DALI_TEST_EQUAL(counter[ResourceType::Shader].Size(), resourceBundle.mShaders.size());
+  DALI_TEST_EQUAL(counter[ResourceType::Mesh].Size(), resourceBundle.mMeshes.size());
+  DALI_TEST_EQUAL(counter[ResourceType::Material].Size(), resourceBundle.mMaterials.size());
+
+  std::fill(counter[ResourceType::Material].begin(), counter[ResourceType::Material].end(), 1u);
+  resourceBundle.CountEnvironmentReferences(counter);
+  i = 0;
+  for (auto& er: counter[ResourceType::Environment])
+  {
+    DALI_TEST_EQUAL(er, testEnvironmentReferences[i]);
+    ++i;
+  }
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-SceneDefinition.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-SceneDefinition.cpp
new file mode 100644 (file)
index 0000000..9b163bd
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/scene-definition.h"
+#include "dali-scene-loader/public-api/utils.h"
+#include <dali-test-suite-utils.h>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+int UtcDaliSceneDefinitionAddNode(void)
+{
+  SceneDefinition sceneDef;
+
+  DALI_TEST_EQUAL(sceneDef.GetNodeCount(), 0u);
+  auto node = new NodeDefinition();
+  node->mName = "First";
+
+  auto result = sceneDef.AddNode(std::unique_ptr<NodeDefinition>{ node });
+  DALI_TEST_EQUAL(result, node);
+  DALI_TEST_EQUAL(sceneDef.GetNodeCount(), 1u);
+  DALI_TEST_EQUAL(sceneDef.GetNode(0), node);
+  DALI_TEST_EQUAL(sceneDef.FindNode(node->mName), node);
+
+  auto node2 = new NodeDefinition();
+  node2->mName = node->mName;
+  result = sceneDef.AddNode(std::unique_ptr<NodeDefinition>{ node2 });
+  DALI_TEST_EQUAL(result, static_cast<NodeDefinition*>(nullptr)); // failed
+  DALI_TEST_EQUAL(sceneDef.GetNodeCount(), 1u); // still
+  DALI_TEST_EQUAL(sceneDef.GetNode(0), node); // still
+  DALI_TEST_EQUAL(sceneDef.FindNode(node->mName), node); // still
+
+  auto child = new NodeDefinition();
+  child->mName = "Second";
+  child->mParentIdx = 0;
+
+  DALI_TEST_CHECK(node->mChildren.empty()); // these are hooked up by AddNode, base on parent idx.
+
+  result = sceneDef.AddNode(std::unique_ptr<NodeDefinition>{ child });
+  DALI_TEST_EQUAL(result, child);
+  DALI_TEST_EQUAL(sceneDef.GetNodeCount(), 2u);
+  DALI_TEST_EQUAL(sceneDef.GetNode(1), child);
+  DALI_TEST_EQUAL(sceneDef.FindNode(child->mName), child);
+
+  DALI_TEST_EQUAL(node->mChildren[0], 1u); // these are hooked up by AddNode, base on parent idx.
+
+  END_TEST;
+}
+
+int UtcDaliSceneDefinitionAddRootsFail(void)
+{
+  SceneDefinition sceneDef;
+
+  DALI_TEST_ASSERTION(sceneDef.AddRootNode(0), "index out of bounds");
+  DALI_TEST_CHECK(sceneDef.GetRoots().empty());
+  DALI_TEST_EQUAL(sceneDef.GetNodeCount(), 0u);
+
+  END_TEST;
+}
+
+namespace
+{
+
+struct TestContext
+{
+  SceneDefinition sceneDef;
+  NodeDefinition* root;
+  NodeDefinition* childA;
+  NodeDefinition* childB;
+
+  TestContext()
+  : sceneDef{},
+    root{ new NodeDefinition{ "Root" }},
+    childA{ new NodeDefinition{ "A" }},
+    childB{ new NodeDefinition{ "B" }}
+  {
+    childA->mParentIdx = 0;
+    childB->mParentIdx = 0;
+
+    root = sceneDef.AddNode(std::unique_ptr<NodeDefinition>{ root });
+    childA = sceneDef.AddNode(std::unique_ptr<NodeDefinition>{ childA });
+    childB = sceneDef.AddNode(std::unique_ptr<NodeDefinition>{ childB });
+  }
+};
+
+enum Event
+{
+  DEFAULT = -1,
+  START,
+  FINISH
+};
+
+struct NodeVisitor : NodeDefinition::IVisitor
+{
+  struct Visit
+  {
+    Event event;
+    NodeDefinition* node;
+
+    bool operator==(const Visit& other) const
+    {
+      return event == other.event && node == other.node;
+    }
+  };
+
+  void Start(NodeDefinition& n) override
+  {
+    visits.push_back({ START, &n });
+  }
+
+  void Finish(NodeDefinition& n) override
+  {
+    visits.push_back({ FINISH, &n });
+  }
+
+  std::vector<Visit> visits;
+};
+
+struct ConstNodeVisitor : NodeDefinition::IConstVisitor
+{
+  struct Visit
+  {
+    Event  event;
+    const NodeDefinition* node;
+
+    bool operator==(const Visit& other) const
+    {
+      return event == other.event && node == other.node;
+    }
+  };
+
+  void Start(const NodeDefinition& n) override
+  {
+    visits.push_back({ START, &n });
+  }
+
+  void Finish(const NodeDefinition& n) override
+  {
+    visits.push_back({ FINISH, &n });
+  }
+
+  std::vector<Visit> visits;
+};
+
+}
+
+int UtcDaliSceneDefinitionAddRemoveRootNode(void)
+{
+  TestContext ctx;
+
+  DALI_TEST_EQUAL(ctx.sceneDef.AddRootNode(0), 0);
+  DALI_TEST_EQUAL(ctx.sceneDef.GetRoots().size(), 1u);
+  DALI_TEST_EQUAL(ctx.sceneDef.GetRoots()[0], 0);
+
+  ctx.sceneDef.RemoveRootNode(0);
+  DALI_TEST_EQUAL(ctx.sceneDef.GetRoots().size(), 0);
+
+  DALI_TEST_EQUAL(ctx.sceneDef.GetNodeCount(), 3u);
+
+  END_TEST;
+}
+
+int UtcDaliSceneDefinitionVisit(void)
+{
+  TestContext ctx;
+
+  NodeVisitor visitor;
+  ctx.sceneDef.Visit(0, Customization::Choices{}, visitor);
+
+  const NodeVisitor::Visit expected[] {
+    { START, ctx.root },
+    { START, ctx.childA },
+    { FINISH, ctx.childA },
+    { START, ctx.childB },
+    { FINISH, ctx.childB },
+    { FINISH, ctx.root },
+  };
+  DALI_TEST_CHECK(std::equal(visitor.visits.begin(), visitor.visits.end(), expected));
+
+  END_TEST;
+};
+
+int UtcDaliSceneDefinitionConstVisit(void)
+{
+  TestContext ctx;
+
+  ConstNodeVisitor visitor;
+  ctx.sceneDef.Visit(0, Customization::Choices{}, visitor);
+
+  const ConstNodeVisitor::Visit expected[] {
+    { START, ctx.root },
+    { START, ctx.childA },
+    { FINISH, ctx.childA },
+    { START, ctx.childB },
+    { FINISH, ctx.childB },
+    { FINISH, ctx.root },
+  };
+  DALI_TEST_CHECK(std::equal(visitor.visits.begin(), visitor.visits.end(), expected));
+
+  END_TEST;
+};
+
+int UtcDaliSceneDefinitionVisitCustomized(void)
+{
+  TestContext ctx;
+
+  ctx.root->mCustomization.reset(new NodeDefinition::CustomizationDefinition{ "A/B" });
+
+  const NodeVisitor::Visit expected[] {
+    { START, ctx.root },
+    { START, ctx.childB },
+    { FINISH, ctx.childB },
+    { FINISH, ctx.root },
+  };
+
+  Customization::Choices choices;
+  for (auto i : { 1, 2 })
+  {
+    choices.Set("A/B", i);
+
+    NodeVisitor visitor;
+    ctx.sceneDef.Visit(0, choices, visitor);
+
+    DALI_TEST_CHECK(std::equal(visitor.visits.begin(), visitor.visits.end(), std::begin(expected)));
+  }
+
+  END_TEST;
+};
+
+int UtcDaliSceneDefinitionConstVisitCustomized(void)
+{
+  TestContext ctx;
+
+  ctx.root->mCustomization.reset(new NodeDefinition::CustomizationDefinition{ "A/B" });
+
+  const ConstNodeVisitor::Visit expected[] {
+    { START, ctx.root },
+    { START, ctx.childB },
+    { FINISH, ctx.childB },
+    { FINISH, ctx.root },
+  };
+
+  Customization::Choices choices;
+  for (auto i : { 1, 2 })
+  {
+    choices.Set("A/B", i);
+
+    ConstNodeVisitor visitor;
+    ctx.sceneDef.Visit(0, choices, visitor);
+
+    DALI_TEST_CHECK(std::equal(visitor.visits.begin(), visitor.visits.end(), std::begin(expected)));
+  }
+
+  END_TEST;
+};
+
+int UtcDaliSceneDefinitionGetCustomizationOptions(void)
+{
+  TestContext ctx;
+
+  ctx.sceneDef.AddRootNode(0); // GetCustomizationOptions requires this.
+
+  ctx.root->mCustomization.reset(new NodeDefinition::CustomizationDefinition{ "A/B" });
+  ctx.childA->mCustomization.reset(new NodeDefinition::CustomizationDefinition{ "hello" });
+  ctx.childB->mCustomization.reset(new NodeDefinition::CustomizationDefinition{ "goodbye" });
+
+  Customization::Choices choices;
+  Customization::Map options;
+  ctx.sceneDef.GetCustomizationOptions(choices, options, &choices);
+
+  DALI_TEST_EQUAL(choices.Size(), 2u);
+  DALI_TEST_EQUAL(options.Size(), 2u);
+
+  struct TestOption
+  {
+    std::string name;
+    Customization customization;
+    Customization::OptionType choice;
+  };
+
+  std::vector<TestOption> testOptions {
+    { "A/B", { 2, { "Root" } }, 0 },
+    { "hello", { 0, { "A" } }, 0 },
+  };
+  for (auto& testOption: testOptions)
+  {
+    auto iFind = choices.Get(testOption.name);
+    DALI_TEST_EQUAL(iFind, testOption.choice);
+
+    auto iFindOption = options.Get(testOption.name);
+    DALI_TEST_CHECK(iFindOption != nullptr);
+    DALI_TEST_EQUAL(iFindOption->numOptions, testOption.customization.numOptions);
+    DALI_TEST_EQUAL(iFindOption->nodes.size(), testOption.customization.nodes.size());
+    DALI_TEST_CHECK(std::equal(iFindOption->nodes.begin(), iFindOption->nodes.end(),
+      testOption.customization.nodes.begin()));
+  }
+
+  choices.Clear();
+  choices.Set("A/B", 1);
+  *options.Get("A/B") = {};
+
+  testOptions[0].choice = 1;
+  testOptions[1].name = "goodbye";
+  testOptions[1].customization.nodes[0] = "B";
+
+  ctx.sceneDef.GetCustomizationOptions(choices, options, &choices);
+
+  DALI_TEST_EQUAL(choices.Size(), 2u);
+  DALI_TEST_EQUAL(options.Size(), 3u);
+
+  for (auto& testOption: testOptions)
+  {
+    auto iFind = choices.Get(testOption.name);
+    DALI_TEST_EQUAL(iFind, testOption.choice);
+
+    auto iFindOption = options.Get(testOption.name);
+    DALI_TEST_CHECK(iFindOption != nullptr);
+    DALI_TEST_EQUAL(iFindOption->numOptions, testOption.customization.numOptions);
+    DALI_TEST_EQUAL(iFindOption->nodes.size(), testOption.customization.nodes.size());
+    DALI_TEST_CHECK(std::equal(iFindOption->nodes.begin(), iFindOption->nodes.end(),
+      testOption.customization.nodes.begin()));
+  }
+
+  END_TEST;
+}
+
+int UtcDaliSceneDefinitionFindNode(void)
+{
+  TestContext ctx;
+
+  Index result = INVALID_INDEX;
+  for (auto n: { ctx.root, ctx.childA, ctx.childB })
+  {
+    ctx.sceneDef.FindNode(n->mName, &result);
+    DALI_TEST_CHECK(result != INVALID_INDEX);
+    DALI_TEST_EQUAL(ctx.sceneDef.GetNode(result), n);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliSceneDefinitionConstFindNode(void)
+{
+  TestContext ctx;
+
+  Index result = INVALID_INDEX;
+  for (auto n: { ctx.root, ctx.childA, ctx.childB })
+  {
+    const_cast<const TestContext&>(ctx).sceneDef.FindNode(n->mName, &result);
+    DALI_TEST_CHECK(result != INVALID_INDEX);
+    DALI_TEST_EQUAL(ctx.sceneDef.GetNode(result), n);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliSceneDefinitionFindNodeIndex(void)
+{
+  TestContext ctx;
+
+  Index result = INVALID_INDEX;
+  for (auto n: { ctx.root, ctx.childA, ctx.childB })
+  {
+    result = ctx.sceneDef.FindNodeIndex(*n);
+    DALI_TEST_CHECK(result != INVALID_INDEX);
+    DALI_TEST_EQUAL(ctx.sceneDef.GetNode(result), n);
+  }
+
+  END_TEST;
+}
+
+int UtcDaliSceneDefinitionFindNodes(void)
+{
+  TestContext ctx;
+
+  std::vector<NodeDefinition*> nodes;
+  auto nodeConsumer = [&nodes](NodeDefinition& nd) {
+    nodes.push_back(&nd);
+  };
+
+  auto nodePredicate = [](const NodeDefinition& nd) {
+    return nd.mName.length() == 1;
+  };
+
+  ctx.sceneDef.FindNodes(nodePredicate, nodeConsumer, 1);
+  DALI_TEST_EQUAL(nodes.size(), 1);
+  DALI_TEST_EQUAL(nodes[0]->mName, "A");
+  DALI_TEST_EQUAL(nodes[0], ctx.childA);
+
+  nodes.clear();
+  ctx.sceneDef.FindNodes(nodePredicate, nodeConsumer);
+
+  DALI_TEST_EQUAL(nodes.size(), 2);
+  DALI_TEST_EQUAL(nodes[0]->mName, "A");
+  DALI_TEST_EQUAL(nodes[0], ctx.childA);
+  DALI_TEST_EQUAL(nodes[1]->mName, "B");
+  DALI_TEST_EQUAL(nodes[1], ctx.childB);
+
+  END_TEST;
+}
+
+int UtcDaliSceneDefinitionRemoveNode(void)
+{
+  TestContext ctx;
+  DALI_TEST_EQUAL(ctx.sceneDef.RemoveNode("doesn't exist"), false);
+
+  // pre-removing A
+  DALI_TEST_EQUAL(ctx.sceneDef.GetNodeCount(), 3u);
+  DALI_TEST_EQUAL(ctx.root->mChildren.size(), 2u);
+
+  Index result;
+  DALI_TEST_EQUAL(ctx.sceneDef.FindNode("B", &result), ctx.childB);
+  DALI_TEST_EQUAL(result, 2);
+
+  DALI_TEST_EQUAL(ctx.sceneDef.RemoveNode("A"), true);
+
+  // post-removing A
+  DALI_TEST_EQUAL(ctx.sceneDef.GetNodeCount(), 2u);
+
+  result = 12345;
+  DALI_TEST_EQUAL(ctx.sceneDef.FindNode("A", &result), static_cast<NodeDefinition*>(nullptr));
+  DALI_TEST_EQUAL(result, 12345); // doesn't change
+
+  DALI_TEST_EQUAL(ctx.sceneDef.FindNode("B", &result), ctx.childB);
+  DALI_TEST_EQUAL(result, 1); // dropped
+
+  DALI_TEST_EQUAL(ctx.root->mChildren.size(), 1u);
+  DALI_TEST_EQUAL(ctx.root->mChildren[0], 1u);
+
+  // removing root
+  DALI_TEST_EQUAL(ctx.sceneDef.RemoveNode("Root"), true);
+  DALI_TEST_EQUAL(ctx.sceneDef.GetNodeCount(), 0);
+
+  END_TEST;
+}
+
+int UtcDaliSceneDefinitionReparentNode(void)
+{
+  TestContext ctx;
+
+  ctx.sceneDef.ReparentNode("B", "A", 0);
+
+  DALI_TEST_EQUAL(ctx.childB->mParentIdx, ctx.sceneDef.FindNodeIndex(*ctx.childA));
+  DALI_TEST_EQUAL(ctx.childA->mChildren.size(), 1u);
+  DALI_TEST_EQUAL(ctx.childA->mChildren[0], ctx.sceneDef.FindNodeIndex(*ctx.childB));
+
+  END_TEST;
+}
+
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-ShaderDefinition.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-ShaderDefinition.cpp
new file mode 100644 (file)
index 0000000..e58e702
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/shader-definition.h"
+#include <dali-test-suite-utils.h>
+#include <string_view>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+int UtcDaliShaderDefinitionFailedToLoad(void)
+{
+  ShaderDefinition shaderDef;
+  shaderDef.mVertexShaderPath = "nonexistent.vsh";
+  shaderDef.mFragmentShaderPath = "nonexistent.vsh";
+
+  DALI_TEST_ASSERTION(shaderDef.LoadRaw(""), "Failed to load shader source");
+
+  shaderDef.mVertexShaderPath = "dli_pbr.vsh";
+
+  auto shaderPath = TEST_RESOURCE_DIR "/";
+  DALI_TEST_ASSERTION(shaderDef.LoadRaw(shaderPath), "Failed to load shader source");
+
+  shaderDef.mFragmentShaderPath = "dli_pbr.fsh";
+
+  shaderDef.mHints.push_back("MODIFIES_GEOMETRY");
+  shaderDef.mHints.push_back("OUTPUT_IS_TRANSPARENT");
+  auto raw = shaderDef.LoadRaw(shaderPath);
+
+  TestApplication app;
+  auto shader = shaderDef.Load(std::move(raw));
+  DALI_TEST_CHECK(shader);
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-ShaderDefinitionFactory.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-ShaderDefinitionFactory.cpp
new file mode 100644 (file)
index 0000000..c1b5481
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/gltf2-loader.h"
+#include "dali-scene-loader/public-api/shader-definition-factory.h"
+#include "dali-scene-loader/public-api/node-definition.h"
+#include "dali-scene-loader/public-api/resource-bundle.h"
+#include <dali-test-suite-utils.h>
+#include <string_view>
+#include <set>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+namespace
+{
+
+bool EndsWith(const std::string& str, const std::string& suffix) // ends_width() is C++20
+{
+  return str.size() >= suffix.size() && str.substr(str.size() - suffix.size()).compare(suffix) == 0;
+}
+
+MaterialDefinition& NewMaterialDefinition(ResourceBundle& resources)
+{
+  resources.mMaterials.push_back({});
+  return resources.mMaterials.back().first;
+}
+
+MeshDefinition& NewMeshDefinition(ResourceBundle& resources)
+{
+  resources.mMeshes.push_back({});
+  return resources.mMeshes.back().first;
+}
+
+void ClearMeshesAndMaterials(ResourceBundle& resources)
+{
+  resources.mMaterials.clear();
+  resources.mMeshes.clear();
+}
+
+struct Context
+{
+  ResourceBundle resources;
+  ShaderDefinitionFactory factory;
+
+  Context()
+  {
+    factory.SetResources(resources);
+  }
+};
+
+struct ShaderParameters
+{
+  MeshDefinition& meshDef;
+  MaterialDefinition& materialDef;
+  NodeDefinition& nodeDef;
+};
+
+struct Permutation
+{
+  using ConfigureFn = void(*)(ShaderParameters&);
+
+  ConfigureFn configureFn;
+
+  std::set<std::string> defines;
+  RendererState::Type rendererStateSet = 0;
+  RendererState::Type rendererStateClear = 0;
+};
+
+struct PermutationSet
+{
+  std::vector<const Permutation*> permutations;
+  Index shaderIdx;
+};
+
+}
+
+int UtcDaliShaderDefinitionFactoryProduceShaderInvalid(void)
+{
+  Context ctx;
+
+  NodeDefinition nodeDef;
+  nodeDef.mRenderable.reset(new NodeDefinition::Renderable());
+
+  DALI_TEST_EQUAL(INVALID_INDEX, ctx.factory.ProduceShader(nodeDef));
+  DALI_TEST_CHECK(ctx.resources.mShaders.empty());
+
+  END_TEST;
+}
+
+int UtcDaliShaderDefinitionFactoryProduceShader(void)
+{
+  Context ctx;
+  ctx.resources.mMaterials.push_back({});
+  ctx.resources.mMeshes.push_back({});
+
+  Permutation permutations[]{
+    {
+      [](ShaderParameters& p) {},
+      {},
+      RendererState::DEPTH_TEST | RendererState::DEPTH_WRITE | RendererState::CULL_BACK,
+    },
+    {
+      [](ShaderParameters& p) {
+        p.materialDef.mFlags |= MaterialDefinition::TRANSPARENCY;
+      },
+      { "THREE_TEX" },
+      RendererState::ALPHA_BLEND,
+      RendererState::DEPTH_WRITE,
+    },
+    {
+      [](ShaderParameters& p) {
+        p.materialDef.mTextureStages.push_back({ MaterialDefinition::ALBEDO, {} });
+      },
+      { "THREE_TEX" }
+    },
+    {
+      [](ShaderParameters& p) {
+        p.materialDef.mTextureStages.push_back({ MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS, {} });
+      },
+      { "THREE_TEX" }
+    },
+    {
+      [](ShaderParameters& p) {
+        p.materialDef.mTextureStages.push_back({ MaterialDefinition::NORMAL, {} });
+      },
+      { "THREE_TEX" }
+    },
+    {
+      [](ShaderParameters& p) {
+        p.materialDef.mFlags |= MaterialDefinition::SUBSURFACE;
+      },
+      { "SSS" }
+    },
+    {
+      [](ShaderParameters& p) {
+        p.materialDef.SetAlphaCutoff(.5f);
+      },
+      { "ALPHA_TEST" }
+    },
+    {
+      [](ShaderParameters& p) {
+        p.materialDef.SetAlphaCutoff(1.f);
+      },
+      { "ALPHA_TEST" }
+    },
+    {
+      [](ShaderParameters& p) {
+        p.materialDef.mFlags |= MaterialDefinition::GLTF_CHANNELS;
+      },
+      { "GLTF_CHANNELS" }
+    },
+    {
+      [](ShaderParameters& p) {
+        p.meshDef.mJoints0.mBlob.mOffset = 0;
+        p.meshDef.mWeights0.mBlob.mOffset = 0;
+      },
+      { "SKINNING" }
+    },
+    {
+      [](ShaderParameters& p) {
+        p.meshDef.mFlags |= MeshDefinition::FLIP_UVS_VERTICAL;
+      },
+      { "FLIP_V" }
+    },
+    {
+      [](ShaderParameters& p) {
+        p.meshDef.mBlendShapes.push_back({});
+      },
+    },
+    {
+      [](ShaderParameters& p) {
+        p.meshDef.mBlendShapes.back().deltas.mBlob.mOffset = 0;
+      },
+      { "MORPH_POSITION", "MORPH" }
+    },
+    {
+      [](ShaderParameters& p) {
+        p.meshDef.mBlendShapes.back().normals.mBlob.mOffset = 0;
+      },
+      { "MORPH_NORMAL", "MORPH" }
+    },
+    {
+      [](ShaderParameters& p) {
+        p.meshDef.mBlendShapes.back().tangents.mBlob.mOffset = 0;
+      },
+      { "MORPH_TANGENT", "MORPH" }
+    },
+    {
+      [](ShaderParameters& p) {
+        auto& blendShapes = p.meshDef.mBlendShapes;
+        DALI_ASSERT_ALWAYS(!blendShapes.empty() &&
+          (blendShapes.back().deltas.mBlob.mOffset != MeshDefinition::INVALID ||
+          blendShapes.back().normals.mBlob.mOffset != MeshDefinition::INVALID ||
+          blendShapes.back().tangents.mBlob.mOffset != MeshDefinition::INVALID));
+        p.meshDef.mBlendShapeVersion = BlendShapes::Version::VERSION_2_0;
+      },
+      { "MORPH_VERSION_2_0" }
+    },
+  };
+
+  PermutationSet permSets[] {
+    // default
+    { { &permutations[0] }, 0 },
+
+    // alpha
+    { { &permutations[0], &permutations[1] }, 1 },
+
+    // three-texture setups
+    { { &permutations[0], &permutations[2] }, 2 },
+    { { &permutations[0], &permutations[3] }, 2 },
+    { { &permutations[0], &permutations[4] }, 2 },
+    { { &permutations[0], &permutations[2], &permutations[3] }, 2 },
+    { { &permutations[0], &permutations[3], &permutations[4] }, 2 },
+    { { &permutations[0], &permutations[4], &permutations[2] }, 2 },
+    { { &permutations[0], &permutations[2], &permutations[3], &permutations[4] }, 2 },
+
+    // subsurface scattering
+    { { &permutations[0], &permutations[5] }, 3 },
+
+    // alpha test
+    { { &permutations[0], &permutations[6] }, 4 },
+    { { &permutations[0], &permutations[7] }, 4 },
+
+    // glTF channels
+    { { &permutations[0], &permutations[8] }, 5 },
+
+    // skinning
+    { { &permutations[0], &permutations[9] }, 6 },
+
+    // flip uvs
+    { { &permutations[0], &permutations[10] }, 7 },
+
+    // morphing
+    { { &permutations[0], &permutations[11], &permutations[12] }, 8 },
+    { { &permutations[0], &permutations[11], &permutations[13] }, 9 },
+    { { &permutations[0], &permutations[11], &permutations[14] }, 10 },
+    { { &permutations[0], &permutations[11], &permutations[12], &permutations[13] }, 11 },
+    { { &permutations[0], &permutations[11], &permutations[13], &permutations[14] }, 12 },
+    { { &permutations[0], &permutations[11], &permutations[14], &permutations[12] }, 13 },
+    { { &permutations[0], &permutations[11], &permutations[12], &permutations[13], &permutations[14] }, 14 },
+
+    { { &permutations[0], &permutations[11], &permutations[12], &permutations[15] }, 15 },
+    { { &permutations[0], &permutations[11], &permutations[13], &permutations[15] }, 16 },
+    { { &permutations[0], &permutations[11], &permutations[14], &permutations[15] }, 17 },
+    { { &permutations[0], &permutations[11], &permutations[12], &permutations[13], &permutations[15] }, 18 },
+    { { &permutations[0], &permutations[11], &permutations[13], &permutations[14], &permutations[15] }, 19 },
+    { { &permutations[0], &permutations[11], &permutations[14], &permutations[12], &permutations[15] }, 20 },
+    { { &permutations[0], &permutations[11], &permutations[12], &permutations[13], &permutations[14], &permutations[15] }, 21 },
+
+    // etc.
+    { { &permutations[0], &permutations[1], &permutations[2] }, 1 },
+    { { &permutations[0], &permutations[1], &permutations[3] }, 1 },
+    { { &permutations[0], &permutations[1], &permutations[2], &permutations[3] }, 1 },
+  };
+  for(auto& ps: permSets)
+  {
+    printf("%ld\n", &ps - permSets);
+
+    auto modelNode = new ModelNode();
+    modelNode->mMeshIdx = 0;
+    modelNode->mMaterialIdx = 0;
+
+    NodeDefinition nodeDef;
+    nodeDef.mRenderable.reset(modelNode);
+
+    auto& meshDef = NewMeshDefinition(ctx.resources);
+    auto& materialDef = NewMaterialDefinition(ctx.resources);
+    ShaderParameters sp{ meshDef, materialDef, nodeDef };
+
+    std::set<std::string> defines;
+    RendererState::Type rendererState = 0;
+    for (auto p : ps.permutations)
+    {
+      p->configureFn(sp);
+      defines.insert(p->defines.begin(), p->defines.end());
+      rendererState = (rendererState | p->rendererStateSet) & ~p->rendererStateClear;
+    }
+
+    auto shaderIdx = ctx.factory.ProduceShader(nodeDef);
+    DALI_TEST_EQUAL(ps.shaderIdx, shaderIdx);
+
+    auto& shaderDef = ctx.resources.mShaders[shaderIdx].first;
+    DALI_TEST_CHECK(EndsWith(shaderDef.mVertexShaderPath, ".vsh"));
+    DALI_TEST_CHECK(EndsWith(shaderDef.mFragmentShaderPath, ".fsh"));
+    DALI_TEST_EQUAL(shaderDef.mRendererState, rendererState);
+
+    uint32_t definesUnmatched = shaderDef.mDefines.size();
+    for (auto& d: shaderDef.mDefines)
+    {
+      auto iFind = defines.find(d);
+      if (iFind != defines.end())
+      {
+        defines.erase(iFind);
+        --definesUnmatched;
+      }
+      else
+      {
+        printf("mismatched: %s\n", d.c_str());
+        break;
+      }
+    }
+
+    DALI_TEST_CHECK(defines.empty());
+    DALI_TEST_EQUAL(0, definesUnmatched);
+
+    printf("defines OK\n");
+
+    auto uMaxLOD = shaderDef.mUniforms["uMaxLOD"];
+    DALI_TEST_EQUAL(uMaxLOD.GetType(), Property::FLOAT);
+
+    auto uCubeMatrix = shaderDef.mUniforms["uCubeMatrix"];
+    DALI_TEST_EQUAL(uCubeMatrix.GetType(), Property::MATRIX);
+
+    ClearMeshesAndMaterials(ctx.resources);
+  }
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-StringCallback.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-StringCallback.cpp
new file mode 100644 (file)
index 0000000..acd5183
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/string-callback.h"
+#include <dali-test-suite-utils.h>
+#include <string_view>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+namespace
+{
+
+char sBuffer[64];
+
+void TestLogFunction(Integration::Log::DebugPriority level, std::string& str)
+{
+  snprintf(sBuffer, sizeof(sBuffer), "%d: %s", level, str.c_str());
+}
+
+}
+
+int UtcDaliUtilsDefaultStringCallback(void)
+{
+  InstallLogFunction(TestLogFunction);
+  DefaultErrorCallback("Hello world!");
+  DALI_TEST_EQUAL(std::string(sBuffer), "2: DefaultErrorCallback Hello world!");
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-Utils.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-Utils.cpp
new file mode 100644 (file)
index 0000000..68e30a0
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/utils.h"
+#include <dali-test-suite-utils.h>
+#include <string_view>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+namespace
+{
+constexpr std::string_view TEST_STRING = "The quick brown fox jumped over a lazy dog.1337true";
+}
+
+template <size_t N>
+void HelpTestStreamBuffer()
+{
+  char buffer[N];
+  StreamBuffer streamBuffer(buffer, N); // note we aren't leaving space for a terminating 0 as we're using strNcmp.
+  std::ostream stream(&streamBuffer);
+
+  stream << "The quick brown fox jumped over a lazy dog.";
+  stream << 1337;
+  stream << std::boolalpha << true;
+
+  const size_t checkLen = std::min(N, TEST_STRING.size());
+  DALI_TEST_EQUAL(0, strncmp(buffer, TEST_STRING.data(), checkLen));
+}
+
+int UtcDaliUtilsStreamBuffer(void)
+{
+  HelpTestStreamBuffer<16>();
+  HelpTestStreamBuffer<32>();
+  HelpTestStreamBuffer<64>();
+  END_TEST;
+}
+
+int UtcDaliUtilsFormatString(void)
+{
+  DALI_TEST_EQUAL(FormatString("%s", "hello"), "hello");
+  DALI_TEST_EQUAL(FormatString("%d", 1667), "1667");
+
+  DALI_TEST_EQUAL(FormatString("%s %d", "hello", 2778), "hello 2778");
+  DALI_TEST_EQUAL(FormatString("%d %s", 3889, "hello"), "3889 hello");
+
+  END_TEST;
+}
diff --git a/automated-tests/src/dali-scene-loader/utc-Dali-ViewProjection.cpp b/automated-tests/src/dali-scene-loader/utc-Dali-ViewProjection.cpp
new file mode 100644 (file)
index 0000000..c4567ab
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// Enable debug log for test coverage
+#define DEBUG_ENABLED 1
+
+#include "dali-scene-loader/public-api/view-projection.h"
+#include <dali-test-suite-utils.h>
+
+using namespace Dali;
+using namespace Dali::SceneLoader;
+
+namespace
+{
+void SetPerspectiveProjection(Matrix& m)
+{
+  auto data = m.AsFloat();
+  float near = 1.f;
+  float far = 10.f;
+  float right = 1.f;
+  float left = -right;
+  float top = .75f;
+  float bottom = -top;
+  data[0] = 2.f * near / (right - left);
+  data[5] = 2.f * near / (top - bottom);
+  data[8] = (right + left) / (right - left);
+  data[9] = (top + bottom) / (top - bottom);
+  data[10] = (far + near) / (far - near);
+  data[11] = -1.f;
+  data[14] = 2 * far * near / (far - near);
+}
+
+}
+
+int UtcDaliViewProjection(void)
+{
+  ViewProjection vp;
+  Matrix viewMatrix;
+  viewMatrix.SetTransformComponents( Vector3::ONE,
+    Quaternion(Radian(Degree(90.f)), Vector3::YAXIS),
+    Vector3::XAXIS * 200.f);
+  vp.GetView() = viewMatrix;
+
+  Matrix projectionMatrix;
+  SetPerspectiveProjection(projectionMatrix);
+  vp.GetProjection() = projectionMatrix;
+
+  vp.Update();
+
+  [&](const ViewProjection& vp) {
+    DALI_TEST_EQUAL(vp.GetView(), viewMatrix);
+    DALI_TEST_EQUAL(vp.GetProjection(), projectionMatrix);
+  }(vp);
+
+  Matrix expectedViewProjection{ false };
+  Matrix::Multiply(expectedViewProjection, viewMatrix, projectionMatrix);
+  auto viewProjectionResult = vp.GetViewProjection();
+  DALI_TEST_EQUAL(viewProjectionResult, expectedViewProjection);
+
+  Matrix expectedInverseProjection{ projectionMatrix };
+  expectedInverseProjection.Invert();
+  auto inverseProjectionResult = vp.GetInverseProjection();
+  DALI_TEST_EQUAL(inverseProjectionResult, expectedInverseProjection);
+
+  END_TEST;
+}
+
+int UtcDaliViewProjectionUpdateFail(void)
+{
+  ViewProjection vp;
+  DALI_TEST_ASSERTION(vp.Update(), "Failed to find inverse");
+
+  END_TEST;
+}
index 6756093..a94f92b 100644 (file)
@@ -76,7 +76,7 @@ static Geometry CreateGeometryMapInternal(const void* opacityMap,
   return Dali::Geometry::New();
 }
 
-static void* NPatchBuildInternal(const Devel::PixelBuffer& pixelBuffer, Toolkit::Internal::NPatchLoader::Data* data )
+static void* NPatchBuildInternal(const Devel::PixelBuffer& pixelBuffer, Toolkit::Internal::NPatchData* data )
 {
   gCallStack.emplace_back( "BuildNPatch" );
   fprintf(stderr, "AddOn::NPatchBuild()\n");
index 90ee00e..3d538cd 100644 (file)
@@ -107,7 +107,66 @@ int UtcDaliAnimatedImageVisualGetPropertyMap01(void)
 int UtcDaliAnimatedImageVisualGetPropertyMap02(void)
 {
   ToolkitTestApplication application;
-  tet_infoline( "UtcDaliAnimatedImageVisualGetPropertyMap for multi image" );
+  tet_infoline( "UtcDaliAnimatedImageVisualGetPropertyMap for multi image with fixed cache" );
+
+  // request AnimatedImageVisual with a property map
+  VisualFactory factory = VisualFactory::Get();
+  Property::Array urls;
+  CopyUrlsIntoArray( urls );
+
+  Visual::Base animatedImageVisual = factory.CreateVisual(
+    Property::Map()
+    .Add( Toolkit::Visual::Property::TYPE, Visual::ANIMATED_IMAGE )
+    .Add( "url", urls )
+    .Add( "batchSize", 4 )
+    .Add( "cacheSize", 20 )
+    .Add( "loopCount", 10 )
+    .Add( "frameDelay", 200 )
+    .Add( "pixelArea", Vector4() )
+    .Add( "wrapModeU", WrapMode::REPEAT )
+    .Add( "wrapModeV", WrapMode::DEFAULT ));
+
+  Property::Map resultMap;
+  animatedImageVisual.CreatePropertyMap( resultMap );
+  // check the property values from the returned map from a visual
+  Property::Value* value = resultMap.Find( Toolkit::Visual::Property::TYPE,  Property::INTEGER );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get<int>() == Visual::ANIMATED_IMAGE );
+
+  value = resultMap.Find( ImageVisual::Property::URL, "url" );
+  DALI_TEST_CHECK( value );
+  Property::Array* resultUrls = value->GetArray();
+  DALI_TEST_CHECK( resultUrls );
+  DALI_TEST_EQUALS( resultUrls->Count(), urls.Count(), TEST_LOCATION );
+
+  value = resultMap.Find( ImageVisual::Property::BATCH_SIZE, "batchSize" );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_EQUALS( value->Get<int>(), 4, TEST_LOCATION );
+
+  value = resultMap.Find( ImageVisual::Property::CACHE_SIZE, "cacheSize" );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_EQUALS( value->Get<int>(), 20, TEST_LOCATION );
+
+  value = resultMap.Find( Toolkit::DevelImageVisual::Property::LOOP_COUNT, "loopCount" );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_EQUALS( value->Get<int>(), 10, TEST_LOCATION );
+
+  value = resultMap.Find( ImageVisual::Property::FRAME_DELAY, "frameDelay" );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_EQUALS( value->Get<int>(), 200, TEST_LOCATION );
+
+  value = resultMap.Find( Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, "totalFrameNumber" );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_EQUALS( value->Get<int>(), 11, TEST_LOCATION );
+
+  END_TEST;
+}
+
+
+int UtcDaliAnimatedImageVisualGetPropertyMap03(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliAnimatedImageVisualGetPropertyMap for multi image rolling cache" );
 
   // request AnimatedImageVisual with a property map
   VisualFactory factory = VisualFactory::Get();
@@ -155,10 +214,14 @@ int UtcDaliAnimatedImageVisualGetPropertyMap02(void)
   DALI_TEST_CHECK( value );
   DALI_TEST_EQUALS( value->Get<int>(), 200, TEST_LOCATION );
 
+  value = resultMap.Find( Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, "totalFrameNumber" );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_EQUALS( value->Get<int>(), 11, TEST_LOCATION );
+
   END_TEST;
 }
 
-int UtcDaliAnimatedImageVisualGetPropertyMap03(void)
+int UtcDaliAnimatedImageVisualGetPropertyMap04(void)
 {
   ToolkitTestApplication application;
   tet_infoline( "UtcDaliAnimatedImageVisualGetPropertyMap" );
@@ -193,6 +256,10 @@ int UtcDaliAnimatedImageVisualGetPropertyMap03(void)
   DALI_TEST_CHECK( value );
   DALI_TEST_CHECK( value->Get<int>() == 2 );
 
+  value = resultMap.Find( Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, "totalFrameNumber" );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_EQUALS( value->Get<int>(), 4, TEST_LOCATION );
+
   END_TEST;
 }
 
index dbc326d..0bde103 100644 (file)
@@ -198,6 +198,52 @@ int UtcDaliArcVisualGetPropertyMap01(void)
   END_TEST;
 }
 
+int UtcDaliArcVisualGetPropertyMap02(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliArcVisualGetPropertyMap02" );
+
+  float thickness = 20.0f;
+  float startAngle = 0.0f, sweepAngle = 90.0f;
+
+  Property::Map propertyMap;
+  propertyMap.Add(Visual::Property::TYPE, DevelVisual::ARC)
+             .Add(Visual::Property::MIX_COLOR, Color::RED)
+             .Add(DevelArcVisual::Property::THICKNESS, thickness )
+             .Add(DevelArcVisual::Property::START_ANGLE, startAngle )
+             .Add(DevelArcVisual::Property::SWEEP_ANGLE, sweepAngle )
+             .Add(DevelArcVisual::Property::CAP, DevelArcVisual::Cap::ROUND );
+
+  Visual::Base visual = VisualFactory::Get().CreateVisual( propertyMap );
+  DALI_TEST_CHECK( visual );
+
+  Property::Map resultMap;
+  visual.CreatePropertyMap( resultMap );
+
+  // check the property values from the returned map from a visual
+  Property::Value* value = resultMap.Find( Visual::Property::MIX_COLOR, Property::VECTOR4 );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_EQUALS( value->Get< Vector4 >(), Color::RED, TEST_LOCATION );
+
+  value = resultMap.Find( DevelArcVisual::Property::THICKNESS, Property::FLOAT );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_EQUALS( value->Get< float >(), thickness, TEST_LOCATION );
+
+  value = resultMap.Find( DevelArcVisual::Property::START_ANGLE, Property::FLOAT );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_EQUALS( value->Get< float >(), startAngle, TEST_LOCATION );
+
+  value = resultMap.Find( DevelArcVisual::Property::SWEEP_ANGLE, Property::FLOAT );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_EQUALS( value->Get< float >(), sweepAngle, TEST_LOCATION );
+
+  value = resultMap.Find( DevelArcVisual::Property::CAP, Property::INTEGER );
+  DALI_TEST_CHECK( value );
+  DALI_TEST_CHECK( value->Get< int >() == DevelArcVisual::Cap::ROUND );
+
+  END_TEST;
+}
+
 int UtcDaliArcVisualUpdateProperty(void)
 {
   ToolkitTestApplication application;
index c135c9f..6f3fa6a 100644 (file)
@@ -3959,6 +3959,8 @@ int UtcDaliVisualGetVisualProperty01(void)
   application.Render();
 
   Vector3 targetColor(1.0f, 1.0f, 1.0f);
+  Vector2 targetOffset(0.05f, 0.05f);
+  Vector2 targetSize(1.1f, 1.1f);
   float targetOpacity = 0.5f;
   float targetCornerRadius = 20.0f;
   float targetBlurRadius = 10.0f;
@@ -3966,6 +3968,8 @@ int UtcDaliVisualGetVisualProperty01(void)
   Animation animation = Animation::New(1.0f);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Property::MIX_COLOR), targetColor);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Property::OPACITY), targetOpacity);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Transform::Property::OFFSET), targetOffset);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, Visual::Transform::Property::SIZE), targetSize);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, DevelVisual::Property::CORNER_RADIUS), targetCornerRadius);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, DevelColorVisual::Property::BLUR_RADIUS), targetBlurRadius);
   animation.Play();
@@ -3982,6 +3986,18 @@ int UtcDaliVisualGetVisualProperty01(void)
   DALI_TEST_CHECK(colorValue);
   DALI_TEST_EQUALS(colorValue->Get<Vector4>(), Vector4(targetColor.r, targetColor.g, targetColor.b, targetOpacity), TEST_LOCATION);
 
+  Property::Value* transformValue = resultMap.Find(Dali::Toolkit::Visual::Property::TRANSFORM);
+  Dali::Property::Map* transformMap = transformValue->GetMap();
+  DALI_TEST_CHECK(transformMap);
+
+  Property::Value* offsetValue = transformMap->Find(Toolkit::Visual::Transform::Property::OFFSET);
+  DALI_TEST_CHECK(offsetValue);
+  DALI_TEST_EQUALS(offsetValue->Get<Vector2>(), targetOffset, TEST_LOCATION);
+
+  Property::Value* sizeValue = transformMap->Find(Toolkit::Visual::Transform::Property::SIZE);
+  DALI_TEST_CHECK(sizeValue);
+  DALI_TEST_EQUALS(sizeValue->Get<Vector2>(), targetSize, TEST_LOCATION);
+
   Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::FLOAT);
   DALI_TEST_CHECK(cornerRadiusValue);
   DALI_TEST_EQUALS(cornerRadiusValue->Get< float >(), targetCornerRadius, TEST_LOCATION);
@@ -3992,6 +4008,8 @@ int UtcDaliVisualGetVisualProperty01(void)
 
   // Test uniform values
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", targetColor), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector2>("offset", targetOffset), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector2>("size", targetSize), true, TEST_LOCATION);
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<float>("cornerRadius", targetCornerRadius), true, TEST_LOCATION);
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<float>("blurRadius", targetBlurRadius), true, TEST_LOCATION);
 
@@ -4040,6 +4058,8 @@ int UtcDaliVisualGetVisualProperty02(void)
   application.Render();
 
   Vector3 targetColor(1.0f, 1.0f, 1.0f);
+  Vector2 targetOffset(0.05f, 0.05f);
+  Vector2 targetSize(1.1f, 1.1f);
   float targetOpacity = 0.5f;
   float targetCornerRadius = 20.0f;
   float targetBlurRadius = 10.0f;
@@ -4048,6 +4068,8 @@ int UtcDaliVisualGetVisualProperty02(void)
   Animation animation = Animation::New(1.0f);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "mixColor"), targetColor);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "opacity"), targetOpacity);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "offset"), targetOffset);
+  animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "size"), targetSize);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "cornerRadius"), targetCornerRadius);
   animation.AnimateTo(DevelControl::GetVisualProperty(dummyControl, DummyControl::Property::TEST_VISUAL, "blurRadius"), targetBlurRadius);
   animation.Play();
@@ -4064,6 +4086,18 @@ int UtcDaliVisualGetVisualProperty02(void)
   DALI_TEST_CHECK(colorValue);
   DALI_TEST_EQUALS(colorValue->Get<Vector4>(), Vector4(targetColor.r, targetColor.g, targetColor.b, targetOpacity), TEST_LOCATION);
 
+  Property::Value* transformValue = resultMap.Find(Dali::Toolkit::Visual::Property::TRANSFORM);
+  Dali::Property::Map* transformMap = transformValue->GetMap();
+  DALI_TEST_CHECK(transformMap);
+
+  Property::Value* offsetValue = transformMap->Find(Toolkit::Visual::Transform::Property::OFFSET);
+  DALI_TEST_CHECK(offsetValue);
+  DALI_TEST_EQUALS(offsetValue->Get<Vector2>(), targetOffset, TEST_LOCATION);
+
+  Property::Value* sizeValue = transformMap->Find(Toolkit::Visual::Transform::Property::SIZE);
+  DALI_TEST_CHECK(sizeValue);
+  DALI_TEST_EQUALS(sizeValue->Get<Vector2>(), targetSize, TEST_LOCATION);
+
   Property::Value* cornerRadiusValue = resultMap.Find(DevelVisual::Property::CORNER_RADIUS, Property::FLOAT);
   DALI_TEST_CHECK(cornerRadiusValue);
   DALI_TEST_EQUALS(cornerRadiusValue->Get< float >(), targetCornerRadius, TEST_LOCATION);
@@ -4074,6 +4108,8 @@ int UtcDaliVisualGetVisualProperty02(void)
 
   // Test uniform values
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector3>("mixColor", targetColor), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector2>("offset", targetOffset), true, TEST_LOCATION);
+  DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<Vector2>("size", targetSize), true, TEST_LOCATION);
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<float>("cornerRadius", targetCornerRadius), true, TEST_LOCATION);
   DALI_TEST_EQUALS(application.GetGlAbstraction().CheckUniformValue<float>("blurRadius", targetBlurRadius), true, TEST_LOCATION);
 
index db708c1..929825c 100644 (file)
@@ -771,9 +771,6 @@ int UtcDaliVisualFactoryGetNPatchVisual3(void)
 
     DALI_TEST_EQUALS( textureTrace.FindMethod("BindTexture"), true, TEST_LOCATION );
 
-    application.GetScene().Remove( actor );
-    DALI_TEST_CHECK( actor.GetRendererCount() == 0u );
-
     Vector2 naturalSize( 0.0f, 0.0f );
     visual.GetNaturalSize( naturalSize );
     DALI_TEST_EQUALS( naturalSize, Vector2( imageSize.GetWidth() - 2.0f, imageSize.GetHeight() - 2.0f ), TEST_LOCATION );
@@ -984,6 +981,79 @@ int UtcDaliVisualFactoryGetNPatchVisual7(void)
   END_TEST;
 }
 
+int UtcDaliVisualFactoryGetNPatchVisual8(void)
+{
+  ToolkitTestApplication application;
+  tet_infoline( "UtcDaliVisualFactoryGetNPatchVisual8: Add 9-patch visual on stage, instantly remove it and add new 9-patch visual with same propertyMap" );
+
+  VisualFactory factory = VisualFactory::Get();
+  DALI_TEST_CHECK( factory );
+
+  // Get actual size of test image
+  ImageDimensions imageSize = Dali::GetClosestImageSize( TEST_9_PATCH_FILE_NAME );
+
+  Property::Map propertyMap;
+  propertyMap.Insert( Toolkit::Visual::Property::TYPE, Visual::N_PATCH );
+  propertyMap.Insert( ImageVisual::Property::URL, TEST_9_PATCH_FILE_NAME );
+  propertyMap.Insert( ImageVisual::Property::SYNCHRONOUS_LOADING, false );
+  {
+    Visual::Base visual = factory.CreateVisual( propertyMap );
+    DALI_TEST_CHECK( visual );
+
+    Vector2 naturalSize( 0.0f, 0.0f );
+    visual.GetNaturalSize( naturalSize );
+    DALI_TEST_EQUALS( naturalSize, Vector2( imageSize.GetWidth(), imageSize.GetHeight() ), TEST_LOCATION );
+
+    TestGlAbstraction& gl = application.GetGlAbstraction();
+    TraceCallStack& textureTrace = gl.GetTextureTrace();
+    textureTrace.Enable(true);
+
+    DummyControl actor = DummyControl::New(true);
+
+    DummyControlImpl& dummyImpl = static_cast<DummyControlImpl&>(actor.GetImplementation());
+    dummyImpl.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
+
+    actor.SetProperty( Actor::Property::SIZE, Vector2( 200.f, 200.f ) );
+    DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+
+    application.GetScene().Add( actor );
+    actor.Unparent();
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render();
+
+    visual = factory.CreateVisual( propertyMap );
+    DALI_TEST_CHECK( visual );
+
+    visual.GetNaturalSize( naturalSize );
+    DALI_TEST_EQUALS( naturalSize, Vector2( imageSize.GetWidth(), imageSize.GetHeight() ), TEST_LOCATION );
+
+    actor = DummyControl::New(true);
+
+    DummyControlImpl& dummyImpl2 = static_cast<DummyControlImpl&>(actor.GetImplementation());
+    dummyImpl2.RegisterVisual( DummyControl::Property::TEST_VISUAL, visual );
+
+    actor.SetProperty( Actor::Property::SIZE, Vector2( 200.f, 200.f ) );
+    DALI_TEST_EQUALS( actor.GetRendererCount(), 0u, TEST_LOCATION );
+
+    application.GetScene().Add( actor );
+
+    DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger(1 ), true, TEST_LOCATION );
+
+    application.SendNotification();
+    application.Render();
+
+    Renderer renderer = actor.GetRendererAt( 0 );
+    auto textures = renderer.GetTextures();
+
+    DALI_TEST_EQUALS( textures.GetTextureCount(), 1, TEST_LOCATION );
+  }
+
+  END_TEST;
+}
+
 int UtcDaliNPatchVisualAuxiliaryImage01(void)
 {
   ToolkitTestApplication application;
@@ -1061,7 +1131,7 @@ int UtcDaliNPatchVisualAuxiliaryImage02(void)
 
   Renderer renderer2 = imageView2.GetRendererAt( 0 );
   auto textureSet2 = renderer2.GetTextures();
-  DALI_TEST_EQUALS( textureSet1 == textureSet2, true, TEST_LOCATION );
+  DALI_TEST_EQUALS( textureSet1 != textureSet2, true, TEST_LOCATION );
 
   END_TEST;
 }
index 21b7c0f..657446c 100644 (file)
@@ -34,6 +34,7 @@ OPTION(ENABLE_LINK_TEST          "Enable the link test" ON)
 OPTION(INSTALL_DOXYGEN_DOC       "Install doxygen doc" ON)
 OPTION(CONFIGURE_AUTOMATED_TESTS "Configure automated tests" ON)
 OPTION(USE_DEFAULT_RESOURCE_DIR  "Whether to use the default resource folders. Otherwise set environment variables for DALI_IMAGE_DIR, DALI_SOUND_DIR, DALI_STYLE_DIR, DALI_STYLE_IMAGE_DIR and DALI_DATA_READ_ONLY_DIR" ON)
+OPTION(BUILD_SCENE_LOADER        "Whether to build dali-scene-loader." ON)
 
 IF( ENABLE_PKG_CONFIGURE )
   FIND_PACKAGE( PkgConfig REQUIRED )
@@ -443,7 +444,7 @@ IF( ENABLE_COVERAGE )
     ADD_CUSTOM_TARGET( ${DALI_TOOLKIT_PREFIX}rename_cov_data ./rename-cov-data )
 
     ADD_CUSTOM_TARGET( ${DALI_TOOLKIT_PREFIX}cov_data ${LCOV_BIN} ${LCOV_OPTS} --base-directory . --directory . -c -o dali.info
-      COMMAND ${LCOV_BIN} ${LCOV_OPTS} --remove dali.info \"*/dali-env/*\" \"/usr/include/*\" "*/dali-env/*" "*solid-color-actor*" "*/dali-toolkit/third-party/*" -o dali.info )
+      COMMAND ${LCOV_BIN} ${LCOV_OPTS} --remove dali.info \"*/dali-env/*\" \"/usr/include/*\" "*/dali-env/*" "*solid-color-actor*" "*/dali-toolkit/third-party/*" \"*/dali-scene-loader/third-party/*\" -o dali.info )
 
     ADD_CUSTOM_TARGET( ${DALI_TOOLKIT_PREFIX}coverage genhtml ${LCOV_OPTS} -o ${COVERAGE_OUTPUT_DIR} dali.info
       DEPENDS cov_data )
@@ -520,6 +521,10 @@ IF( CONFIGURE_AUTOMATED_TESTS )
                   ${ROOT_SRC_DIR}/automated-tests/CMakeLists.txt @ONLY )
 ENDIF()
 
+IF ( BUILD_SCENE_LOADER )
+  ADD_SUBDIRECTORY( ${CMAKE_CURRENT_SOURCE_DIR}/dali-scene-loader )
+ENDIF()
+
 # Configuration Messages
 MESSAGE( STATUS "Configuration:\n" )
 MESSAGE( STATUS "Prefix:                        " ${PREFIX} )
@@ -541,6 +546,7 @@ MESSAGE( STATUS "Use pkg configure:             " ${ENABLE_PKG_CONFIGURE} )
 MESSAGE( STATUS "Vector Based Text Rendering:   " ${ENABLE_VECTOR_BASED_TEXT_RENDERING} )
 MESSAGE( STATUS "Enable link test:              " ${ENABLE_LINK_TEST} )
 MESSAGE( STATUS "Configure automated tests:     " ${CONFIGURE_AUTOMATED_TESTS} )
+MESSAGE( STATUS "Build Dali Scene Loader:       " ${BUILD_SCENE_LOADER} )
 MESSAGE( STATUS "CXXFLAGS:                      " ${CMAKE_CXX_FLAGS} )
 MESSAGE( STATUS "LDFLAGS:                       " ${CMAKE_SHARED_LINKER_FLAGS_INIT}${CMAKE_SHARED_LINKER_FLAGS} )
 
diff --git a/build/tizen/dali-scene-loader/CMakeLists.txt b/build/tizen/dali-scene-loader/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2e2cc38
--- /dev/null
@@ -0,0 +1,159 @@
+cmake_minimum_required(VERSION 2.6)
+set(name "dali2-scene-loader")
+
+project(${name} CXX)
+
+set(${name}_VERSION_MAJOR 2)
+set(${name}_VERSION_MINOR 0)
+set(${name}_VERSION_PATCH 0)
+set(${name}_VERSION ${${name}_VERSION_MAJOR}.${${name}_VERSION_MINOR}.${${name}_VERSION_PATCH} )
+
+SET(DALI_SCENE_LOADER_VERSION ${${name}_VERSION} )
+
+if(CMAKE_BUILD_TYPE MATCHES Debug)
+       add_definitions("-DDEBUG_ENABLED")
+endif()
+
+foreach(flag ${PKGS_CFLAGS})
+       set(extra_flags "${extra_flags} ${flag}")
+endforeach(flag)
+
+set(prj_cxx_std c++17)
+if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+       set(extra_flags "${extra_flags} -fPIC -std=${prj_cxx_std}")
+elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+       set(extra_flags "${extra_flags} -fPIC -std=${prj_cxx_std}")
+elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
+       set(extra_flags "${extra_flags} /std:${prj_cxx_std} /vmg /D_USE_MATH_DEFINES /D_CRT_SECURE_NO_WARNINGS /MP /GS /Oi /GL /EHsc")
+endif()
+
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${extra_flags}")
+
+set(prefix ${CMAKE_INSTALL_PREFIX})
+
+set(repo_root_dir "${CMAKE_CURRENT_LIST_DIR}/../../../")
+set(scene_loader_dir "${repo_root_dir}/dali-scene-loader")
+
+option(ENABLE_PKG_CONFIGURE "Use pkgconfig" ON)
+option(ENABLE_COVERAGE "Coverage" OFF)
+
+IF( ENABLE_COVERAGE OR "$ENV{CXXFLAGS}" MATCHES --coverage )
+  ADD_COMPILE_OPTIONS( --coverage )
+  SET(ENABLE_COVERAGE ON)
+  SET(COVERAGE --coverage)
+ENDIF()
+
+if (ENABLE_PKG_CONFIGURE)
+       find_package(PkgConfig REQUIRED)
+
+       pkg_check_modules(DALICORE REQUIRED dali2-core)
+       pkg_check_modules(DALIADAPTOR REQUIRED dali2-adaptor)
+
+       # Configure the pkg-config file
+       # Requires the following variables to be setup:
+       # @PREFIX@ @EXEC_PREFIX@ @DALI_VERSION@ @LIB_DIR@ @DEV_INCLUDE_PATH@
+       set( LIB_DIR $ENV{libdir} )
+       if( NOT LIB_DIR )
+               set( LIB_DIR ${CMAKE_INSTALL_LIBDIR} )
+       endif()
+       if( NOT LIB_DIR )
+               set( LIB_DIR ${prefix}/lib )
+       endif()
+
+       set(PREFIX ${prefix})
+       set(EXEC_PREFIX ${CMAKE_INSTALL_PREFIX})
+       set(DEV_INCLUDE_PATH ${repo_root_dir})
+
+       set(core_pkg_cfg_file dali2-scene-loader.pc)
+       configure_file(${CMAKE_CURRENT_LIST_DIR}/${core_pkg_cfg_file}.in ${core_pkg_cfg_file} @ONLY)
+endif()
+
+if (ENABLE_COVERAGE)
+       find_program(lcov_bin "lcov")
+       if (${lcov_bin})
+               set(cov_dir ".cov")
+               set(cov_output_dir "doc/coverage")
+
+               execute_process(COMMAND bash -c "${lcov_bin} --version | cut -d' ' -f4" OUTPUT_VARIABLE lcov_version)
+               string(REPLACE "." ";" lcov_vlist ${lcov_version})
+               if (NOT $<VERSION_LESS:${lcov_version},"1.10"> )
+                       set(lcov_opts --rc lcov_branch_coverage=1)
+               endif()
+
+               add_custom_target(${prefix}rename_cov_data ./rename-cov-data)
+
+               add_custom_target(${prefix}cov_data
+                       ${lcov_bin} ${lcov_opts} --base-directory . --directory . -c -o dali.info
+                       COMMAND ${lcov_bin} ${lcov_opts} --remove dali.info \"*/dali-env/*\" \"/usr/include/*\" \"/usr/local/include/*\" \"*/dali-env/*\" \"*/dali-scene-loader/third-party/*\" -o dali.info
+               )
+
+               add_custom_target(${prefix}coverage genhtml ${lcov_opts} -o ${cov_output_dir} dali.info)
+
+               add_custom_target(${prefix}reset_coverage @${lcov_bin} -\ --direwctory `pwd`)
+
+               add_custom_target(${prefix}distclean @echo cleaning for source distribution)
+               add_custom_command(
+                       DEPENDS ${prefix}clean
+                       COMMENT "distribution clean"
+                       COMMAND find
+                       ARGS .
+                       -not -name config.cmake -and \(
+                       -name tester.c -or
+                       -name Testing -or
+                       -name CMakeFiles -or
+                       -name doc -or
+                       -name cmake.depends -or
+                       -name cmake.check_depends -or
+                       -name CMakeCache.txt -or
+                       -name cmake.check_cache -or
+                       -name *.cmake -or
+                       -name Makefile -or
+                       -name core -or
+                       -name core.* -or
+                       -name gmon.out -or
+                       -name install_manifest.txt -or
+                       -name *.pc -or
+                       -name *.gcov -or
+                       -name *.gcno -or
+                       -name *.gcda -or
+                       -name *~ -or
+                       -name libdali*.so* \)
+                       | grep -v TC | xargs rm -rf
+                       TARGET  ${DALI_TOOLKIT_PREFIX}distclean
+                       VERBATIM
+               )
+       endif()
+endif()
+
+set(scene_loader_src_files "")
+include("${scene_loader_dir}/internal/file.list")
+include("${scene_loader_dir}/public-api/file.list")
+
+set(prefix_include_dir "${prefix}/include")
+include_directories(${repo_root_dir}
+       ${prefix_include_dir}
+)
+
+add_library(${name} SHARED ${scene_loader_src_files})
+
+target_link_libraries(${name} ${DALICORE_LDFLAGS} ${DALIADAPTOR_LDFLAGS}
+       dali2-toolkit
+       ${COVERAGE})
+
+if( ANDROID )
+       target_link_libraries(${name} log)
+endif()
+
+install(TARGETS ${name} DESTINATION ${LIB_DIR})
+
+file(GLOB scene_loader_include_files ${scene_loader_public_api_dir})
+install(DIRECTORY ${scene_loader_include_files}
+       DESTINATION "${prefix_include_dir}/dali-scene-loader"
+       FILES_MATCHING PATTERN "*.h"
+)
+
+if (ENABLE_PKG_CONFIGURE)
+       install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${core_pkg_cfg_file}
+               DESTINATION ${LIB_DIR}/pkgconfig
+       )
+endif()
diff --git a/build/tizen/dali-scene-loader/build.sh b/build/tizen/dali-scene-loader/build.sh
new file mode 100755 (executable)
index 0000000..5397887
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+git clean -xdf .
+cmake -DCMAKE_INSTALL_PREFIX=$DESKTOP_PREFIX .
+make install -j16
diff --git a/build/tizen/dali-scene-loader/dali2-scene-loader.pc.in b/build/tizen/dali-scene-loader/dali2-scene-loader.pc.in
new file mode 100644 (file)
index 0000000..8f7e885
--- /dev/null
@@ -0,0 +1,12 @@
+prefix=@PREFIX@
+exec_prefix=@EXEC_PREFIX@
+apiversion=@DALI_SCENE_LOADER_VERSION@
+libdir=@LIB_DIR@
+includedir=@DEV_INCLUDE_PATH@
+
+Name: Dali 3D Engine Scene Loader
+Description: Dali Scene Loader library
+Version: ${apiversion}
+Requires: dali2-toolkit
+Libs: -L${libdir} -ldali2-scene-loader
+Cflags: -I${includedir}
index 75cece0..0a9d553 100644 (file)
@@ -995,6 +995,7 @@ INPUT                  = @DOXYGEN_DOCS_DIR@/content \
                          @prefix@/include/dali/devel-api \
                          ../../../dali-toolkit/public-api \
                          ../../../dali-toolkit/devel-api \
+                         ../../../dali-scene-loader/public-api \
                          ../../../automated-tests/README.md
 
 # This tag can be used to specify the character encoding of the source files
@@ -2266,7 +2267,10 @@ INCLUDE_FILE_PATTERNS  =
 # recursively expanded use the := operator instead of the = operator.
 # This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
 
-PREDEFINED             = DALI_TOOLKIT_API \
+PREDEFINED             = DALI_CORE_API \
+                         DALI_ADAPTOR_API \
+                         DALI_TOOLKIT_API \
+                         DALI_SCENE_LOADER_API \
                          DALI_INTERNAL \
                          __attribute__ \
                          ((visibility \
diff --git a/dali-scene-loader.manifest b/dali-scene-loader.manifest
new file mode 100644 (file)
index 0000000..a76fdba
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+       <request>
+               <domain name="_" />
+       </request>
+</manifest>
diff --git a/dali-scene-loader.manifest-smack b/dali-scene-loader.manifest-smack
new file mode 100644 (file)
index 0000000..08561d6
--- /dev/null
@@ -0,0 +1,8 @@
+<manifest>
+       <assign>
+               <filesystem path="/usr/lib/*" label="_" />
+       </assign>
+       <request>
+               <domain name="dali"/>
+       </request>
+</manifest>
diff --git a/dali-scene-loader/README.md b/dali-scene-loader/README.md
new file mode 100644 (file)
index 0000000..b29c463
--- /dev/null
@@ -0,0 +1,365 @@
+# Table of contents
+   * [Overview](#overview)
+   * [`scenes`](#scenes)
+   * [`nodes`](#nodes)
+      * [Transformations](#transformations)
+      * [Size](#size)
+      * [Visibility](#visibility)
+      * [`children`](#children)
+      * [`customization`](#customization)
+      * [Renderables](#renderables)
+         * [`model`](#model)
+         * [`arc`](#arc)
+      * [`inverseBindPoseMatrix`](#inversebindposematrix)
+   * [`materials`](#materials)
+   * [`meshes`](#meshes)
+   * [`shaders`](#shaders)
+      * [`rendererState`](#rendererState)
+      * [`defines`](#defines)
+      * [`hints`](#hints)
+      * [Uniforms](#uniforms)
+   * [`skeletons`](#skeletons)
+   * [`cameras`](#cameras)
+      * [Perspective cameras](#perspective-cameras)
+      * [Orthographic cameras](#orthographic-cameras)
+   * [`lights`](#lights)
+   * [`animations`](#animations)
+      * [`properties`](#properties)
+         * [`keyFramesBin`](#keyframesbin)
+         * [`keyFrames`](#keyFrames)
+         * [`value`](#value)
+   * [`animationGroups`](#animationGroups)
+
+# Overview
+DLI is a JSON based format for representing 3D scenes.
+   * Like glTF, the DLI document defines a set of scenes, which in turn define a hierarchical structure of nodes, and the additional data required to render them - meshes, geometry.
+   * Unlike glTF, it allows definitions of shaders, environment maps, and lighting parameters as well;
+   * Unlike glTF, DLI does not concern itself with buffers, buffer views and accessors;
+   * It supports customizations, which allow replacing parts of the scene based on customization tags and options, without reloading the whole scene.
+   * It supports the processing of custom categories, which can be scheduled to take place prior to or after the processing of the scene data, as well as custom node and animation processors.
+
+# `scenes`
+The "scenes" element is an array of JSON objects, each of which must define a `nodes` array with the index of the definition of the root node.
+
+:warning: The array must not be empty. Only the first element is used.
+An optional `scene` element with an integer value may be defined to specify the index of the first scene to be created.
+The rest of the scenes are created in the order of their definition (from index 0 to the highest, skipping the default - already created - scene).
+```js
+{
+    "scene": 0,
+    "scenes": [ { "nodes": [ 0 ] } ],
+    "nodes": [ {
+        "name": "some_unique_name"
+    } ]
+}
+```
+
+# `nodes`
+The 3D scene is built using a hierarchy of nodes, which are used to position the objects to render.
+
+:warning: Each node must have a `name` string that 1, is not an empty string and 2, is unique within the DLI document. The use of alpha-numeric characters and underscore only is highly recommeneded.
+
+## Transformations
+There are two ways to define the local transform of each nodes. Both are optional, defaulting to a position at the origin, a scale of one and no rotation.
+   * A `matrix` array of 16 numerical values defining a column-major 4x4 matrix;
+   * A `position` array of 3 numerical values defining a 3D vector and an `angle` array of 3 numerical values defining euler angles of rotation along the 3 axes.
+
+## Size
+The size of the bounding box of a node may be specified using either of the optional `size` or `bounds` properties, as arrays of 2 or 3 numerical values. Its default value is the unit vector.
+
+## Visibility
+The `visible` optional boolean property defines whether a node and its children are rendered (`true`) or not (`false`).
+
+## `children`
+An array of 0 or more indices into the top level `nodes` array, which shall inherit the transform and visibility of their parent node.
+
+:warning: Nodes are processed in the order they are encountered during the depth-first traversal of the hierarchy.
+```js
+  "nodes": [ {
+    "name": "hip",
+    "children": [ 3, 1, 2 ]
+  }, {
+    "name": "spine"
+  }, {
+    "name": "left leg"
+  }, {
+    "name": "right leg"
+  } ]
+```
+
+## `customization`
+Customizations may allow creating a different sub-tree of each node that define them, based on application specific configuration settings known at the time of creating the scene.
+The definition of a `customization` is a single string tag: 
+```js
+  "nodes": [ {
+    "name": "Soup du jour",
+    "customization": "flavor", // this one
+    "children": [ 1, 2, 3 ]
+  }, {
+    "name": "Broccoli and Stilton",
+  }, {
+    "name": "Butternut Squash",
+  }, {
+    "name": "Strawberry and Cream",
+  } ]
+```
+:warning: Customizations and renderables are mutually exclusive on the same node.
+
+## Renderables
+There is support for two types of nodes that define renderable content.
+The definition of these renderables come in sub-objects.
+All of them support a `color` property, which is an array of 3 or 4 numerical values for RGB or RGBA components. For the alpha value to take effect, alpha blending must be enabled; this is controlled by the [material](#materiaL).
+
+:warning: Customizations and renderables are mutually exclusive on the same node.
+
+### `model`
+Provides definition for a 3D object, which requires a `mesh`, `shader` and `material`.
+Each of these are provided in form of an integer index into the related top-level array of the DLI document.
+
+:warning: `mesh` must be provided; the rest are optional and default to 0.
+```js
+  "nodes": [ {
+    "name": "Sphere",
+    "model": {
+      "mesh": 0, // required
+      "shader": 0, // optional, defaults to 0
+      "material": 1 // optional, defaults to 0
+    }
+  } ]
+```
+
+### `arc`
+Arc is a specialisation of a model that allows the rendering of circular progress bars. As such, it also must provide a `mesh` ID.
+```js
+  "nodes": [ {
+    "name": "Any Name",
+    "arc": {
+      "mesh": 0, // required
+      "shader": 0, // optional, defaults to 0
+      "material": 1 // optional, defaults to 0
+      "antiAliasing": true,
+      "radius": -0.928,
+      "startAngle": -81.0,
+      "endAngle": 261
+    }
+  } ]
+```
+   * `startAngle` and `endAngle` are the angular positions where the arc starts and ends, in degrees.
+   * `radius` is the inner radius of the arc, the outer radius being defined by the [`size`](#size) of the node.
+   * `antiAliasing` defines whether the edges of the arc should be smoothed.
+   
+## `inverseBindPoseMatrix`
+Nodes that serve as joints of a [skeleton](#skeleton) must define this property as an array of 16 numerical values for a column major 4x4 matrix.
+```js
+  "nodes": [ {
+    "name" : "l_shoulder_JNT",
+    "inverseBindPoseMatrix" : [ 0.996081, -0.0407448, 0.0785079, 0.0, 0.0273643, 0.985992, 0.164531, 0.0, -0.0841121, -0.161738, 0.983242, 0.0, -0.0637747, -1.16091, -0.161038, 1.0 ]
+  } ]
+```
+
+# `environment`
+An array of environment map definitions, which have the following format:
+```js
+  "environment": [{
+    "cubeSpecular": "Studio_001/Radiance.ktx",
+    "cubeDiffuse": "Studio_001/Irradiance.ktx",
+    "cubeInitialOrientation": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] 
+  } ]
+```
+`cubeSpecular` and `cubeDiffuse` are the names of the respective cube map files that will be attempted to be located from the _environment_ path, which is up to the application. A 1x1x1 white RGB888 cubemap is created in place of either that was omitted.
+`cubeInitialOrientation` may be used to inform the (PBR) shader of a matrix which describes the initial orientation of the cube map. Defaults to the identity matrix.
+
+# `materials`
+Defines configurations of textures (and their samplers) that form materials. These can be created from single values or image files (in the formats that DALi supports).
+```js
+  "materials": [ {
+    "environment": 0,
+    "albedoMap": "A.png",
+    "normal": "N.png",
+    "metallicRoughnessMap": "MR.png",
+    "color": [ 1.0, 0.8, 0.7, 0.5 ]
+  } ]
+```
+   * `environment`: The index of the [environment map](#environments) to use. Single integer, defaults to `0`.
+   * `mipmap`: A boolean to speficy if the creation and sampling of mipmaps should be enabled. Off by default.
+   * `color`: Base color, which the color of the node gets multiplied by. Defaults to white.
+   * `metallic` and `roughness`: Properties for PBR materials; both are expected to be a single numerical value and default to `1.0`.
+
+## Texture maps
+`albedoMap` / `albedoMetallicMap` / `normalMap` / `normalRoughnessMap` / `metallicRoughnessMap` / `subsurfaceMap`: define various texture semantics, i.e. the role of the texture, which shall be loaded from an image _inside the materials path_, which is up to the application. All of them are optional.
+
+:warning: Semantics shall not overlap within the same material, e.g. multiple albedo definitions, or albedo and albedoMetallic.
+
+# `meshes`
+Defines an array of meshes which are used to access geometry data stored in a binary file. The `uri` property is used to locate each file _inside the mesh path_, which is up to the application; alternatively it can be set to `"quad"`, resulting in the creation of a unit quad.
+Those models loaded from a file may provide an accessor, and flag its presence in the `attributes` bitmask property. The following are supported:
+|Attribute Name|Bit|Decimal value|Type|Remarks|
+|-|-|-|-|-|
+|`indices`  |0|  1|unsigned short||
+|`positions`|1|  2|Vector3||
+|`normals`  |2|  4|Vector3||
+|`textures` |3|  8|Vector2|UVs|
+|`tangents` |4| 16|Vector3||
+|           |5| 32||Ignored, but reserved for bitangents|
+|`joints0`  |6| 64|Vector4|Joint IDs for skinned meshes|
+|`weights0` |7|128|Vector4|Joint weights for skinned meshes|
+
+E.g. if positions, normals and tangents are provided, the `attributes` property must have a value of 2 + 4 + 16 = 22.
+Each attribute must provide a `byteOffset` and `byteLength` property, which must be correctly sized for the type of the given attribute.
+Finally, to specify what primitives should the geometry be rendered as, a `primitive` property may be provided with one of the following values: `TRIANGLES` (default), `LINES`, `POINTS`.
+
+## Skinned meshes
+DLI supports meshes that allow deformation by skeletal animation. These must define a few additional properties:
+   * `joints0` and `weights0` attributes, as above.
+   * A [`skeleton`](#skeletons) ID, to specify which (joint) nodes' transformations affect the mesh.
+
+:warning: The maximum number of bones supported by DALi Scene Loader is `64`.
+
+## Blend shapes
+Blend shapes provide alternate configurations of vertex `positions`, `normals` and/or `tangents` that may be blended with the same attributes of the base mesh, controlled by an animatable `weight`.
+```js
+  "meshes": [ {
+      "uri": "example.bin",
+      "attributes": 2,
+      "positions": {
+          "byteOffset": 0,
+          "byteLength": 12000
+      },
+      "blendShapesHeader": {
+          "version": "2.0",
+          "byteOffset": 12000,
+          "byteLEngth": 4
+      },
+      "blendShapes": [ {
+          "weight": 0.0,
+          "positions": {
+              byteOffset: 12004,
+              byteLength: 12000
+          }
+      } ]
+  } ]
+```
+A `blendShapesHeader`, if present, must define:
+   * the `version` of the blend shapes; supported values are `1.0` and `2.0`. The difference between the versions is that v1.0 requires a per-blend shape definition of an un-normalization factor.
+   * the `byteOffset` and `byteLength` of a buffer in the binary which defines the width (2 bytes) and height (2 bytes) of the texture that dali-scene-loader creates for blend shape data.
+
+The `blendShapes` array then defines the shapes that are available to blend between, comprising of:
+   * An initial `weight` numerical, the default is 0;
+   * `position` / `normals` / `tangents` attributes, which must be also present in the base mesh and are the same format (`byteOffset` / `byteLength`);
+
+:warning: The size of the attributes of the blend shape must match that of the base mesh (and each other) - i.e. they must define the same number of vertices.
+
+# `shaders`
+Provides an array of shader programs that renderables may use for rendering.
+For each shader, `vertex` and `fragment` are required string properties pointing to the shader files _inside the shader path_, which is up to the application.
+   * `rendererState`: This string defines the options for configuring the settings of the renderer. Refer to public-api/renderer-state.h for details.
+   * `defines`: An optional array of strings which will be used to create #defines into the vertex and fragment shaders.
+   * `hints`: An optional array of strings that map to `Dali::Shader::Hint` values. Therefore two values are supported - `MODIFIES_GEOMETRY` and `OUTPUT_IS_TRANSPARENT`.
+
+## Uniforms
+Every property that is not one of the reserved keys above, will be attempted to be registered as a uniform of the same name.
+
+:warning: Boolean values will be converted to floating point 1.0 (for `true`) or 0.0 (for `false`).
+
+:warning: Integer values will be converted to floating point.
+
+:warning: Arrays of numerical values will be treated as one of the vec2 / vec3 / vec4 / mat3 / mat4 types, depending on what do they define sufficient components for.
+```js
+  "shaders": [ {
+    "vertex": "dli_pbr.vsh",
+    "fragment": "dli_pbr.fsh",
+    "defines": [ "HIGHP", SKINNING" ],
+    "rendererState": "DEPTH_WRITE|DEPTH_TEST|CULL_BACK",
+    "hints": 
+    "uMaxLOD": 6
+  } ]
+```
+
+# `skeletons`
+Skeletons in DLI simply define the name of a `node` that shall serve as the _root joint_ of the given skeleton.
+```js
+  "skeletons": [ {
+    "node": "hipJoint"
+  } ]
+```
+The Joint IDs in skinned mesh data relate to the descendants of the [node](#nodes) identified as the root joint that are joints, i.e. define [`inverseBindPoseMatrix`](#inversebindposematrix), in depth-first traversal order.
+
+# `cameras`
+Define the transformation of viewers and the projection used by them.
+All cameras may define:
+   * `matrix`: an array of 16 numerical values for the transform matrix
+   * `near` and `far` values for the position of the respective clipping planes. These default to `0.1` and `1000.0`, respectively.
+
+## Perspective cameras
+This projection type - and a vertical field of view of `60` degrees - is the default.
+The (V)FOV can be specified in the `fov` property, as a single numerical value.
+
+## Orthographic cameras
+If the `orthographic` is defined with an array of four numerical values for the left, right, bottom and top clipping planes (in this order), then orthographic projection is used, and `fov` is ignored.
+
+# `lights`
+Define parameters for a single light source - the implementation is up to the application. The following properties are supported.
+   * `transform`: matrix of 16 numerical values for the positioning / directing of the light;
+   * `color`: array of 3 components for RGB;
+   * `intensity`: single float;
+   * `shadowIntensity`: single float;
+   * `shadowMapSize`: unsigned integer size (same width & height) of shadow map.
+   * `orthographicSize`: single float to define the size (same width & height) of orthographic position.
+
+# `animations`
+Animations provide a way to change properties of nodes (and those of their renderables) over time.
+```js
+  "animations": [ {
+     "name": "Idle",
+     "loopCount": 0,
+     "duration": 4.0,
+     "endAction": "DISCARD",
+     "disconnectAction": "DISCARD",
+     "properties": []
+  } ]
+```
+   * `name`: the identifier to look the animation up by;
+   * `loopCount`: the number of times that the animation should be played. The default is `1`; `0` signifies infinity repetitions.
+   * `duration`: the duration of the animation in seconds. If not provided, it will be calculated from the properties.
+   * `endAction` and `disconnectAction`: the supported values, defaults and their meaning are described in the `Dali::Animation::EndAction` enum;
+
+## `properties`
+An array of animation property definitions.
+   * `node`: the name of the node whose property shall be animated;
+   * `property`: the name that the property was registered under.
+   * `alphaFunction`: the name of an alpha function which shall be used in animating a value, or between key frames;
+   * `timePeriod`: `delay` (defaults to `0.0`) and `duration` (defaults to the `duration` of the animation) of the property animation, in sceonds;
+
+The property may be animated using one of the following methods. They are listed in priority order; if e.g. a property defines `keyFramesBin` and `keyFrames`, then only `keyFramesBin` is used. 
+
+### `keyFramesBin`
+JSON object that defines a keyframe animation in a binary buffer, with the following properties:
+   * `url` the path to the file containing the buffer;
+   * `numKeys`: the number of keys.
+   * `byteOffset`: offset to the start of the buffer
+
+The size of the buffer depends on the property being animated, where  the property value for each frame follows a 4 byte floating point value determining progress (between 0 and 1).
+
+:warning: Only `position` (3D vector of floats, 12 bytes), `rotation` (Quaternion, 12 bytes), and `scale` (3D vector, 12 bytes) properties are supported.
+
+### `keyFrames`
+JSON array of keyframe objects defined with the following properties:
+   * `progress`: a scalar between `0` and `1` to apply to the duration to get the time stamp of the frame;
+   * `value`: array of 3 or 4 numerical values depending on which property is being animated;
+
+:warning: Only `position`, `rotation`, and `scale` properties are supported.
+
+### `value`
+Value animations animate a property using a single value that may be absolute or relative.
+The properties it supports are the following:
+   * `value`: the value to animate to (if absolute) or by (if `relative`);
+   * `relative`: whether `value` is a target, or an offset;
+
+# `animationGroups`
+Animation groups simply group animations together by name, under another name, which may be used to trigger them at the same time.
+```js
+  "animationGroups": [ {
+    "name": "Idle",
+    "animations": [ "IdleBody", "IdleFace" ]
+  } ]
+```
diff --git a/dali-scene-loader/internal/file.list b/dali-scene-loader/internal/file.list
new file mode 100644 (file)
index 0000000..70d94f3
--- /dev/null
@@ -0,0 +1,8 @@
+set(scene_loader_internal_dir "${scene_loader_dir}/internal")
+
+set(scene_loader_src_files ${scene_loader_src_files}
+       ${scene_loader_internal_dir}/gltf2-asset.cpp
+       ${scene_loader_internal_dir}/hash.cpp
+       ${scene_loader_internal_dir}/json-reader.cpp
+       ${scene_loader_internal_dir}/json-util.cpp
+)
diff --git a/dali-scene-loader/internal/gltf2-asset.cpp b/dali-scene-loader/internal/gltf2-asset.cpp
new file mode 100644 (file)
index 0000000..e77a6f5
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+* Copyright (c) 2020 Samsung Electronics Co., Ltd.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+#include "dali-scene-loader/internal/gltf2-asset.h"
+#include "dali/public-api/math/matrix.h"
+#include <algorithm>
+#include <map>
+
+#define ENUM_STRING_MAPPING(t, x) { #x, t::x }
+
+using namespace Dali;
+
+namespace gltf2
+{
+namespace
+{
+
+constexpr uint32_t ACCESSOR_TYPE_ELEMENT_COUNT[]{
+  1,
+  2,
+  3,
+  4,
+  4,
+  9,
+  16,
+  static_cast<uint32_t>(-1)
+};
+
+const std::map<std::string_view, AccessorType::Type> ACCESSOR_TYPES{
+  ENUM_STRING_MAPPING(AccessorType, SCALAR),
+  ENUM_STRING_MAPPING(AccessorType, VEC2),
+  ENUM_STRING_MAPPING(AccessorType, VEC3),
+  ENUM_STRING_MAPPING(AccessorType, VEC4),
+  ENUM_STRING_MAPPING(AccessorType, MAT2),
+  ENUM_STRING_MAPPING(AccessorType, MAT3),
+  ENUM_STRING_MAPPING(AccessorType, MAT4),
+};
+
+const std::map<std::string_view, AlphaMode::Type> ALPHA_MODE_TYPES{
+  ENUM_STRING_MAPPING(AlphaMode::Type, OPAQUE),
+  ENUM_STRING_MAPPING(AlphaMode::Type, MASK),
+  ENUM_STRING_MAPPING(AlphaMode::Type, BLEND),
+};
+
+const std::map<std::string_view, Attribute::Type> ATTRIBUTE_TYPES{
+  ENUM_STRING_MAPPING(Attribute::Type, POSITION),
+  ENUM_STRING_MAPPING(Attribute::Type, NORMAL),
+  ENUM_STRING_MAPPING(Attribute::Type, TANGENT),
+  ENUM_STRING_MAPPING(Attribute::Type, TEXCOORD_0),
+  ENUM_STRING_MAPPING(Attribute::Type, TEXCOORD_1),
+  ENUM_STRING_MAPPING(Attribute::Type, COLOR_0),
+  ENUM_STRING_MAPPING(Attribute::Type, JOINTS_0),
+  ENUM_STRING_MAPPING(Attribute::Type, WEIGHTS_0),
+};
+
+const std::map<std::string_view, Animation::Sampler::Interpolation::Type> ANIMATION_SAMPLER_INTERPOLATION{
+  ENUM_STRING_MAPPING(Animation::Sampler::Interpolation::Type, STEP),
+  ENUM_STRING_MAPPING(Animation::Sampler::Interpolation::Type, LINEAR),
+  ENUM_STRING_MAPPING(Animation::Sampler::Interpolation::Type, CUBICSPLINE),
+};
+
+const std::map<std::string_view, Animation::Channel::Target::Type> ANIMATION_CHANNEL_TARGET_PATH_TYPES{
+  ENUM_STRING_MAPPING(Animation::Channel::Target::Type, TRANSLATION),
+  ENUM_STRING_MAPPING(Animation::Channel::Target::Type, ROTATION),
+  ENUM_STRING_MAPPING(Animation::Channel::Target::Type, SCALE),
+  ENUM_STRING_MAPPING(Animation::Channel::Target::Type, WEIGHTS),
+};
+
+}
+
+bool Component::IsUnsigned(Type t)
+{
+  return t == UNSIGNED_BYTE || t == UNSIGNED_SHORT || t == UNSIGNED_INT;
+}
+
+uint32_t Component::Size(Type t)
+{
+  switch (t)
+  {
+  case BYTE:
+  case UNSIGNED_BYTE:
+    return 1;
+  case SHORT:
+  case UNSIGNED_SHORT:
+    return 2;
+  case UNSIGNED_INT:
+  case FLOAT:
+    return 4;
+  default:
+    return -1;
+  }
+}
+
+uint32_t AccessorType::ElementCount(Type t)
+{
+  return ACCESSOR_TYPE_ELEMENT_COUNT[t];
+}
+
+AccessorType::Type AccessorType::FromString(const char* s, size_t len)
+{
+  auto iFind = ACCESSOR_TYPES.find(std::string_view(s, len));
+  if (iFind != ACCESSOR_TYPES.end())
+  {
+    return iFind->second;
+  }
+  return AccessorType::INVALID;
+}
+
+AlphaMode::Type AlphaMode::FromString(const char* s, size_t len)
+{
+  auto iFind = ALPHA_MODE_TYPES.find(std::string_view(s, len));
+  if (iFind != ALPHA_MODE_TYPES.end())
+  {
+    return iFind->second;
+  }
+  return AlphaMode::INVALID;
+}
+
+Attribute::Type Attribute::FromString(const char* s, size_t len)
+{
+  auto iFind = ATTRIBUTE_TYPES.find(std::string_view(s, len));
+  if (iFind != ATTRIBUTE_TYPES.end())
+  {
+    return iFind->second;
+  }
+  return Attribute::INVALID;
+}
+
+Animation::Sampler::Interpolation::Type Animation::Sampler::Interpolation::FromString(const char* s, size_t len)
+{
+  auto iFind = ANIMATION_SAMPLER_INTERPOLATION.find(std::string_view(s, len));
+  if (iFind != ANIMATION_SAMPLER_INTERPOLATION.end())
+  {
+    return iFind->second;
+  }
+  return Animation::Sampler::Interpolation::Type::INVALID;
+}
+
+uint32_t ComponentTypedBufferViewClient::GetBytesPerComponent() const
+{
+  return Component::Size(mComponentType);
+}
+
+Animation::Channel::Target::Type Animation::Channel::Target::FromString(const char* s, size_t len)
+{
+  std::string target(s, len);
+  std::transform(target.begin(), target.end(), target.begin(), ::toupper);
+
+  auto iFind = ANIMATION_CHANNEL_TARGET_PATH_TYPES.find(std::string_view(target.c_str(), len));
+  if (iFind != ANIMATION_CHANNEL_TARGET_PATH_TYPES.end())
+  {
+    return iFind->second;
+  }
+  return Animation::Channel::Target::INVALID;
+};
+
+void Node::SetMatrix(const Matrix& m)
+{
+  m.GetTransformComponents(mTranslation, mRotation, mScale);
+}
+
+Quaternion ReadQuaternion(const json_value_s & j)
+{
+       return Quaternion(ReadDaliVector<Vector4>(j));
+}
+
+}
diff --git a/dali-scene-loader/internal/gltf2-asset.h b/dali-scene-loader/internal/gltf2-asset.h
new file mode 100644 (file)
index 0000000..79a72db
--- /dev/null
@@ -0,0 +1,585 @@
+#ifndef DALI_SCENE_LOADER_GLTF2_ASSET_H_
+#define DALI_SCENE_LOADER_GLTF2_ASSET_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/index.h"
+#include "dali-scene-loader/internal/json-reader.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/math/vector4.h"
+#include "dali/public-api/math/quaternion.h"
+#include "dali/public-api/common/vector-wrapper.h"
+#include "dali/devel-api/common/map-wrapper.h"
+#include <cstdint>
+#include <memory>
+
+namespace gltf2
+{
+
+using Index = Dali::SceneLoader::Index;
+
+template <typename T>
+class Ref
+{
+public:
+  Ref() = default;
+  Ref(std::vector<T>& v, Index i)
+  : mVector(&v),
+    mIndex(i)
+  {}
+
+  /**
+   * @return The index of the object into the vector.
+   * @note It is client code responsibility to ensure that the vector is unambiguous. It should be in
+   *  a glTF document, since there's one vector for each type.
+   */
+  Index GetIndex() const { return mIndex; }
+
+  /**
+   * @brief There may be scenarios in which the object, whose vector we're populating, changes, e.g.
+   *  when we don't have a final one at the time of reading the references.
+   */
+  void UpdateVector(std::vector<T>& v)
+  {
+    mVector = &v;
+  }
+
+  operator bool() const { return mVector != nullptr; }
+  T* operator->() const { return &(*mVector)[mIndex]; }
+  T& operator*() const { return (*mVector)[mIndex]; }
+
+  bool operator==(const Ref<T>& other) const
+  {
+    return mVector == other.mVector && mIndex == other.mIndex;
+  }
+
+  bool operator!=(const Ref<T>& other) const
+  {
+    return !operator==(other);
+  }
+
+private:
+  std::vector<T>* mVector = nullptr;
+  Index mIndex = Dali::SceneLoader::INVALID_INDEX;
+};
+
+struct Asset
+{
+  std::string_view mVersion;
+};
+
+struct Component
+{
+  enum Type
+  {
+    BYTE = 5120,
+    UNSIGNED_BYTE = 5121,
+    SHORT = 5122,
+    UNSIGNED_SHORT = 5123,
+    UNSIGNED_INT = 5125,
+    FLOAT = 5126,
+    INVALID = -1
+  };
+
+  static bool IsUnsigned(Type t);
+  static uint32_t Size(Type t);
+
+  Component() = delete;
+};
+
+struct AccessorType
+{
+  enum Type
+  {
+    SCALAR,
+    VEC2,
+    VEC3,
+    VEC4,
+    MAT2,
+    MAT3,
+    MAT4,
+    INVALID
+  };
+
+  static uint32_t ElementCount(Type t);
+
+  static Type FromString(const char* s, size_t len);
+
+  AccessorType() = delete;
+};
+
+struct AlphaMode
+{
+  enum Type
+  {
+    OPAQUE,
+    MASK,
+    BLEND,
+    INVALID
+  };
+
+  static Type FromString(const char* s, size_t len);
+
+  AlphaMode() = delete;
+};
+
+struct Attribute
+{
+  enum Type
+  {
+    POSITION,
+    NORMAL,
+    TANGENT,
+    TEXCOORD_0,
+    TEXCOORD_1,
+    COLOR_0,
+    JOINTS_0,
+    WEIGHTS_0,
+    INVALID
+  };
+
+  static Type FromString(const char* s, size_t len);
+
+  Attribute() = delete;
+};
+
+struct Buffer
+{
+  uint32_t mByteLength;
+  std::string_view mUri;
+  //TODO: extensions
+  //TODO: extras
+};
+
+struct BufferView
+{
+  struct Target
+  {
+    enum Type
+    {
+      NONE,
+      ARRAY_BUFFER = 34962,
+      ELEMENT_ARRAY_BUFFER = 34963
+    };
+
+    Target() = delete;
+  };
+
+  Ref<Buffer> mBuffer;
+  uint32_t mByteOffset = 0;
+  uint32_t mByteLength;
+  uint32_t mByteStride = 0;  // if 0 after reading, it needs to be calculated
+  uint32_t mTarget;
+  //TODO: extensions
+  //TODO: extras
+};
+
+struct BufferViewClient
+{
+  Ref<BufferView> mBufferView;
+  uint32_t mByteOffset = 0;
+};
+
+struct ComponentTypedBufferViewClient : BufferViewClient
+{
+  Component::Type mComponentType = Component::INVALID;
+
+  uint32_t GetBytesPerComponent() const;
+};
+
+struct Named
+{
+  std::string_view mName;
+
+protected:
+  Named() = default;
+};
+
+struct Accessor : ComponentTypedBufferViewClient, Named
+{
+  struct Sparse
+  {
+    uint32_t mCount;
+    ComponentTypedBufferViewClient mIndices;
+    BufferViewClient mValues;
+    //TODO: extensions
+    //TODO: extras
+  };
+
+  uint32_t mCount;
+  bool mNormalized = false;
+  AccessorType::Type mType = AccessorType::INVALID;
+  std::vector<float> mMin;
+  std::vector<float> mMax;
+  std::unique_ptr<Sparse> mSparse;
+  //TODO: extensions
+  //TODO: extras
+
+  uint32_t GetElementSizeBytes() const
+  {
+    return GetBytesPerComponent() * AccessorType::ElementCount(mType);
+  }
+
+  uint32_t GetBytesLength() const
+  {
+    return GetElementSizeBytes() * mCount;
+  }
+
+  void SetSparse(const Sparse& s)
+  {
+    mSparse.reset(new Sparse(s));
+  }
+};
+
+struct Image: Named
+{
+  std::string_view mUri;
+  std::string_view mMimeType;
+  Ref<BufferView> mBufferView;
+  //TODO: extensions
+  //TODO: extras
+};
+
+struct Filter
+{
+  enum Type
+  {
+    NEAREST = 9728,
+    LINEAR = 9729,
+    NEAREST_MIPMAP_NEAREST = 9984,
+    NEAREST_MIPMAP_LINEAR = 9985,
+    LINEAR_MIPMAP_NEAREST = 9986,
+    LINEAR_MIPMAP_LINEAR = 9987,
+  };
+
+  Filter() = delete;
+};
+
+struct Wrap
+{
+  enum Type
+  {
+    REPEAT = 10497,
+    CLAMP_TO_EDGE = 33071,
+    MIRRORED_REPEAT = 33648,
+  };
+
+  Wrap() = delete;
+};
+
+struct Sampler
+{
+  Filter::Type mMinFilter = Filter::LINEAR;
+  Filter::Type mMagFilter = Filter::LINEAR;
+  Wrap::Type mWrapS = Wrap::CLAMP_TO_EDGE;
+  Wrap::Type mWrapT = Wrap::CLAMP_TO_EDGE;
+  //TODO: extensions
+  //TODO: extras
+};
+
+struct Texture
+{
+  Ref<Image> mSource;
+  Ref<Sampler> mSampler;
+};
+
+struct TextureInfo
+{
+  Ref<gltf2::Texture> mTexture;
+  uint32_t mTexCoord = 0;
+  float mScale = 1.f;
+
+  operator bool() const
+  {
+    return !!mTexture;
+  }
+};
+
+struct Material: Named
+{
+  struct Pbr//MetallicRoughness
+  {
+    Dali::Vector4 mBaseColorFactor = Dali::Vector4::ONE;
+    TextureInfo mBaseColorTexture;
+    float mMetallicFactor = 1.f;
+    float mRoughnessFactor = 1.f;
+    TextureInfo mMetallicRoughnessTexture;
+    //TODO: extensions
+    //TODO: extras
+  };
+
+  Pbr mPbrMetallicRoughness;
+  TextureInfo mNormalTexture;
+  TextureInfo mOcclusionTexture;
+  TextureInfo mEmissiveTexture;
+  Dali::Vector3 mEmissiveFactor;
+  AlphaMode::Type mAlphaMode = AlphaMode::OPAQUE;
+  float mAlphaCutoff = .5f;
+  bool mDoubleSided = false;
+  //TODO: extensions
+  //TODO: extras
+};
+
+struct Mesh: Named
+{
+  struct Primitive
+  {
+    enum Mode
+    {
+      POINTS,
+      LINES,
+      LINE_LOOP,
+      LINE_STRIP,
+      TRIANGLES,
+      TRIANGLE_STRIP,
+      TRIANGLE_FAN,
+      INVALID
+    };
+
+    std::map<Attribute::Type, Ref<Accessor>> mAttributes;
+    std::vector<std::map<Attribute::Type, Ref<Accessor>>> mTargets;
+    Ref<Accessor> mIndices;
+    Ref<Material> mMaterial;
+    Mode mMode = TRIANGLES;
+
+    //TODO: [morph] targets
+    //TODO: extras
+    //TODO: extensions
+  };
+
+  std::vector<Primitive> mPrimitives;
+  std::vector<float> mWeights;
+  //TODO: extras
+  //TODO: extensions
+};
+
+struct Node;
+
+struct Skin : Named
+{
+  Ref<Accessor> mInverseBindMatrices;
+  Ref<Node> mSkeleton;
+  std::vector<Ref<Node>> mJoints;
+  //TODO: extras
+  //TODO: extensions
+};
+
+struct Camera: Named
+{
+  struct Perspective
+  {
+    float mAspectRatio;
+    float mYFov;
+    float mZFar;
+    float mZNear;
+    //TODO: extras
+    //TODO: extensions
+  };
+
+  struct Orthographic
+  {
+    float mXMag;
+    float mYMag;
+    float mZFar;
+    float mZNear;
+    //TODO: extras
+    //TODO: extensions
+  };
+
+  std::string_view mType;
+  Perspective mPerspective;
+  Orthographic mOrthographic;
+  //TODO: extras
+  //TODO: extensions
+};
+
+struct Node: Named
+{
+  Dali::Vector3 mTranslation = Dali::Vector3::ZERO;
+  Dali::Quaternion mRotation = Dali::Quaternion::IDENTITY;
+  Dali::Vector3 mScale = Dali::Vector3::ONE;
+
+  Ref<Camera> mCamera;
+  std::vector<Ref<Node>> mChildren;
+  Ref<Mesh> mMesh;
+
+  Ref<Skin> mSkin;
+  //TODO: [morph] weights
+  //TODO: extras
+  //TODO: extensions
+
+  void SetMatrix(const Dali::Matrix& m);
+};
+
+struct Animation : Named
+{
+  struct Sampler
+  {
+    struct Interpolation
+    {
+      enum Type
+      {
+        STEP,
+        LINEAR,
+        CUBICSPLINE,
+        INVALID
+      };
+      static Type FromString(const char* s, size_t len);
+    };
+
+    Ref<Accessor> mInput;
+    Ref<Accessor> mOutput;
+    Interpolation::Type mInterpolation;
+
+    //TODO: extras
+    //TODO: extensions
+  };
+
+  struct Channel
+  {
+    struct Target
+    {
+      enum Type
+      {
+        TRANSLATION,
+        ROTATION,
+        SCALE,
+        WEIGHTS,
+        INVALID
+      };
+
+      static Type FromString(const char* s, size_t len);
+
+      Ref<Node> mNode;
+      Type mPath;
+    };
+
+    Ref<Sampler> mSampler;
+    Target mTarget;
+    //TODO: extras
+    //TODO: extensions
+  };
+
+  std::vector<Sampler> mSamplers;
+  std::vector<Channel> mChannels;
+};
+
+struct Scene: Named
+{
+  std::vector<Ref<Node>> mNodes;
+};
+
+struct Document
+{
+  Asset mAsset;
+
+  std::vector<Buffer> mBuffers;
+  std::vector<BufferView> mBufferViews;
+  std::vector<Accessor> mAccessors;
+
+  std::vector<Image> mImages;
+  std::vector<Sampler> mSamplers;
+  std::vector<Texture> mTextures;
+  std::vector<Material> mMaterials;
+
+  std::vector<Mesh> mMeshes;
+  std::vector<Skin> mSkins;
+
+  std::vector<Camera> mCameras;
+  std::vector<Node> mNodes;
+
+  std::vector<Animation> mAnimations;
+
+  std::vector<Scene> mScenes;
+  Ref<Scene> mScene;
+
+  Document() = default;
+  Document(const Document&) = delete;
+  Document(Document&&) = default;
+
+  Document& operator=(const Document&) = delete;
+  Document& operator=(Document&&) = default;
+};
+
+/**
+ * @brief Provides a json::Property<T>::ReadFn for interpreting unsigned integers
+ *  as a Ref<U> into a std::vector<U> data member of a type T.
+ */
+template <typename T>
+struct RefReader
+{
+  static T* sObject;
+
+  template <typename U, std::vector<U> T::* V>
+  static Ref<U> Read(const json_value_s& j)
+  {
+    uint32_t index = json::Read::Number<uint32_t>(j);
+    return Ref<U>(sObject->*V, index);
+  }
+};
+
+template <typename T>
+T* RefReader<T>::sObject = nullptr;
+
+/**
+ * @brief Convenience method to set the object for RefReader.
+ */
+template <typename T>
+void SetRefReaderObject(T& object)
+{
+  RefReader<T>::sObject = &object;
+}
+
+/**
+ * @brief Reads a string and attempts to convert it to an enum.
+ * @note The enum must: 1, be called Type, nested to T, 2, provide a FromString static method taking a const char*
+ *  (string data) and a size_t (string length) and returning T::Type.
+ */
+template <typename T> // T must have a nested enum called Type and a static Type FromString(const char*) method.
+typename T::Type ReadStringEnum(const json_value_s& j)
+{
+  auto str = json::Read::StringView(j);
+
+  return T::FromString(str.data(), str.size());
+}
+
+/**
+ * @brief Convenience method to attempt to create a Dali vector type T from an array of floats.
+ * @note T must provide an AsFloat() member method returning the non-const array of its
+ *  float components.
+ */
+template <typename T>
+inline
+T ReadDaliVector(const json_value_s& j)
+{
+  std::vector<float> floats = json::Read::Array<float, json::Read::Number<float>>(j);
+  T result;
+  std::copy(floats.begin(), std::min(floats.end(), floats.begin() + sizeof(T) / sizeof(float)), result.AsFloat());
+  return result;
+}
+
+/**
+ * @brief Convenience method to attemt to read a Quaternion, which implicitly converts
+ *  to Vector4 but fails to provide an AsFloat() method.
+ */
+Dali::Quaternion ReadQuaternion(const json_value_s& j);
+
+}
+
+#endif //DALI_SCENE_LOADER_GLTF2_ASSET_H_
diff --git a/dali-scene-loader/internal/hash.cpp b/dali-scene-loader/internal/hash.cpp
new file mode 100644 (file)
index 0000000..e863f0a
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/internal/hash.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+Hash::Hash(uint64_t initial)
+: mValue(initial)
+{}
+
+Hash& Hash::Add(bool b)
+{
+  mValue = Concatenate(b ? 0 : 1);
+  return *this;
+}
+
+Hash& Hash::Add(int32_t i)
+{
+  mValue = Concatenate(i);
+  return *this;
+}
+
+Hash& Hash::Add(uint32_t i)
+{
+  mValue = Concatenate(i);
+  return *this;
+}
+
+Hash& Hash::Add(uint64_t i)
+{
+  mValue = Concatenate(i);
+  return *this;
+}
+
+Hash& Hash::Add(float f)
+{
+  return AddObjectBytes(f);
+}
+
+Hash& Hash::Add(const char * cStr)
+{
+  return Add(cStr, strlen(cStr));
+}
+
+Hash& Hash::Add(const char * cStr, size_t len)
+{
+  auto i0 = reinterpret_cast<const uint8_t*>(cStr);
+  return AddBytes(i0, i0 + len);
+}
+
+Hash& Hash::Add(const std::string & str)
+{
+  auto i0 = reinterpret_cast<const uint8_t*>(str.c_str());
+  return AddBytes(i0, i0 + str.size());
+}
+
+Hash& Hash::AddBytes(const uint8_t * i0, const uint8_t * i1)
+{
+  while (i0 != i1)
+  {
+    mValue = Concatenate(*i0);
+    ++i0;
+  }
+  return *this;
+}
+
+Hash::operator uint64_t() const
+{
+  return mValue;
+}
+
+uint64_t Hash::Concatenate(uint64_t value)
+{
+  return mValue * 31 + value;
+}
+
+}
+}
diff --git a/dali-scene-loader/internal/hash.h b/dali-scene-loader/internal/hash.h
new file mode 100644 (file)
index 0000000..32c69da
--- /dev/null
@@ -0,0 +1,125 @@
+#ifndef DALI_SCENE_LOADER_HASH_H_
+#define DALI_SCENE_LOADER_HASH_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <string>
+#include <cstring>
+#include <cstdint>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/**
+ * @brief Rudimentary hash generator that follows a builder pattern.
+ */
+class Hash
+{
+public:
+  static constexpr uint64_t DEFAULT_SEED = 61081;
+
+  explicit Hash(uint64_t initial = DEFAULT_SEED);
+
+  /**
+   * @brief Applies a boolean to the hash.
+   * @return Its updated self.
+   */
+  Hash& Add(bool b);
+
+  /**
+   * @brief Applies a signed 32-bit integer to the hash.
+   * @return Its updated self.
+   */
+  Hash& Add(int32_t i);
+
+  /**
+   * @brief Applies an unsigned 32-bit integer to the hash.
+   * @return Its updated self.
+   */
+  Hash& Add(uint32_t i);
+
+  /**
+   * @brief Applies an unsigned 64-bit integer to the hash.
+   * @return Its updated self.
+   */
+  Hash& Add(uint64_t i);
+
+  /**
+   * @brief Applies a float to the hash.
+   * @return Its updated self.
+   */
+  Hash& Add(float f);
+
+  /**
+   * @brief Applies a c-string to the hash.
+   * @return Its updated self.
+   */
+  Hash& Add(const char* cStr);
+
+  /**
+   * @brief Applies a c-string @a cStr of @a len characters, to the hash.
+   * @return Its updated self.
+   */
+  Hash& Add(const char* cStr, size_t len);
+
+  /**
+   * @brief Applies a string to the hash.
+   * @return Its updated self.
+   */
+  Hash& Add(const std::string& str);
+
+  /**
+   * @brief Applies a series of bytes between @a i0 and @a i1 to the hash.
+   * @return Its updated self.
+   */
+  Hash& AddBytes(const uint8_t* i0, const uint8_t* i1);
+
+  /**
+   * @brief Applies the bytes of an object @a value, to the hash.
+   * @return Its updated self.
+   */
+  template <typename T>
+  Hash& AddObjectBytes(const T& value);
+
+  operator uint64_t() const;
+
+private:
+  uint64_t mValue;
+
+  uint64_t Concatenate(uint64_t value);
+};
+
+
+template<typename T>
+Hash& Hash::AddObjectBytes(const T & value)
+{
+  auto i0 = reinterpret_cast<const uint8_t*>(&value);
+  auto i1 = i0 + sizeof(T);
+  while (i0 != i1)
+  {
+    mValue = Concatenate(*i0);
+    ++i0;
+  }
+  return *this;
+}
+
+}
+}
+
+#endif // DALI_SCENE_LOADER_HASH_H_
diff --git a/dali-scene-loader/internal/json-reader.cpp b/dali-scene-loader/internal/json-reader.cpp
new file mode 100644 (file)
index 0000000..6b12d1d
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+* Copyright (c) 2020 Samsung Electronics Co., Ltd.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+#include "dali-scene-loader/internal/json-reader.h"
+#include <algorithm>
+#include <cstring>
+
+namespace json
+{
+
+int StrCmp(const json_string_s& js, const char* s)
+{
+  auto sSize = strlen(s);
+  auto shorter = std::min(js.string_size, sSize);
+  auto base = strncmp(js.string, s, shorter);
+  return ((base != 0) || (sSize == js.string_size)) ? base : ((js.string_size < sSize) ?
+    -s[shorter] : js.string[shorter]);
+}
+
+int StrCmp(const json_string_s& js, const std::string& s)
+{
+  auto sSize = s.size();
+  auto shorter = std::min(js.string_size, sSize);
+  auto base = strncmp(js.string, s.c_str(), shorter);
+  return ((base != 0) || (sSize == js.string_size)) ? base : ((js.string_size < sSize) ?
+    -s[shorter] : js.string[shorter]);
+}
+
+void Validate(const json_value_s & jv, json_type_e type)
+{
+  if (jv.type != type)
+  {
+    throw std::runtime_error("Invalid type; expected: " + std::to_string(type) + ", got: " + std::to_string(jv.type));
+  }
+}
+
+json_value_s* FindObjectChild(const std::string& key, json_object_s& obj)
+{
+  auto i = obj.start;
+  while (i)
+  {
+    if (0 == StrCmp(*i->name, key))
+    {
+      return i->value;
+    }
+    i = i->next;
+  }
+  return nullptr;
+}
+
+}
diff --git a/dali-scene-loader/internal/json-reader.h b/dali-scene-loader/internal/json-reader.h
new file mode 100644 (file)
index 0000000..2f769a7
--- /dev/null
@@ -0,0 +1,439 @@
+#ifndef DALI_SCENE_LOADER_JSON_READER_H_
+#define DALI_SCENE_LOADER_JSON_READER_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/third-party/json.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/common/vector-wrapper.h"
+#include <algorithm>
+#include <map>
+#include <string_view>
+#include <sstream>
+#include <cstring>
+#include <memory>
+
+namespace json
+{
+
+/**
+ * @brief Helper for freeing the memory allocated by json_parse()
+ */
+struct json_value_deleter
+{
+  void operator()(json_value_s* p) const
+  {
+    free(p);
+  }
+};
+typedef std::unique_ptr<json_value_s, json_value_deleter> unique_ptr;
+
+/**
+ * @brief Case sensitive comparison of json_string_s and c-string.
+ * @return difference in first different character, or 0 if the strings are identical.
+ */
+int StrCmp(const json_string_s& js, const char* s);
+
+/**
+ * @brief Case sensitive comparison of json_string_s and std::string.
+ * @return difference in first different character, or 0 if the strings are identical.
+ */
+int StrCmp(const json_string_s& js, const std::string& s);
+
+/**
+ * @brief Convenience function to compare json_string_s and other supported string type,
+ *  in swapped order.
+ */
+template <typename String>
+inline
+int StrCmp(String& s, const json_string_s& js)
+{
+  return -StrCmp(js, s);
+}
+
+/**
+ * @brief Checks @a jv to be the given @a type, otherwise a std::runtime_error is thrown.
+ */
+void Validate(const json_value_s& jv, json_type_e type);
+
+namespace detail
+{
+/**
+ * @brief Compile-time type-enum mapping.
+ */
+template <typename T>
+struct Type2Enum
+{};
+
+#define TYPE2ENUM(x) template<> struct Type2Enum<json_## x ##_s>\
+{\
+  enum { VALUE = json_type_## x };\
+};
+
+TYPE2ENUM(object)
+TYPE2ENUM(array)
+TYPE2ENUM(string)
+TYPE2ENUM(number)
+#undef TYPE2ENUM
+}
+
+/**
+ * @brief Casts the payload of a json_value_s to the given type.
+ */
+template <typename Out>
+inline
+const Out& Cast(const json_value_s& j)
+{
+  Validate(j, static_cast<json_type_e>(detail::Type2Enum<typename std::decay<Out>::type>::VALUE));
+  return *static_cast<const Out*>(j.payload);
+}
+
+/**
+ * @brief Casts the payload of a json_value_s to the given type.
+ * @note std::runtime_error is thrown if the value is not the given type.
+ */
+template <typename Out>
+inline
+Out& Cast(json_value_s& j)
+{
+  Validate(j, static_cast<json_type_e>(detail::Type2Enum<typename std::decay<Out>::type>::VALUE));
+  return *static_cast<Out*>(j.payload);
+}
+
+/**
+ * @brief Helper function to find a child element of @a obj mapped to @a key.
+ * @return Pointer to the element, or nullptr if it could not be found.
+ */
+json_value_s* FindObjectChild(const std::string& key, json_object_s& obj);
+
+/**
+ * @brief Helper functions for reading various value types.
+ */
+struct Read
+{
+  static bool Boolean(const json_value_s& j)
+  {
+    if (j.type == json_type_true)
+    {
+      return true;
+    }
+    else if (j.type == json_type_false)
+    {
+      return false;
+    }
+    else // try to interpret a numeric value.
+    {
+      return Number<int>(j) != 0;
+    }
+  }
+
+  template <typename T>
+  static T Number(const json_value_s& j)
+  {
+    auto& jn = Cast<const json_number_s>(j);
+    std::stringstream ss;
+    for (auto i0 = jn.number, i1 = i0 + jn.number_size; i0 != i1; ++i0)
+    {
+      ss.put(*i0);
+    }
+
+    T result;
+    if (ss >> result)
+    {
+      return result;
+    }
+    throw std::runtime_error("Failed to convert value to number");
+  }
+
+  template <typename E>
+  static E Enum(const json_value_s& j)
+  {
+    size_t number = Number<size_t>(j);
+    return static_cast<E>(number);
+  }
+
+  static std::string_view StringView(const json_value_s& j)
+  {
+    auto& js = Cast<json_string_s>(j);
+    return std::string_view(js.string, js.string_size);
+  }
+
+  static std::string String(const json_value_s& j)
+  {
+    auto& js = Cast<const json_string_s>(j);
+    return std::string(js.string, js.string_size);
+  }
+
+  template <typename T, T(*readElement)(const json_value_s&)>
+  static std::vector<T> Array(const json_value_s& j)
+  {
+    auto& ja = Cast<const json_array_s>(j);
+    std::vector<T> result;
+    result.reserve(ja.length);
+    auto i = ja.start;
+    while (i)
+    {
+      result.push_back(std::move(readElement(*i->value)));
+      i = i->next;
+    }
+    return result;
+  }
+};
+
+/**
+ * @brief Core class for object properties.
+ */
+struct PropertyCore
+{
+protected:
+  explicit PropertyCore(const std::string& key)
+  : mKey(key)
+  {}
+
+  const std::string& GetKey() const { return mKey; }
+
+private:
+  std::string mKey;
+};
+
+/**
+ * @brief Base class for the properties of a type T.
+ */
+template <typename T>
+struct PropertyBase : PropertyCore
+{
+  using PropertyCore::GetKey;
+
+  explicit PropertyBase(const std::string& key)
+  : PropertyCore(key)
+  {}
+
+  virtual ~PropertyBase()
+  {}
+
+  virtual void Read(const json_value_s& j, T& obj) = 0;
+};
+
+/**
+ * @brief Concrete property of an object to read into from JSON with a given function.
+ */
+template <class T, typename U>
+struct Property : PropertyBase<T>
+{
+  using ReadFn = U(*)(const json_value_s&);
+  using MemberPtr = U T::*;
+  using SetterArgType = typename std::conditional<sizeof(U) <= sizeof(uintptr_t), U, const U&>::type;
+  using Setter = void (T::*)(SetterArgType);
+
+  Property(const std::string& key, ReadFn read, MemberPtr ptr)
+  : PropertyBase<T>(key),
+    mRead(read),
+    mAccessor(new DirectAccessor(ptr))
+  {}
+
+  Property(const std::string& key, ReadFn read, Setter setter)
+  : PropertyBase<T>(key),
+    mRead(read),
+    mAccessor(new SetterAccessor(setter))
+  {}
+
+  ~Property()
+  {}
+
+  void Read(const json_value_s& j, T& obj) override
+  {
+    mAccessor->Set(mRead(j), obj);
+  }
+
+private:
+  struct AccessorBase
+  {
+    virtual ~AccessorBase()
+    {}
+
+    virtual void Set(U value, T& obj) const = 0;
+  };
+
+  struct DirectAccessor : AccessorBase
+  {
+    DirectAccessor(MemberPtr ptr)
+    : mPointer(ptr)
+    {}
+
+    void Set(U value, T& obj) const override
+    {
+      obj.*mPointer = std::move(value);
+    }
+
+    MemberPtr mPointer;
+  };
+
+  struct SetterAccessor : AccessorBase
+  {
+    SetterAccessor(Setter setter)
+    : mSetter(setter)
+    {}
+
+    void Set(U value, T& obj) const override
+    {
+      (obj.*mSetter)(value);
+    }
+
+    Setter mSetter;
+  };
+
+  ReadFn mRead;
+  std::unique_ptr<AccessorBase> mAccessor;
+};
+
+/**
+ * @brief Helper function to make a Property for a member of type U, of object of type T.
+ */
+template <class T, typename U>
+Property<T, U>* MakeProperty(const std::string& key, typename Property<T, U>::ReadFn readFn,
+  U T::* ptr)
+{
+  return new Property<T, U>(key, readFn, ptr);
+}
+
+/**
+ * @brief Core class for an object Reader.
+ */
+struct ReaderCore
+{
+protected:
+  std::vector<void*> mProperties;
+
+  ReaderCore() = default;
+
+  ReaderCore(const ReaderCore& other) = delete;
+  ReaderCore& operator=(const ReaderCore& other) = delete;
+
+  ReaderCore(ReaderCore&& other) = default;
+  ReaderCore& operator=(ReaderCore&& other) = default;
+};
+
+/**
+ * @brief Object Reader template for reading into an object of a given type,
+ *  with properties registered for the various members.
+ */
+template <typename T>
+class Reader : protected ReaderCore
+{
+public:
+  Reader() = default;
+
+  Reader(const Reader<T>& other) = delete;
+  Reader<T>& operator=(const Reader<T>& other) = delete;
+
+  Reader(Reader<T>&& other) = default;
+  Reader<T>& operator=(Reader&& other) = default;
+
+  ~Reader()
+  {
+    for (auto& p : mProperties)
+    {
+      delete Cast(p);
+    }
+  }
+
+  Reader<T>& Register(PropertyBase<T>& prop)
+  {
+    auto iInsert = std::lower_bound(mProperties.begin(), mProperties.end(), &prop,
+      SortPredicate);
+    if (iInsert == mProperties.end() || Cast(*iInsert)->GetKey() != prop.GetKey())
+    {
+      mProperties.insert(iInsert, &prop);
+    }
+    else
+    {
+      delete Cast(*iInsert);
+      *iInsert = &prop;
+    }
+    return *this;
+  }
+
+  void Read(const json_object_s& jo, T& obj) const
+  {
+    auto i = jo.start;
+    while (i)
+    {
+      auto iFind = std::lower_bound(mProperties.begin(), mProperties.end(),
+        *i->name, FindPredicate);
+      if (iFind != mProperties.end())
+      {
+        auto prop = Cast(*iFind);
+        if (0 == StrCmp(*i->name, prop->GetKey()))
+        {
+          prop->Read(*i->value, obj);
+        }
+      }
+      i = i->next;
+    }
+  }
+
+private:
+  static inline PropertyBase<T>* Cast(void* p)
+  {
+    return static_cast<PropertyBase<T>*>(p);
+  }
+
+  static bool SortPredicate(void* p, PropertyBase<T>* prop)
+  {
+    const auto prop0 = Cast(p);
+    return prop0->GetKey().compare(prop->GetKey()) < 0;
+  }
+
+  static bool FindPredicate(void* p, const json_string_s& key)
+  {
+    const auto prop = Cast(p);
+    return StrCmp(prop->GetKey(), key) < 0;
+  }
+};
+
+/**
+ * @brief Wraps a Reader<T> in a function usable as a Property<T>::ReadFn, i.e. to facilitate
+ *  deserializing structures of nested objects.
+ */
+template <typename T>
+struct ObjectReader
+{
+  static const Reader<T>* sReader;
+
+  static T Read(const json_value_s& j)
+  {
+    T result;
+    auto& jo = Cast<json_object_s>(j);
+    sReader->Read(jo, result);
+    return result;
+  }
+};
+
+template <typename T>
+const Reader<T>* ObjectReader<T>::sReader = nullptr;
+
+template <typename T>
+void SetObjectReader(const Reader<T>& r)
+{
+  ObjectReader<T>::sReader = &r;
+}
+
+} // json
+
+#endif //DALI_SCENE_LOADER_JSON_READER_H_
diff --git a/dali-scene-loader/internal/json-util.cpp b/dali-scene-loader/internal/json-util.cpp
new file mode 100644 (file)
index 0000000..613a764
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/internal/json-util.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/common/extents.h"
+#include "dali/public-api/math/matrix3.h"
+#include "dali/public-api/math/matrix.h"
+#include "dali/public-api/math/quaternion.h"
+#include "dali/public-api/math/radian.h"
+#include "dali/public-api/math/vector2.h"
+#include "dali/public-api/math/vector3.h"
+#include "dali/public-api/object/property-value.h"
+#include "dali/devel-api/common/map-wrapper.h"
+#include <array>
+
+namespace Dali
+{
+using namespace Toolkit;
+
+namespace SceneLoader
+{
+namespace
+{
+
+template <typename T>
+Property::Value ReadPrimitiveHelper(const TreeNode* tn, bool(*reader)(const TreeNode*, T&))
+{
+  T value;
+  if (reader(tn, value))
+  {
+    return Property::Value(value);
+  }
+  return Property::Value();
+}
+
+template <typename T>
+Property::Value ReadVectorHelper(const TreeNode* tn)
+{
+  static_assert(sizeof(T) % sizeof(float) == 0, "");
+  T value;
+  if (ReadVector(tn, value.AsFloat(), sizeof(T) / sizeof(float)))
+  {
+    return Property::Value(value);
+  }
+  return Property::Value();
+}
+
+Property::Value ReadVectorSingleFloatHelper(const TreeNode* tn)
+{
+  float value;
+  if (ReadVector(tn, &value, 1u))
+  {
+    return Property::Value(value);
+  }
+  return Property::Value();
+}
+
+Property::Value ReadRotationHelper(const TreeNode* tn)
+{
+  switch (tn->Size())
+  {
+  case 3:
+  {
+    // degrees as per spec
+    Vector3 rotation;
+    ReadVector(tn, rotation.AsFloat(), 3u);
+    return Property::Value(Quaternion(Radian(Degree(rotation.x)),
+      Radian(Degree(rotation.y)),
+      Radian(Degree(rotation.z))));
+  }
+  case 4:
+  {
+    Vector4 v;
+    ReadVector(tn, v.AsFloat(), 4u);
+    //Quaternion
+    return Property::Value(Quaternion(v));
+  }
+  default:
+    return Property::Value();
+  }
+}
+
+template <typename T>
+bool ReadQuadHelper(const TreeNode* tn, const std::array<T*, 4>& quad)
+{
+  auto i = quad.begin();
+  auto iEnd = quad.end();
+  auto iJson = tn->CBegin();
+  while (iJson != tn->CEnd() && i != iEnd)
+  {
+    int value;
+    if (ReadInt(&(*iJson).second, value) && value <= std::numeric_limits<T>::max())
+    {
+      **i = value;
+      ++i;
+      ++iJson;
+    }
+    else
+    {
+      return false;
+    }
+  }
+  return true;
+}
+
+const std::map<std::string, Property::Value(*)(const TreeNode*)> kTypeIds {
+  // NONE
+  { "boolean", [](const TreeNode* tn) {
+    return ReadPrimitiveHelper<bool>(tn, ReadBool);
+  }},
+  { "float", [](const TreeNode* tn) {
+    return ReadPrimitiveHelper<float>(tn, ReadFloat);
+  }},
+  { "integer", [](const TreeNode* tn) {
+    return ReadPrimitiveHelper<int>(tn, ReadInt);
+  }},
+  { "vector2", ReadVectorHelper<Vector2> },
+  { "vector3", ReadVectorHelper<Vector3> },
+  { "vector4", ReadVectorHelper<Vector4> },
+  { "matrix3", ReadVectorHelper<Matrix3> },
+  { "matrix", ReadVectorHelper<Matrix> },
+  { "rectangle", [](const TreeNode* tn) {
+    Rect<int> value;
+    if (ReadQuadHelper<int>(tn, { &value.x, &value.y, &value.width, &value.height }))
+    {
+      return Property::Value(value);
+    }
+    return Property::Value();
+  }},
+  { "rotation", ReadRotationHelper },
+  // STRING - not particularly animatable
+  // ARRAY - not particularly animatable
+  // MAP - not particularly animatable
+  { "extents", [](const TreeNode* tn) {
+    Extents value;
+    if (ReadQuadHelper<uint16_t>(tn, { &value.start, &value.end, &value.top, &value.bottom }))
+    {
+      return Property::Value(value);
+    }
+    return Property::Value();
+  }},
+};
+
+Property::Value(* const kArrayPropertyProcessors[])(const TreeNode*) {
+  ReadVectorHelper<Matrix>,
+  ReadVectorHelper<Matrix3>,
+  ReadVectorHelper<Vector4>,
+  ReadVectorHelper<Vector3>,
+  ReadVectorHelper<Vector2>,
+  ReadVectorSingleFloatHelper
+};
+
+}  // nonamespace
+
+bool ReadBool(const TreeNode* node, bool& num)
+{
+  if (!node)
+  {
+    return false;
+  }
+
+  bool returnValue = false;
+  if (node->GetType() == TreeNode::BOOLEAN)
+  {
+    num = node->GetBoolean();
+    returnValue = true;
+  }
+
+  return returnValue;
+}
+
+bool ReadInt(const TreeNode* node, int& num)
+{
+  if (!node)
+  {
+    return false;
+  }
+
+  bool returnValue = false;
+  if (node->GetType() == TreeNode::INTEGER)
+  {
+    num = node->GetInteger();
+    returnValue = true;
+  }
+  else if (node->GetType() == TreeNode::FLOAT)
+  {
+    num = static_cast<int>(node->GetFloat());
+    returnValue = true;
+  }
+
+  return returnValue;
+}
+
+bool ReadFloat(const TreeNode* node, float& num)
+{
+  if (!node)
+  {
+    return false;
+  }
+
+  bool returnValue = false;
+  if (node->GetType() == TreeNode::FLOAT)
+  {
+    num = node->GetFloat();
+    returnValue = true;
+  }
+  else if (node->GetType() == TreeNode::INTEGER)
+  {
+    num = static_cast<float>(node->GetInteger());
+    returnValue = true;
+  }
+
+  return returnValue;
+}
+
+bool ReadIndex(const Toolkit::TreeNode* node, Index& num)
+{
+  bool returnValue = node && node->GetType() == TreeNode::INTEGER;
+  if (returnValue)
+  {
+    num = static_cast<Index>(node->GetInteger());
+  }
+
+  return returnValue;
+}
+
+bool ReadBlob(const Toolkit::TreeNode* node, unsigned int& offset, unsigned int& length)
+{
+  if (!node)
+  {
+    return false;
+  }
+
+  int iOffset, iLength;
+  bool success = ReadInt(node->GetChild("byteOffset"), iOffset) &&
+    ReadInt(node->GetChild("byteLength"), iLength) &&
+    iOffset >= 0 && iLength >= 0;  // 0 length might not be sensible, but is not an error at this stage.
+  if (success)
+  {
+    offset = static_cast<unsigned int>( iOffset );
+    length = static_cast<unsigned int>( iLength );
+  }
+  return success;
+}
+
+size_t GetNumericalArraySize(const TreeNode* node)
+{
+  size_t size = 0;
+  if (node->GetType() == TreeNode::ARRAY)
+  {
+    for (auto i0 = node->CBegin(), i1 = node->CEnd(); i0 != i1 &&
+      ((*i0).second.GetType() == TreeNode::FLOAT || (*i0).second.GetType() == TreeNode::INTEGER);
+      ++i0)
+    {
+      ++size;
+    }
+  }
+  return size;
+}
+
+bool ReadVector(const TreeNode* node, float* num, unsigned int size)
+{
+  if (!node)
+  {
+    return false;
+  }
+
+  bool returnValue = false;
+  if ((node->Size() >= size) && (node->GetType() == TreeNode::ARRAY))
+  {
+    unsigned int offset = 0u;
+    for (TreeNode::ConstIterator it = node->CBegin(); offset < size; ++it, ++offset)
+    {
+      const TreeNode& coord = (*it).second;
+      if (!ReadFloat(&coord, *(num + offset)))
+      {
+        return false;
+      }
+    }
+    returnValue = true;
+  }
+
+  return returnValue;
+}
+
+bool ReadVector(const Toolkit::TreeNode* node, int* num, unsigned int size)
+{
+  if (!node)
+  {
+    return false;
+  }
+
+  bool returnValue = false;
+  if ((node->Size() >= size) && (node->GetType() == TreeNode::ARRAY))
+  {
+    unsigned int offset = 0u;
+    for (TreeNode::ConstIterator it = node->CBegin(); offset < size; ++it, ++offset)
+    {
+      const TreeNode& coord = (*it).second;
+      if (!ReadInt(&coord, *(num + offset)))
+      {
+        return false;
+      }
+    }
+    returnValue = true;
+  }
+
+  return returnValue;
+}
+
+bool ReadColor(const TreeNode* node, Vector4& color)
+{
+  if (nullptr == node)
+  {
+    return false;
+  }
+
+  if (!ReadVector(node, color.AsFloat(), 4))
+  {
+    if (!ReadVector(node, color.AsFloat(), 3))
+    {
+      return false;
+    }
+    color.a = 1.f;
+  }
+
+  return true;
+}
+
+bool ReadTimePeriod(const TreeNode* node, TimePeriod& timePeriod)
+{
+  if (!node)
+  {
+    return false;
+  }
+
+  if (!ReadFloat(node->GetChild("delay"), timePeriod.delaySeconds) || !ReadFloat(node->GetChild("duration"), timePeriod.durationSeconds))
+  {
+    return false;
+  }
+  return true;
+}
+
+bool ReadString(const TreeNode* node, std::string& strValue)
+{
+  if (!node)
+  {
+    return false;
+  }
+
+  bool returnValue = false;
+  if (node->GetType() == TreeNode::STRING)
+  {
+    strValue = node->GetString();
+    returnValue = true;
+  }
+  return returnValue;
+}
+
+bool ReadStringVector(const TreeNode* node, std::vector<std::string>& strvector)
+{
+  if (!node)
+  {
+    return false;
+  }
+
+  bool returnValue = false;
+  if (node->GetType() == TreeNode::ARRAY)
+  {
+    for (TreeNode::ConstIterator it = node->CBegin(); it != node->CEnd(); ++it)
+    {
+      const TreeNode& strNode = (*it).second;
+      if (strNode.GetType() == TreeNode::STRING)
+      {
+        strvector.push_back(strNode.GetString());
+      }
+      else
+      {
+        return false;
+      }
+    }
+    returnValue = true;
+  }
+  return returnValue;
+}
+
+Property::Value ReadPropertyValue(const Property::Type& propType, const TreeNode& tn)
+{
+  switch (propType)
+  {
+  case Property::BOOLEAN:
+    return ReadPrimitiveHelper<bool>(&tn, ReadBool);
+
+  case Property::FLOAT:
+    return ReadPrimitiveHelper<float>(&tn, ReadFloat);
+
+  case Property::INTEGER:
+    return ReadPrimitiveHelper<int>(&tn, ReadInt);
+
+  case Property::VECTOR2:
+    return ReadVectorHelper<Vector2>(&tn);
+
+  case Property::VECTOR3:
+    return ReadVectorHelper<Vector3>(&tn);
+
+  case Property::VECTOR4:
+    return ReadVectorHelper<Vector4>(&tn);
+
+  case Property::MATRIX3:
+    return ReadVectorHelper<Matrix3>(&tn);
+
+  case Property::MATRIX:
+    return ReadVectorHelper<Matrix>(&tn);
+
+  case Property::RECTANGLE:
+  {
+    Rect<int> value;
+    if (ReadQuadHelper<int>(&tn, { &value.x, &value.y, &value.width, &value.height }))
+    {
+      return Property::Value(value);
+    }
+    break;
+  }
+
+  case Property::ROTATION:
+    return ReadRotationHelper(&tn);
+
+  case Property::EXTENTS:
+  {
+    Extents value;
+    if (ReadQuadHelper<uint16_t>(&tn, { &value.start, &value.end, &value.top, &value.bottom }))
+    {
+      return Property::Value(value);
+    }
+    break;
+  }
+
+  case Property::NONE: // fall
+  default:
+  {
+    DALI_ASSERT_ALWAYS(!"Property type incorrect");
+  }
+  }
+  return Property::Value();
+}
+
+Property::Value ReadPropertyValue(const Toolkit::TreeNode& tn)
+{
+  Property::Value propValue;
+  if (tn.GetType() == TreeNode::OBJECT)  // attempt to disambiguate type.
+  {
+    auto jsonType = tn.GetChild("type");
+    if (jsonType && jsonType->GetType() == TreeNode::STRING)
+    {
+      auto iFind = kTypeIds.find(jsonType->GetString());
+      if (iFind != kTypeIds.end())
+      {
+        propValue = iFind->second(tn.GetChild("value"));
+      }
+    }
+  }
+
+  if (propValue.GetType() == Property::NONE)
+  {
+    if (tn.Size() == 0)
+    {
+      switch (tn.GetType())
+      {
+      case TreeNode::BOOLEAN:
+        propValue = ReadPrimitiveHelper<bool>(&tn, ReadBool);
+        break;
+
+      case TreeNode::INTEGER:
+        propValue = ReadPrimitiveHelper<int>(&tn, ReadInt);
+        break;
+
+      case TreeNode::FLOAT:
+        propValue = ReadPrimitiveHelper<float>(&tn, ReadFloat);
+        break;
+
+      default:
+        break;
+      }
+    }
+    else
+    {
+      bool allNumbers = true;
+      for (auto i0 = tn.CBegin(), i1 = tn.CEnd(); i0 != i1; ++i0)
+      {
+        auto type = (*i0).second.GetType();
+        if(!(type == TreeNode::FLOAT || type == TreeNode::INTEGER))
+        {
+          allNumbers = false;
+          break;
+        }
+      }
+
+      if (allNumbers)
+      {
+        // NOTE: rotations / rectangles / extents must be disambiguated in all circumstances.
+        for (auto& r : kArrayPropertyProcessors)
+        {
+          propValue = r(&tn);
+          if (propValue.GetType() != Property::NONE)
+          {
+            break;
+          }
+        }
+      }
+    }
+  }
+  return propValue;
+}
+
+}
+}
diff --git a/dali-scene-loader/internal/json-util.h b/dali-scene-loader/internal/json-util.h
new file mode 100644 (file)
index 0000000..64a99cd
--- /dev/null
@@ -0,0 +1,95 @@
+#ifndef DALI_SCENE_LOADER_JSON_UTIL_H_
+#define DALI_SCENE_LOADER_JSON_UTIL_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/index.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/common/vector-wrapper.h"
+#include "dali/public-api/animation/time-period.h"
+#include "dali/public-api/object/property.h"
+#include "dali/public-api/math/vector4.h"
+#include "dali-toolkit/devel-api/builder/tree-node.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+bool ReadBool(const Toolkit::TreeNode* node, bool& num);
+
+bool ReadInt(const Toolkit::TreeNode* node, int& num);
+
+bool ReadFloat(const Toolkit::TreeNode* node, float& num);
+
+bool ReadIndex(const Toolkit::TreeNode* node, Index& num);
+
+bool ReadBlob(const Toolkit::TreeNode* node, unsigned int& offset, unsigned int& length);
+
+/**
+ * @brief Gets the number of numerical element of a JSON array;
+ * @return 0 if not an array, otherwise the number of float or integer elements
+ *  at the front of the array.
+ */
+size_t GetNumericalArraySize(const Toolkit::TreeNode* node);
+
+bool ReadVector(const Toolkit::TreeNode* node, float* num, unsigned int size);
+
+bool ReadVector(const Toolkit::TreeNode* node, int* num, unsigned int size);
+
+/**
+ * @brief Reads a color.
+ * 
+ * The node contents could be a vector of 4 floats [r,g,b,a].
+ * 
+ * @param[in] node The tree node with the color.
+ * @param[out] color The RGBA color.
+ * 
+ * @return true if succedded to read the color.
+ */
+bool ReadColor(const Toolkit::TreeNode* node, Vector4& color);
+
+bool ReadTimePeriod(const Toolkit::TreeNode* node, TimePeriod& timePeriod);
+
+bool ReadString(const Toolkit::TreeNode* node, std::string& strValue);
+
+bool ReadStringVector(const Toolkit::TreeNode* node, std::vector<std::string>& strvector);
+
+/**
+ * @brief Attempts to read a property of the given type from the given JSON node.
+ * @return The property value that it could interpret. If unsuccessful, its type will be NONE.
+ * @note Currently only numerical types are supported (including boolean).
+ */
+Property::Value ReadPropertyValue(const Property::Type& propType, const Toolkit::TreeNode& tn);
+
+/**
+ * @brief Attempts to read a property, whose type it will attempt to determine from the given
+ *  JSON node.
+ * @return The property value that it could interpret. If unsuccessful, its type will be NONE.
+ * @note Currently only numerical types are supported (including boolean).
+ * @note Supports a disambiguation syntax, whereby the type can be specified explicitly:
+ *  { "type": "rotation", "value": [...] } .
+ * @note: rotation / rectangle / extents type properties must be disambiguated in all circumstances.
+ */
+Property::Value ReadPropertyValue(const Toolkit::TreeNode& tn);
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_JSON_UTIL_H_
diff --git a/dali-scene-loader/public-api/alpha-function-helper.cpp b/dali-scene-loader/public-api/alpha-function-helper.cpp
new file mode 100644 (file)
index 0000000..33ad3db
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/public-api/alpha-function-helper.h"
+#include <unordered_map>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+namespace
+{
+
+#define DALI_ALPHA_FUNCTION_ENTRY(x) { #x, AlphaFunction::x }
+
+std::unordered_map<std::string, AlphaFunction> sFunctions {
+  DALI_ALPHA_FUNCTION_ENTRY(DEFAULT),
+  DALI_ALPHA_FUNCTION_ENTRY(LINEAR),
+  DALI_ALPHA_FUNCTION_ENTRY(REVERSE),
+  DALI_ALPHA_FUNCTION_ENTRY(EASE_IN_SQUARE),
+  DALI_ALPHA_FUNCTION_ENTRY(EASE_OUT_SQUARE),
+  DALI_ALPHA_FUNCTION_ENTRY(EASE_IN),
+  DALI_ALPHA_FUNCTION_ENTRY(EASE_OUT),
+  DALI_ALPHA_FUNCTION_ENTRY(EASE_IN_OUT),
+  DALI_ALPHA_FUNCTION_ENTRY(EASE_IN_SINE),
+  DALI_ALPHA_FUNCTION_ENTRY(EASE_OUT_SINE),
+  DALI_ALPHA_FUNCTION_ENTRY(EASE_IN_OUT_SINE),
+  DALI_ALPHA_FUNCTION_ENTRY(BOUNCE),
+  DALI_ALPHA_FUNCTION_ENTRY(SIN),
+  DALI_ALPHA_FUNCTION_ENTRY(EASE_OUT_BACK),
+};
+
+#undef DALI_ALPHA_FUNCTION_ENTRY
+
+} // nonamespace
+
+AlphaFunction GetAlphaFunction(const std::string& name, bool* found)
+{
+  auto iFind = sFunctions.find(name);
+  bool success = iFind != sFunctions.end();
+  if (found)
+  {
+    *found = success;
+  }
+  return success ? iFind->second : AlphaFunction(AlphaFunction::DEFAULT);
+}
+
+void RegisterAlphaFunction(const std::string & name, AlphaFunction alphaFn)
+{
+  DALI_ASSERT_ALWAYS(sFunctions.insert({ name, alphaFn }).second &&
+    "Function with given key already exists.");
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/alpha-function-helper.h b/dali-scene-loader/public-api/alpha-function-helper.h
new file mode 100644 (file)
index 0000000..1a6c9ad
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef DALI_SCENE_LOADER_ALPHA_FUNCTION_HELPER_H_
+#define DALI_SCENE_LOADER_ALPHA_FUNCTION_HELPER_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "dali-scene-loader/public-api/api.h"
+
+#include "dali/public-api/animation/alpha-function.h"
+#include <string>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/**
+ * @return Given a name, provide a AlphaFunction; if the name was not
+ *  recognised, get the default one.
+ */
+AlphaFunction DALI_SCENE_LOADER_API GetAlphaFunction(const std::string& name, bool* found = nullptr);
+
+/**
+ * @brief Registers an alpha function only if one with the same @a name has
+ *  not yet been registered. Throws Exception the name isn't unique.
+ */
+void DALI_SCENE_LOADER_API RegisterAlphaFunction(const std::string& name, AlphaFunction alphaFn) noexcept(false);
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_ALPHA_FUNCTION_HELPER_H_
diff --git a/dali-scene-loader/public-api/animated-property.cpp b/dali-scene-loader/public-api/animated-property.cpp
new file mode 100644 (file)
index 0000000..1e8a592
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/public-api/animated-property.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+void AnimatedProperty::Animate(Animation& anim, GetActor getActor)
+{
+  if (Actor actor = getActor(mNodeName))
+  {
+    Property prop = GetProperty(actor);
+    if (mKeyFrames)
+    {
+      anim.AnimateBetween(prop, mKeyFrames, mAlphaFunction, mTimePeriod);
+    }
+    else if (mValue)
+    {
+      if (mValue->mIsRelative)
+      {
+        anim.AnimateBy(prop, mValue->mValue, mAlphaFunction, mTimePeriod);
+      }
+      else
+      {
+        anim.AnimateTo(prop, mValue->mValue, mAlphaFunction, mTimePeriod);
+      }
+    }
+  }
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/animated-property.h b/dali-scene-loader/public-api/animated-property.h
new file mode 100644 (file)
index 0000000..026b68a
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef DALI_SCENE_LOADER_ANIMATED_PROPERTY_H
+#define DALI_SCENE_LOADER_ANIMATED_PROPERTY_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/actors/actor.h"
+#include "dali/public-api/animation/animation.h"
+#include "dali/public-api/object/property.h"
+#include <memory>
+#include <functional>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/**
+ * @brief Intermediate representation for a property that's given to
+ *  a Animation to animate. Since there is no getting an
+ *  animated property back from the Animation (i.e. past AnimateBetween/By/To()),
+ *  changing properties (e.g. from the SDK) requires the whole Animation
+ *  object to be recreated with all of its properties (incl. modifications).
+ */
+struct DALI_SCENE_LOADER_API AnimatedProperty
+{
+public: // METHODS
+  /**
+   * @brief Function to obtain an Actor based on its name. Its processing will
+   *  ignore empty handles returned by it.
+   */
+  using GetActor = std::function<Actor(const std::string&)>;
+
+  /**
+   * @return The Property object (of the given @a actor) whose value is being animated.
+   */
+  Property GetProperty(Actor& actor)
+  {
+    auto idx = actor.GetPropertyIndex(mPropertyName);
+    return Property(actor, idx);
+  }
+
+  /**
+   * @brief The type of the Property (of the given @a actor) that is being animated.
+   */
+  Property::Type GetPropertyType(Actor& actor)
+  {
+    auto idx = actor.GetPropertyIndex(mPropertyName);
+    return actor.GetPropertyType(idx);
+  }
+
+  /**
+   * @brief Registers the animation of this property against the given @a anim.
+   *  @a getActor will be used to obtain the Actor named by this property.
+   *  Failing to find the actor purely means that this property will not be
+   *  animated.
+   */
+  void Animate(Animation& anim, GetActor getActor);
+
+public: // DATA
+  struct Value
+  {
+    Property::Value mValue;
+    bool mIsRelative;
+  };
+
+  std::string mNodeName;
+  std::string mPropertyName;
+
+  KeyFrames mKeyFrames;
+  std::unique_ptr<Value> mValue;
+
+  AlphaFunction mAlphaFunction = AlphaFunction::DEFAULT;
+  TimePeriod mTimePeriod = TimePeriod(0.f);
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_ANIMATED_PROPERTY_H
diff --git a/dali-scene-loader/public-api/animation-definition.cpp b/dali-scene-loader/public-api/animation-definition.cpp
new file mode 100644 (file)
index 0000000..ee2b519
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/public-api/animation-definition.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+const float AnimationDefinition::DEFAULT_DURATION_SECONDS = 1.f;
+const float AnimationDefinition::MIN_DURATION_SECONDS = 1e-2f;
+
+Animation::EndAction AnimationDefinition::StopForModification(Animation& anim)
+{
+  const auto endAction = anim.GetEndAction();
+  anim.SetEndAction(Animation::DISCARD);
+  anim.Stop();
+  return endAction;
+}
+
+AnimationDefinition::AnimationDefinition() = default;
+
+AnimationDefinition::AnimationDefinition(AnimationDefinition&& other)
+: mName(std::move(other.mName)),
+  mDuration(other.mDuration),
+  mLoopCount(other.mLoopCount),
+  mDisconnectAction(other.mDisconnectAction),
+  mEndAction(other.mEndAction),
+  mSpeedFactor(other.mSpeedFactor),
+  mPlayRange(other.mPlayRange),
+  mProperties(std::move(other.mProperties))
+{}
+
+void AnimationDefinition::Animate(Animation& animation, AnimatedProperty::GetActor getActor)
+{
+  DALI_ASSERT_ALWAYS(animation);
+  for (auto& ap : mProperties)
+  {
+    ap.Animate(animation, getActor);
+  }
+}
+
+Animation AnimationDefinition::ReAnimate(AnimatedProperty::GetActor getActor)
+{
+  // Create and configure new animation.
+  Animation a = Animation::New(mDuration);
+  a.SetLoopCount(mLoopCount);
+  a.SetDisconnectAction(mDisconnectAction);
+  a.SetEndAction(mEndAction);
+
+  a.SetSpeedFactor(mSpeedFactor);
+  a.SetPlayRange(mPlayRange);
+
+  Animate(a, getActor);
+  return a;
+}
+
+AnimationDefinition& AnimationDefinition::operator=(AnimationDefinition&& other)
+{
+  AnimationDefinition tmp(std::move(other));
+  mName = std::move(tmp.mName);
+  mDuration = tmp.mDuration;
+  mLoopCount = tmp.mLoopCount;
+  mDisconnectAction = tmp.mDisconnectAction;
+  mEndAction = tmp.mEndAction;
+  mSpeedFactor = tmp.mSpeedFactor;
+  mPlayRange = tmp.mPlayRange;
+  mProperties.swap(tmp.mProperties);
+  return *this;
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/animation-definition.h b/dali-scene-loader/public-api/animation-definition.h
new file mode 100644 (file)
index 0000000..41de471
--- /dev/null
@@ -0,0 +1,88 @@
+#ifndef DALI_SCENE_LOADER_ANIMATION_DEFINITION_H
+#define DALI_SCENE_LOADER_ANIMATION_DEFINITION_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "dali-scene-loader/public-api/api.h"
+#include "dali-scene-loader/public-api/animated-property.h"
+#include "dali/public-api/common/vector-wrapper.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/**
+ * @brief Animation handle + name + definition of properties.
+ */
+class DALI_SCENE_LOADER_API AnimationDefinition
+{
+public: // STATIC
+  // For Animations created in the SDK.
+  static const float DEFAULT_DURATION_SECONDS;
+
+  // For parsing Animations from dli, when duration was not defined.
+  static const float MIN_DURATION_SECONDS;
+
+  /**
+   * @brief Saves the original end action of @a anim, sets the end action to
+   *  Discard, then stops the animation and returns the end action.
+   */
+  static Animation::EndAction StopForModification(Animation& anim);
+
+public: // METHODS
+  AnimationDefinition();
+
+  AnimationDefinition(AnimationDefinition&& other);
+
+  /**
+   * @brief Registers the properties against the given @a animation. @a getActor
+   *  will be used to obtain the Actors for each AnimatedProperty.
+   */
+  void Animate(Animation& animation, AnimatedProperty::GetActor getActor);
+
+  /**
+   * @brief Creates a new Animation and Animates() its properties. @a getActor
+   *  will be used to obtain the Actors for each AnimatedProperty.
+   */
+  Animation ReAnimate(AnimatedProperty::GetActor getActor);
+
+  AnimationDefinition& operator=(AnimationDefinition&& other);
+
+public: // DATA
+  std::string mName;
+
+  float mDuration = DEFAULT_DURATION_SECONDS;
+  int mLoopCount = 1;
+  Animation::EndAction mDisconnectAction = Animation::BAKE_FINAL;
+  Animation::EndAction mEndAction = Animation::BAKE;
+  float mSpeedFactor = 1.f;
+  Vector2 mPlayRange = Vector2{ 0.f, 1.f };
+
+  std::vector<AnimatedProperty> mProperties;
+};
+
+struct AnimationGroupDefinition
+{
+  std::string mName;
+  std::vector<std::string> mAnimations;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_ANIMATION_DEFINITION_H
diff --git a/dali-scene-loader/public-api/api.h b/dali-scene-loader/public-api/api.h
new file mode 100644 (file)
index 0000000..f5ffea0
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef DALI_SCENE_LOADER_API_H
+#define DALI_SCENE_LOADER_API_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#if __GNUC__ >= 4
+#define DALI_SCENE_LOADER_API __attribute__((visibility("default")))
+
+#elif defined(WIN32)
+#ifdef BUILDING_DALI_SCENE_LOADER
+#define DALI_SCENE_LOADER_API  __declspec(dllexport)
+#else
+#define DALI_SCENE_LOADER_API  __declspec(dllimport)
+#endif  // BUILDING_DALI_SCENE_LOADER
+
+#endif
+
+#endif //DALI_SCENE_LOADER_API_H
diff --git a/dali-scene-loader/public-api/blend-shape-details.cpp b/dali-scene-loader/public-api/blend-shape-details.cpp
new file mode 100644 (file)
index 0000000..3308d37
--- /dev/null
@@ -0,0 +1,85 @@
+
+/*
+* Copyright (c) 2020 Samsung Electronics Co., Ltd.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+
+// FILE HEADER
+#include "dali-scene-loader/public-api/blend-shape-details.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/animation/constraints.h"
+#include "dali/public-api/object/property.h"
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/resource-bundle.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+const std::string BlendShapes::NUMBER_OF_BLEND_SHAPES("uNumberOfBlendShapes");
+const std::string BlendShapes::UNNORMALIZE_FACTOR("uBlendShapeUnnormalizeFactor");
+const std::string BlendShapes::COMPONENT_SIZE("uBlendShapeComponentSize");
+
+const std::string BlendShapes::COMPONENTS("blendShapeComponents");
+
+const std::string BlendShapes::WEIGHTS_UNIFORM("uBlendShapeWeight");
+
+void BlendShapes::ConfigureProperties(const std::pair<MeshDefinition, MeshGeometry>& mesh, Shader shader, Actor actor)
+{
+  unsigned int index = 0u;
+
+  char weightNameBuffer[32];
+  char unnormalizeFactorNameBuffer[64];
+  char* const pWeightName = weightNameBuffer + snprintf(weightNameBuffer, sizeof(weightNameBuffer), "%s", WEIGHTS_UNIFORM.c_str());
+  char* const pFactorName = unnormalizeFactorNameBuffer + snprintf(unnormalizeFactorNameBuffer, sizeof(unnormalizeFactorNameBuffer), "%s", UNNORMALIZE_FACTOR.c_str());
+  for (const auto& blendShape : mesh.first.mBlendShapes)
+  {
+    snprintf(pWeightName, sizeof(weightNameBuffer) - (pWeightName - weightNameBuffer), "[%d]", index);
+    std::string weightName{ weightNameBuffer };
+    actor.RegisterProperty(weightName, blendShape.weight);
+
+    if (mesh.first.mBlendShapeVersion == Version::VERSION_1_0)
+    {
+      snprintf(pFactorName, sizeof(unnormalizeFactorNameBuffer) - (pFactorName - unnormalizeFactorNameBuffer), "[%d]", index);
+      std::string factorName{ unnormalizeFactorNameBuffer };
+      shader.RegisterProperty(factorName, mesh.second.blendShapeUnnormalizeFactor[index]);
+    }
+
+    ++index;
+  }
+
+  if (Version::VERSION_2_0 == mesh.first.mBlendShapeVersion)
+  {
+    shader.RegisterProperty(UNNORMALIZE_FACTOR, mesh.second.blendShapeUnnormalizeFactor[0u]);
+  }
+
+  shader.RegisterProperty(NUMBER_OF_BLEND_SHAPES, Property::Value(static_cast<int>(index)));
+  shader.RegisterProperty(COMPONENT_SIZE, Property::Value(static_cast<int>(mesh.second.blendShapeBufferOffset)));
+
+  // Create a read only property to preserve the components of the blend shape.
+  int32_t components = 0x0;
+  for (auto& bs : mesh.first.mBlendShapes)
+  {
+    components |= (bs.deltas.IsDefined() * Component::POSITIONS) |
+      (bs.normals.IsDefined() * Component::NORMALS) | (bs.tangents.IsDefined() * Component::TANGENTS);
+  }
+  shader.RegisterProperty(COMPONENTS, components, Property::AccessMode::READ_ONLY);
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/blend-shape-details.h b/dali-scene-loader/public-api/blend-shape-details.h
new file mode 100644 (file)
index 0000000..67283a8
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef DALI_SCENE_LOADER_BLEND_SHAPE_DETAILS_H
+#define DALI_SCENE_LOADER_BLEND_SHAPE_DETAILS_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/rendering/shader.h"
+#include "dali/public-api/actors/actor.h"
+#include <string>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+struct MeshDefinition;
+struct MeshGeometry;
+
+struct DALI_SCENE_LOADER_API BlendShapes
+{
+  enum class Version
+  {
+    VERSION_1_0,
+    VERSION_2_0,
+    INVALID
+  };
+
+  struct Component
+  {
+    enum
+    {
+      POSITIONS = 0x1,
+      NORMALS = 0x2,
+      TANGENTS = 0x4
+    };
+  };
+
+  // shader properties - animatable (uniforms)
+  static const std::string NUMBER_OF_BLEND_SHAPES;  ///< Integer number of blend shapes loaded.
+  static const std::string UNNORMALIZE_FACTOR;  ///< Scalar(s) for position components of blend shapes; Version 1.0: float array (1 per blend shape); Version 2.0: single float.
+  static const std::string COMPONENT_SIZE;  ///< Integer offset from one component (positions / normals / tangents) of a blend shape to the next.
+
+  // shader properties - read-only (not available as uniforms)
+  static const std::string COMPONENTS;  ///< Integer bitmask of the blend shape components that the shader uses; refer to the Components enum.
+
+  // actor property (instance) - animatable (uniforms)
+  static const std::string WEIGHTS_UNIFORM;  ///< The weight of each blend shape in a float array
+
+  /**
+   * @brief Registers properties based on the mesh definition (and geometry) and identified by the above string constants,
+   *  on the given @a shader and @a actor.
+   */
+  static void ConfigureProperties(const std::pair<MeshDefinition, MeshGeometry>& mesh, Shader shader, Actor actor);
+
+  BlendShapes() = delete;
+};
+
+}
+}
+
+#endif // DALI_SCENE_LOADER_BLEND_SHAPE_DETAILS_H
diff --git a/dali-scene-loader/public-api/camera-parameters.cpp b/dali-scene-loader/public-api/camera-parameters.cpp
new file mode 100644 (file)
index 0000000..875c58b
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/public-api/camera-parameters.h"
+#include "dali-scene-loader/public-api/utils.h"
+#include "dali/public-api/actors/camera-actor.h"
+#include "dali/public-api/math/quaternion.h"
+#include "dali/integration-api/debug.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+namespace
+{
+/**
+ * @brief Creates a perspective matrix.
+ *
+ * @param[out] result The perspective matrix.
+ * @param[in] left The coordinate for the left vertical clipping plane.
+ * @param[in] right The coordinate for the right vertical clipping plane.
+ * @param[in] bottom The coordinate for the bottom horizontal clipping plane.
+ * @param[in] top The coordinate for the top horizontal clipping plane.
+ * @param[in] near The distance to the near depth clipping plane.
+ * @param[in] far The distance to the far depth clipping plane.
+ * @param[in] invertYAxis Whether to invert the 'Y' axis.
+ */
+void Frustum( Matrix& result, float left, float right, float bottom, float top, float nearPlane, float farPlane, bool invertYAxis )
+{
+  float deltaZ = farPlane - nearPlane;
+  if ((nearPlane <= 0.0f) || (farPlane <= 0.0f) || Equals(right, left) || Equals(bottom, top) || (deltaZ <= 0.0f))
+  {
+    DALI_LOG_ERROR("Invalid parameters passed into Frustum!\n");
+    DALI_ASSERT_DEBUG("Invalid parameters passed into Frustum!");
+    return;
+  }
+
+  float deltaX = right - left;
+  float deltaY = invertYAxis ? bottom - top : top - bottom;
+
+  result.SetIdentity();
+
+  float* m = result.AsFloat();
+  m[0] = -2.0f * nearPlane / deltaX;
+  m[1] = m[2] = m[3] = 0.0f;
+
+  m[5] = -2.0f * nearPlane / deltaY;
+  m[4] = m[6] = m[7] = 0.0f;
+
+  m[8] = (right + left) / deltaX;
+  m[9] = (top + bottom) / deltaY;
+  m[10] = (nearPlane + farPlane) / deltaZ;
+  m[11] = 1.0f;
+
+  m[14] = -2.0f * nearPlane * farPlane / deltaZ;
+  m[12] = m[13] = m[15] = 0.0f;
+}
+
+/**
+ * @brief Creates a perspective projection matrix.
+ *
+ * It calls Frustum()
+ *
+ * @param[out] result The perspective projection matrix.
+ * @param[in] fovy The vertical field of view.
+ * @param[in] aspect The aspect ratio.
+ * @param[in] nearPlane The distance to the near depth clipping plane.
+ * @param[in] farPlane The distance to the far depth clipping plane.
+ * @param[in] invertYAxis Whether to invert the 'Y' axis.
+ */
+void Perspective( Matrix& result, float fovy, float aspect, float nearPlane, float farPlane, bool invertYAxis )
+{
+  float frustumH = tanf( fovy * 0.5f ) * nearPlane;
+  float frustumW = frustumH * aspect;
+
+  Frustum( result, -frustumW, frustumW, -frustumH, frustumH, nearPlane, farPlane, invertYAxis );
+}
+
+/**
+* @brief Creates an orthographic projection matrix.
+*
+* @param[out] result The orthographic projection matrix.
+* @param[in] left The coordinate for the left vertical clipping plane.
+* @param[in] right The coordinate for the right vertical clipping plane.
+* @param[in] bottom The coordinate for the bottom horizontal clipping plane.
+* @param[in] top The coordinate for the top horizontal clipping plane.
+* @param[in] nearPlane The distance to the near depth clipping plane.
+* @param[in] farPlane The distance to the far depth clipping plane.
+* @param[in] invertYAxis Whether to invert the 'Y' axis.
+*/
+void Orthographic(Matrix& result, float left, float right, float bottom, float top, float nearPlane, float farPlane, bool invertYAxis)
+{
+  if (Equals(right, left) || Equals(top, bottom) || Equals(farPlane, nearPlane))
+  {
+    DALI_LOG_ERROR("Cannot create orthographic projection matrix with a zero dimension.\n");
+    DALI_ASSERT_DEBUG("Cannot create orthographic projection matrix with a zero dimension.");
+    return;
+  }
+
+  float deltaX = right - left;
+  float deltaY = invertYAxis ? bottom - top : top - bottom;
+  float deltaZ = farPlane - nearPlane;
+
+  float *m = result.AsFloat();
+  m[0] = -2.0f / deltaX;
+  m[1] = 0.0f;
+  m[2] = 0.0f;
+  m[3] = 0.0f;
+
+  m[4] = 0.0f;
+  m[5] = -2.0f / deltaY;
+  m[6] = 0.0f;
+  m[7] = 0.0f;
+
+  m[8] = 0.0f;
+  m[9] = 0.0f;
+  m[10] = 2.0f / deltaZ;
+  m[11] = 0.0f;
+  m[12] = -(right + left) / deltaX;
+  m[13] = -(top + bottom) / deltaY;
+  m[14] = -(nearPlane + farPlane) / deltaZ;
+  m[15] = 1.0f;
+}
+
+} // nonamespace
+
+ViewProjection CameraParameters::GetViewProjection() const
+{
+  ViewProjection viewProjection;
+  // The projection matrix.
+  if (isPerspective)
+  {
+    Perspective(viewProjection.GetProjection(),
+      Radian(Degree(yFov)),
+      1.f,
+      zNear,
+      zFar,
+      true);
+  }
+  else
+  {
+    Orthographic(viewProjection.GetProjection(),
+      orthographicSize.x,
+      orthographicSize.y,
+      orthographicSize.z,
+      orthographicSize.w,
+      zNear,
+      zFar,
+      true);
+
+  }
+
+  // The view matrix.
+  const Quaternion viewQuaternion(ANGLE_180, Vector3::YAXIS);
+  Vector3 translation;
+  Quaternion cameraOrientation;
+  Vector3 scale;
+  matrix.GetTransformComponents(translation, cameraOrientation, scale);
+  cameraOrientation *= viewQuaternion;
+
+  viewProjection.GetView().SetInverseTransformComponents(scale,
+    cameraOrientation,
+    translation);
+
+  viewProjection.Update();
+  return viewProjection;
+}
+
+void CameraParameters::CalculateTransformComponents(Vector3 & position, Quaternion & orientation, Vector3 & scale) const
+{
+  matrix.GetTransformComponents(position, orientation, scale);
+
+  // The CameraActor is expected to look down the negative Z axis, towards the scene.
+  // Here we emulate the default direction of the camera in DALi.
+  Quaternion viewQuaternion(ANGLE_180, Vector3::YAXIS);
+  orientation *= viewQuaternion;
+}
+
+void CameraParameters::ConfigureCamera(CameraActor& camera) const
+{
+  SetActorCentered(camera);
+
+  if (isPerspective)
+  {
+    camera.SetProjectionMode(Camera::PERSPECTIVE_PROJECTION);
+    camera.SetNearClippingPlane(zNear);
+    camera.SetFarClippingPlane(zFar);
+    camera.SetFieldOfView(Radian(Degree(yFov)));
+  }
+  else
+  {
+    camera.SetProjectionMode(Camera::ORTHOGRAPHIC_PROJECTION);
+    camera.SetOrthographicProjection(orthographicSize.x,
+      orthographicSize.y,
+      orthographicSize.z,
+      orthographicSize.w,
+      zNear,
+      zFar);
+  }
+
+  // model
+  Vector3 camTranslation;
+  Vector3 camScale;
+  Quaternion camOrientation;
+  CalculateTransformComponents(camTranslation, camOrientation, camScale);
+
+  camera.SetInvertYAxis(true);
+  camera.SetProperty(Actor::Property::POSITION, camTranslation);
+  camera.SetProperty(Actor::Property::ORIENTATION, camOrientation);
+  camera.SetProperty(Actor::Property::SCALE, camScale);
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/camera-parameters.h b/dali-scene-loader/public-api/camera-parameters.h
new file mode 100644 (file)
index 0000000..044c7f4
--- /dev/null
@@ -0,0 +1,67 @@
+#ifndef DALI_SCENE_LOADER_CAMERA_PARAMETERS_H
+#define DALI_SCENE_LOADER_CAMERA_PARAMETERS_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+ // INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+#include "dali-scene-loader/public-api/view-projection.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/math/matrix.h"
+#include "dali/public-api/math/vector3.h"
+
+namespace Dali
+{
+
+class CameraActor;
+
+namespace SceneLoader
+{
+
+struct DALI_SCENE_LOADER_API CameraParameters
+{
+  Matrix matrix = Matrix::IDENTITY;
+  Vector4 orthographicSize = Vector4{ -1.f, 1.f, 1.f, -1.f };
+  float yFov = 60.f;
+  float zNear = 0.1f;
+  float zFar = 1000.f;
+  bool isPerspective = true;
+
+  /**
+   * @return The view-projection matrix of the camera.
+   */
+  ViewProjection GetViewProjection() const;
+
+  /**
+   * @brief Calculates the @a position, @a orientation and @a scale that's defined
+   *        for this camera, and writes it into the respective variable.
+   */
+  void CalculateTransformComponents(Vector3& position, Quaternion& orientation, Vector3& scale) const;
+
+  /**
+   * @brief Configures the camera in the way that it is supposed to be used with
+   *        scene-loader scenes. This means inverted Y and a rotation of 180 degrees
+   *        along the Y axis, plus whatever the parameters define.
+   */
+  void ConfigureCamera(CameraActor& camera) const;
+};
+
+}
+}
+
+#endif // DALI_SCENE_LOADER_CAMERA_PARAMETERS_H
diff --git a/dali-scene-loader/public-api/customization.cpp b/dali-scene-loader/public-api/customization.cpp
new file mode 100644 (file)
index 0000000..546d938
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/public-api/customization.h"
+#include "dali/devel-api/common/map-wrapper.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+struct Customization::Map::Impl {
+  std::map<Tag, Customization> mCustomizations;
+};
+
+Customization::Map::Map()
+: mImpl{ new Impl }
+{}
+
+Customization::Map::~Map() = default;
+
+Customization* Customization::Map::Set(Tag tag, Customization&& customization)
+{
+  auto& result = (mImpl->mCustomizations[tag] = customization);
+  return &result;
+}
+
+const Customization* Customization::Map::Get(Tag tag) const
+{
+  auto& customizations = mImpl->mCustomizations;
+  auto iFind = customizations.find(tag);
+  if (iFind != customizations.end())
+  {
+    return &iFind->second;
+  }
+  return nullptr;
+}
+
+Customization* Customization::Map::Get(Tag tag)
+{
+  auto& customizations = mImpl->mCustomizations;
+  auto iFind = customizations.find(tag);
+  if (iFind != customizations.end())
+  {
+    return &iFind->second;
+  }
+  return nullptr;
+}
+
+uint32_t Customization::Map::Size() const
+{
+  return mImpl->mCustomizations.size();
+}
+
+void Customization::Map::Clear()
+{
+  mImpl->mCustomizations.clear();
+}
+
+struct Customization::Choices::Impl
+{
+  std::map<Tag, OptionType> mOptions;
+};
+
+Customization::Choices::Choices()
+: mImpl{ new Impl }
+{}
+
+Customization::Choices::~Choices() = default;
+
+void Customization::Choices::Set(Tag tag, OptionType option)
+{
+  mImpl->mOptions[tag] = option;
+}
+
+Customization::OptionType Customization::Choices::Get(Tag tag) const
+{
+  auto& options = mImpl->mOptions;
+  auto iFind = options.find(tag);
+  if (iFind != options.end())
+  {
+    return iFind->second;
+  }
+  return NONE;
+}
+
+uint32_t Customization::Choices::Size() const
+{
+  return mImpl->mOptions.size();
+}
+
+void Customization::Choices::Clear()
+{
+  mImpl->mOptions.clear();
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/customization.h b/dali-scene-loader/public-api/customization.h
new file mode 100644 (file)
index 0000000..ff29b95
--- /dev/null
@@ -0,0 +1,124 @@
+#ifndef DALI_SCENE_LOADER_CUSTOMIZATION_STATE_H_
+#define DALI_SCENE_LOADER_CUSTOMIZATION_STATE_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+ // INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/common/vector-wrapper.h"
+#include <string>
+#include <memory>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/**
+ * @brief Offers a description of an aspect of the scene that can be customized:
+ *     the number of options, and the name of the nodes that are registered
+ *     for the tag, whose children will be shown / hidden based on selection.
+ */
+struct DALI_SCENE_LOADER_API Customization
+{
+  using Tag = std::string;
+  using OptionType = uint32_t;
+
+  /**
+   * @brief A mapping of customizations to tags.
+   */
+  struct DALI_SCENE_LOADER_API Map
+  {
+    Map();
+    ~Map();
+
+    /**
+     * @brief Maps the given @a customization to the given @a tag, overwriting any previous mapping to the same tag.
+     * @return Pointer to the inserted (or pre-existing) Customization instance.
+     */
+    Customization* Set(Tag tag, Customization&& customization);
+
+    /**
+     * @brief Attempts to retrieve a const Customization based on the given @a tag.
+     */
+    const Customization* Get(Tag tag) const;
+
+    /**
+     * @brief Attempts to retrieve a Customization based on the given @a tag.
+     */
+    Customization* Get(Tag tag);
+
+    /**
+     * @brief Returns the number of elements.
+     */
+    uint32_t Size() const;
+
+    /*
+     * @brief Removes every element from the Map.
+     */
+    void Clear();
+
+  private:
+    struct Impl;
+    const std::unique_ptr<Impl> mImpl;
+  };
+
+  /**
+   * @brief A mapping of choices - indices of children of customization nodes to use - to tags.
+   */
+  struct DALI_SCENE_LOADER_API Choices
+  {
+    Choices();
+    ~Choices();
+
+    /**
+     * @brief Maps the given @a option to the given @a tag, overwriting any previous mapping to the same tag.
+     */
+    void Set(Tag tag, OptionType option);
+
+    /**
+     * @brief Attempts to retrieve a Customization based on the given @a tag. Returns NONE if @a tag is not known.
+     */
+    OptionType Get(Tag tag) const;
+
+    /**
+     * @brief Returns the number of elements.
+     */
+    uint32_t Size() const;
+
+    /*
+     * @brief Removes every element from the underlying map.
+     */
+    void Clear();
+
+  private:
+    struct Impl;
+    const std::unique_ptr<Impl> mImpl;
+  };
+
+  static const OptionType NONE = OptionType(-1);
+
+  OptionType numOptions = 0;
+  std::vector<std::string> nodes;  // to apply option to.
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_CUSTOMIZATION_STATE_H_
diff --git a/dali-scene-loader/public-api/dli-loader.cpp b/dali-scene-loader/public-api/dli-loader.cpp
new file mode 100644 (file)
index 0000000..11dc036
--- /dev/null
@@ -0,0 +1,1778 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "dali-scene-loader/public-api/dli-loader.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/object/property-array.h"
+#include "dali/devel-api/common/map-wrapper.h"
+#include "dali-toolkit/devel-api/builder/json-parser.h"
+#include "dali/integration-api/debug.h"
+#include <fstream>
+#include <limits>
+#include <algorithm>
+#include <memory>
+#include <cmath>
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/parse-renderer-state.h"
+#include "dali-scene-loader/public-api/skinning-details.h"
+#include "dali-scene-loader/public-api/load-result.h"
+#include "dali-scene-loader/public-api/scene-definition.h"
+#include "dali-scene-loader/public-api/animation-definition.h"
+#include "dali-scene-loader/public-api/alpha-function-helper.h"
+#include "dali-scene-loader/public-api/camera-parameters.h"
+#include "dali-scene-loader/public-api/light-parameters.h"
+#include "dali-scene-loader/public-api/blend-shape-details.h"
+#include "dali-scene-loader/public-api/ktx-loader.h"
+#include "dali-scene-loader/public-api/utils.h"
+#include "dali-scene-loader/internal/json-util.h"
+
+#define DLI_0_1_COMPATIBILITY
+
+namespace Dali
+{
+using namespace Toolkit;
+
+namespace SceneLoader
+{
+
+namespace rs = RendererState;
+
+namespace
+{
+const std::string NODES = "nodes";
+const std::string SCENES = "scenes";
+const std::string NODE = "node";
+const std::string URI = "uri";
+const std::string URL = "url";
+const std::string CUSTOMIZATION = "customization";
+const std::string HINTS = "hints";
+const std::string NAME("name");
+const std::string BLEND_SHAPE_HEADER("blendShapeHeader");
+const std::string BLEND_SHAPES("blendShapes");
+const std::string BLEND_SHAPE_VERSION_1_0("1.0");
+const std::string BLEND_SHAPE_VERSION_2_0("2.0");
+const std::string VERSION("version");
+
+const char* const SHADOW_MAP_SIZE = "shadowMapSize";
+const char* const ORTHOGRAPHIC_SIZE = "orthographicSize";
+const char* const PIXEL_UNITS = "px";
+
+const char SLASH = '/';
+
+void ReadModelTransform(const TreeNode* node, Quaternion& orientation, Vector3& translation, Vector3& scale)
+{
+  float num[16u] = { .0f };
+
+  if (ReadVector(node->GetChild("matrix"), num, 16u))
+  {
+    Matrix mat(num);
+    mat.GetTransformComponents(translation, orientation, scale);
+  }
+  else
+  {
+    if (ReadVector(node->GetChild("angle"), num, 3u))
+    {
+      orientation = Quaternion(Radian(Degree(num[0u])), Radian(Degree(num[1u])), Radian(Degree(num[2u])));
+    }
+
+    if (ReadVector(node->GetChild("position"), num, 3u))
+    {
+      translation = Vector3(num);
+    }
+  }
+}
+
+bool ReadAttribBlob(const TreeNode* node, MeshDefinition::Blob& buffer)
+{
+  return ReadBlob(node, buffer.mOffset, buffer.mLength);
+}
+
+bool ReadAttribAccessor(const TreeNode* node, MeshDefinition::Accessor& accessor)
+{
+  return ReadBlob(node, accessor.mBlob.mOffset, accessor.mBlob.mLength);
+}
+
+bool ReadColorCode(const TreeNode* node, Vector4& color,
+  DliLoader::ConvertColorCode convertColorCode)
+{
+  if (!node || !convertColorCode)
+  {
+    return false;
+  }
+
+  color = convertColorCode(node->GetString());
+
+  return true;
+}
+
+bool ReadColorCodeOrColor(const TreeNode* node, Vector4& color,
+  DliLoader::ConvertColorCode convertColorCode)
+{
+  return ReadColorCode(node->GetChild("colorCode"), color, convertColorCode) ||
+    ReadColor(node->GetChild("color"), color);
+}
+
+RendererState::Type ReadRendererState(const TreeNode& tnRendererState)
+{
+  if (tnRendererState.GetType() == TreeNode::INTEGER)
+  {
+    return static_cast<RendererState::Type>(tnRendererState.GetInteger());
+  }
+  else if (tnRendererState.GetType() == TreeNode::STRING)
+  {
+    return RendererState::Parse(tnRendererState.GetString());
+  }
+  else
+  {
+    return -1;
+  }
+}
+
+///@brief Reads arc properties.
+void ReadArcField(const TreeNode* eArc, ArcNode& arc)
+{
+  ReadBool(eArc->GetChild("antiAliasing"), arc.mAntiAliasing);
+  ReadInt(eArc->GetChild("arcCaps"), arc.mArcCaps);
+  ReadFloat(eArc->GetChild("radius"), arc.mRadius);
+
+  arc.mStartAngleDegrees = .0f;
+  ReadFloat(eArc->GetChild("startAngle"), arc.mStartAngleDegrees);
+
+  arc.mEndAngleDegrees = .0f;
+  ReadFloat(eArc->GetChild("endAngle"), arc.mEndAngleDegrees);
+}
+
+const TreeNode *GetNthChild(const TreeNode *node, uint32_t index)
+{
+  uint32_t i = 0;
+  for (TreeNode::ConstIterator it = (*node).CBegin(); it != (*node).CEnd(); ++it, ++i)
+  {
+    if (i == index)
+    {
+      return &((*it).second);
+    }
+  }
+  return NULL;
+}
+
+const TreeNode* RequireChild(const TreeNode* node, const std::string& childName)
+{
+  auto child = node->GetChild(childName);
+  if (!child)
+  {
+    ExceptionFlinger flinger(ASSERT_LOCATION);
+    flinger << "Failed to find child node '" << childName << "'";
+    if (auto nodeName = node->GetName())
+    {
+      flinger << " on '" << nodeName << "'";
+    }
+    flinger << ".";
+  }
+  return child;
+}
+
+void ParseProperties(const Toolkit::TreeNode& node, Property::Array& array);
+
+void ParseProperties(const Toolkit::TreeNode& node, Property::Map& map)
+{
+  DALI_ASSERT_DEBUG(node.GetType() == TreeNode::OBJECT);
+  for (auto i0 = node.CBegin(), i1 = node.CEnd(); i0 != i1; ++i0)
+  {
+    auto kv = *i0;
+    switch (kv.second.GetType())
+    {
+    case TreeNode::ARRAY:
+    {
+      Property::Array array;
+      ParseProperties(kv.second, array);
+      map.Insert(kv.first, array);
+    } break;
+
+    case TreeNode::OBJECT:
+    {
+      Property::Map innerMap;
+      ParseProperties(kv.second, innerMap);
+      map.Insert(kv.first, innerMap);
+    } break;
+
+    case TreeNode::STRING:
+      map.Insert(kv.first, kv.second.GetString());
+      break;
+
+    case TreeNode::INTEGER:
+      map.Insert(kv.first, kv.second.GetInteger());
+      break;
+
+    case TreeNode::BOOLEAN:
+      map.Insert(kv.first, kv.second.GetBoolean());
+      break;
+
+    case TreeNode::FLOAT:
+      map.Insert(kv.first, kv.second.GetFloat());
+      break;
+
+    case TreeNode::IS_NULL:
+      break;
+    }
+  }
+}
+
+void ParseProperties(const Toolkit::TreeNode& node, Property::Array& array)
+{
+  DALI_ASSERT_DEBUG(node.GetType() == TreeNode::ARRAY);
+  for (auto i0 = node.CBegin(), i1 = node.CEnd(); i0 != i1; ++i0)
+  {
+    auto kv = *i0;
+    switch (kv.second.GetType())
+    {
+    case TreeNode::ARRAY:
+    {
+      Property::Array innerArray;
+      ParseProperties(kv.second, innerArray);
+      array.PushBack(innerArray);
+    } break;
+
+    case TreeNode::OBJECT:
+    {
+      Property::Map map;
+      ParseProperties(kv.second, map);
+      array.PushBack(map);
+    } break;
+
+    case TreeNode::STRING:
+      array.PushBack(kv.second.GetString());
+      break;
+
+    case TreeNode::INTEGER:
+      array.PushBack(kv.second.GetInteger());
+      break;
+
+    case TreeNode::BOOLEAN:
+      array.PushBack(kv.second.GetBoolean());
+      break;
+
+    case TreeNode::FLOAT:
+      array.PushBack(kv.second.GetFloat());
+      break;
+
+    case TreeNode::IS_NULL:
+      break;
+    }
+  }
+}
+
+}//namespace
+
+struct DliLoader::Impl
+{
+  StringCallback mOnError = DefaultErrorCallback;
+  Toolkit::JsonParser mParser;
+
+  void ParseScene(LoadParams& params);
+
+private:
+  std::map<Index, Matrix>  mInverseBindMatrices;
+
+  /**
+   * @brief Due to .dli nodes being processed in depth-first traversal with orphans being
+   *  ignored, features that rely on node indices (which is more compact and closer to
+   *  glTF) require a mapping from .dli node indices to those in the resulting SceneDefinition.
+   *  The index mapper is responsible for maintaing this mapping, and resolving node IDs
+   *  once the processing of the nodes has finished.
+   * @note The resolution requires the whole scene graph to finish parsing, therefore any
+   *  node extensions relying on node IDs will see the dli ID in their processor.
+   */
+  struct IIndexMapper
+  {
+    /**
+     * @brief Attempts to create a mapping from a node's @a dli index to its @a scene
+     *  index.
+     * @return Whether the operation was successful.
+     */
+    virtual bool Map(Index iDli, Index iScene) =0;
+
+    /**
+     * @return The scene index for the node's @a dli index.
+     */
+    virtual Index Resolve(Index iDli) =0;
+  };
+
+  /**
+   * @brief Traverses the DOM tree created by LoadDocument() in an attempt to create
+   *  an intermediate representation of resources and nodes.
+   */
+  void ParseSceneInternal(Index iScene, const Toolkit::TreeNode* tnScenes,
+    const Toolkit::TreeNode* tnNodes, LoadParams& params);
+
+  void ParseSkeletons(const Toolkit::TreeNode* skeletons, SceneDefinition& scene, ResourceBundle& resources);
+  void ParseEnvironments(const Toolkit::TreeNode* environments, ResourceBundle& resources);
+  void ParseMaterials(const Toolkit::TreeNode* materials, ConvertColorCode convertColorCode,
+    ResourceBundle& resources);
+
+  void ParseNodes(const Toolkit::TreeNode* nodes, Index index, LoadParams& params);
+  void ParseNodesInternal(const Toolkit::TreeNode* nodes, Index index,
+    std::vector<Index>& inOutParentStack, LoadParams& params, IIndexMapper& indexMapper);
+
+  void ParseAnimations(const Toolkit::TreeNode* animations, LoadParams& params);
+  void ParseAnimationGroups(const Toolkit::TreeNode* animationGroups, LoadParams& params);
+
+  void ParseShaders(const Toolkit::TreeNode* shaders, ResourceBundle& resources);
+  void ParseMeshes(const Toolkit::TreeNode* meshes, ResourceBundle& resources);
+
+  void GetCameraParameters(std::vector<CameraParameters>& cameras) const;
+  void GetLightParameters(std::vector<LightParameters>& lights) const;
+};
+
+DliLoader::DliLoader()
+: mImpl{ new Impl }
+{}
+
+DliLoader::~DliLoader() = default;
+
+void DliLoader::SetErrorCallback(StringCallback onError)
+{
+  mImpl->mOnError = onError;
+}
+
+bool DliLoader::LoadScene(const std::string& uri, LoadParams& params)
+{
+  std::string daliBuffer = LoadTextFile(uri.c_str());
+
+  auto& parser = mImpl->mParser;
+  parser = JsonParser::New();
+  if (!parser.Parse(daliBuffer))
+  {
+    return false;
+  }
+
+  mImpl->ParseScene(params);
+  return true;
+}
+
+std::string DliLoader::GetParseError() const
+{
+  std::stringstream stream;
+
+  auto& parser = mImpl->mParser;
+  if (parser.ParseError())
+  {
+    stream << "position: " << parser.GetErrorPosition() << ", line: " << parser.GetErrorLineNumber() << ", column: " << parser.GetErrorColumn() << ", description: " << parser.GetErrorDescription() << ".";
+  }
+
+  return stream.str();
+}
+
+void DliLoader::Impl::ParseScene(LoadParams& params)
+{
+  auto& input = params.input;
+  auto& output = params.output;
+
+  // get index of root node.
+  auto docRoot = mParser.GetRoot();
+
+  // Process resources first - these are shared
+  if (auto environments = docRoot->GetChild("environment"))
+  {
+    ParseEnvironments(environments, output.mResources);  // NOTE: must precede parsing of materials
+  }
+
+  if (auto meshes = docRoot->GetChild("meshes"))
+  {
+    ParseMeshes(meshes, output.mResources);
+  }
+
+  if (auto shaders = docRoot->GetChild("shaders"))
+  {
+    ParseShaders(shaders, output.mResources);
+  }
+
+  if (auto materials = docRoot->GetChild("materials"))
+  {
+    ParseMaterials(materials, input.mConvertColorCode, output.mResources);
+  }
+
+  for (auto& c : input.mPreNodeCategoryProcessors)
+  {
+    if (auto node = docRoot->GetChild(c.first))
+    {
+      Property::Array array;
+      ParseProperties(*node, array);
+      c.second(std::move(array), mOnError);
+    }
+  }
+
+  // Process scenes
+  Index iScene = 0;  // default scene
+  ReadIndex(docRoot->GetChild("scene"), iScene);
+
+  auto tnScenes = RequireChild(docRoot, "scenes");
+  auto tnNodes = RequireChild(docRoot, "nodes");
+  ParseSceneInternal(iScene, tnScenes, tnNodes, params);
+
+  ParseSkeletons(docRoot->GetChild("skeletons"), output.mScene, output.mResources);
+
+  output.mScene.EnsureUniqueSkinningShaderInstances(output.mResources);
+  output.mScene.EnsureUniqueBlendShapeShaderInstances(output.mResources);
+
+  // Ger cameras and lights
+  GetCameraParameters(output.mCameraParameters);
+  GetLightParameters(output.mLightParameters);
+
+  // Post-node processors and animations last
+  for (auto& c : input.mPostNodeCategoryProcessors)
+  {
+    if (auto node = docRoot->GetChild(c.first))
+    {
+      Property::Array array;
+      ParseProperties(*node, array);
+      c.second(std::move(array), mOnError);
+    }
+  }
+
+  if (auto animations = docRoot->GetChild("animations"))
+  {
+    ParseAnimations(animations, params);
+  }
+
+  if (!output.mAnimationDefinitions.empty())
+  {
+    if (auto animationGroups = docRoot->GetChild("animationGroups"))
+    {
+      ParseAnimationGroups(animationGroups, params);
+    }
+  }
+}
+
+void DliLoader::Impl::ParseSceneInternal(Index iScene, const Toolkit::TreeNode* tnScenes,
+  const Toolkit::TreeNode* tnNodes, LoadParams& params)
+{
+  auto getSceneRootIdx = [tnScenes, tnNodes](Index iScene) {
+    auto tn = GetNthChild(tnScenes, iScene);  // now a "scene" object
+    if (!tn)
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << iScene << " is out of bounds access into " << SCENES << ".";
+    }
+
+    tn = RequireChild(tn, NODES);  // now a "nodes" array
+    if (tn->GetType() != TreeNode::ARRAY)
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << SCENES << "[" << iScene << "]." << NODES <<
+        " has an invalid type; array required.";
+    }
+
+    if (tn->Size() < 1)
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << SCENES << "[" << iScene << "]." << NODES <<
+        " must define a node id.";
+    }
+
+    tn = GetNthChild(tn, 0);  // now the first element of the array
+    Index iRootNode;
+    if (!ReadIndex(tn, iRootNode))
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << SCENES << "[" << iScene << "]." << NODES <<
+        " has an invalid value for root node index: '" << iRootNode << "'.";
+    }
+
+    if (iRootNode >= tnNodes->Size())
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << "Root node index << " << iRootNode << " of scene " << iScene << " is out of bounds.";
+    }
+
+    tn = GetNthChild(tnNodes, iRootNode);  // now a "node" object
+    if (tn->GetType() != TreeNode::OBJECT)
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << "Root node of scene " << iScene << " is of invalid JSON type; object required";
+    }
+
+    return iRootNode;
+  };
+
+  Index iRootNode = getSceneRootIdx(iScene);
+  ParseNodes(tnNodes, iRootNode, params);
+
+  auto& scene = params.output.mScene;
+  scene.AddRootNode(0);
+
+  for (Index i = 0; i < iScene; ++i)
+  {
+    Index iRootNode = getSceneRootIdx(i);
+    const Index iRoot = scene.GetNodeCount();
+    ParseNodes(tnNodes, iRootNode, params);
+    scene.AddRootNode(iRoot);
+  }
+
+  auto numScenes = tnScenes->Size();
+  for (Index i = iScene + 1; i < numScenes; ++i)
+  {
+    Index iRootNode = getSceneRootIdx(i);
+    const Index iRoot = scene.GetNodeCount();
+    ParseNodes(tnNodes, iRootNode, params);
+    scene.AddRootNode(iRoot);
+  }
+}
+
+void DliLoader::Impl::ParseSkeletons(const TreeNode* skeletons, SceneDefinition& scene, ResourceBundle& resources)
+{
+  if (skeletons)
+  {
+    auto iStart = skeletons->CBegin();
+    for (auto i0 = iStart, i1 = skeletons->CEnd(); i0 != i1; ++i0)
+    {
+      auto& node = (*i0).second;
+      std::string skeletonRootName;
+      if (ReadString(node.GetChild(NODE), skeletonRootName))
+      {
+        SkeletonDefinition skeleton;
+        if (!scene.FindNode(skeletonRootName, &skeleton.mRootNodeIdx))
+        {
+          ExceptionFlinger(ASSERT_LOCATION) << FormatString("Skeleton %d: node '%s' not defined.", resources.mSkeletons.size(), skeletonRootName.c_str());
+        }
+
+        uint32_t jointCount = 0;
+        std::function<void(Index)> visitFn;
+        auto& ibms = mInverseBindMatrices;
+        visitFn = [&](Index id) {
+          auto node = scene.GetNode(id);
+          jointCount += ibms.find(id) != ibms.end();
+
+          for (auto i : node->mChildren)
+          {
+            visitFn(i);
+          }
+        };
+        visitFn(skeleton.mRootNodeIdx);
+
+        if (jointCount > Skinning::MAX_JOINTS)
+        {
+          mOnError(FormatString("Skeleton %d: joint count exceeds supported limit.", resources.mSkeletons.size()));
+          jointCount = Skinning::MAX_JOINTS;
+        }
+
+        skeleton.mJoints.reserve(jointCount);
+
+        visitFn = [&](Index id) {
+          auto iFind = ibms.find(id);
+          if (iFind != ibms.end() && skeleton.mJoints.size() < Skinning::MAX_JOINTS)
+          {
+            skeleton.mJoints.push_back({ id, iFind->second });
+          }
+
+          auto node = scene.GetNode(id);
+          for (auto i : node->mChildren)
+          {
+            visitFn(i);
+          }
+        };
+        visitFn(skeleton.mRootNodeIdx);
+
+        resources.mSkeletons.push_back(std::move(skeleton));
+      }
+      else
+      {
+        ExceptionFlinger(ASSERT_LOCATION) << "skeleton " << std::distance(iStart, i0) <<
+          ": Missing required attribute '"<< NODE <<"'.";
+      }
+    }
+  }
+}
+
+void DliLoader::Impl::ParseEnvironments(const TreeNode* environments, ResourceBundle& resources)
+{
+  Matrix cubeOrientation(Matrix::IDENTITY);
+
+  for (auto i0 = environments->CBegin(), i1 = environments->CEnd(); i0 != i1; ++i0)
+  {
+    auto& node = (*i0).second;
+
+    EnvironmentDefinition envDef;
+    ReadString(node.GetChild("cubeSpecular"), envDef.mSpecularMapPath);
+    ReadString(node.GetChild("cubeDiffuse"), envDef.mDiffuseMapPath);
+    ToUnixFileSeparators(envDef.mSpecularMapPath);
+    ToUnixFileSeparators(envDef.mDiffuseMapPath);
+    envDef.mIblIntensity = 1.0f;
+    ReadFloat(node.GetChild("iblIntensity"), envDef.mIblIntensity);
+    if (ReadVector(node.GetChild("cubeInitialOrientation"), cubeOrientation.AsFloat(), 16u))
+    {
+      envDef.mCubeOrientation = Quaternion(cubeOrientation);
+    }
+
+    resources.mEnvironmentMaps.emplace_back(std::move(envDef), EnvironmentDefinition::Textures());
+  }
+
+  // NOTE: guarantees environmentMaps to have an empty environment.
+  if (resources.mEnvironmentMaps.empty())
+  {
+    resources.mEnvironmentMaps.emplace_back(EnvironmentDefinition(), EnvironmentDefinition::Textures());
+  }
+}
+
+void DliLoader::Impl::ParseShaders(const TreeNode* shaders, ResourceBundle& resources)
+{
+  uint32_t iShader = 0;
+  for (auto i0 = shaders->CBegin(), i1 = shaders->CEnd(); i0 != i1; ++i0, ++iShader)
+  {
+    auto& node = (*i0).second;
+    ShaderDefinition shaderDef;
+    ReadStringVector(node.GetChild("defines"), shaderDef.mDefines);
+
+    // Read shader hints. Possible values are:
+    //                         Don't define for No hints.
+    // "OUTPUT_IS_TRANSPARENT" Might generate transparent alpha from opaque inputs.
+    //     "MODIFIES_GEOMETRY" Might change position of vertices, this option disables any culling optimizations.
+
+    ReadStringVector(node.GetChild( HINTS ), shaderDef.mHints);
+
+    if (ReadString(node.GetChild("vertex"), shaderDef.mVertexShaderPath) &&
+      ReadString(node.GetChild("fragment"), shaderDef.mFragmentShaderPath))
+    {
+      ToUnixFileSeparators(shaderDef.mVertexShaderPath);
+      ToUnixFileSeparators(shaderDef.mFragmentShaderPath);
+
+      for (TreeNode::ConstIterator j0 = node.CBegin(), j1 = node.CEnd(); j0 != j1; ++j0)
+      {
+        const TreeNode::KeyNodePair& keyValue = *j0;
+        const std::string& key = keyValue.first;
+        const TreeNode& value = keyValue.second;
+
+        Property::Value uniformValue;
+        if (key.compare("vertex") == 0 || key.compare("fragment") == 0 || key.compare("defines") == 0 || key.compare(HINTS) == 0)
+        {
+          continue;
+        }
+        else if (key.compare("rendererState") == 0)
+        {
+          shaderDef.mRendererState = ReadRendererState(keyValue.second);
+        }
+        else if (value.GetType() == TreeNode::INTEGER || value.GetType() == TreeNode::FLOAT)
+        {
+          float f = 0.f;
+          ReadFloat(&value, f);
+          uniformValue = f;
+        }
+        else if (value.GetType() == TreeNode::BOOLEAN)
+        {
+          DALI_LOG_WARNING("\"bool\" uniforms are handled as floats in shader");
+          bool value = false;
+          if (ReadBool(&keyValue.second, value))
+          {
+            uniformValue = value ? 1.0f : 0.0f;
+          }
+        }
+        else switch (auto size = GetNumericalArraySize(&value))
+        {
+        case 16:
+        {
+          Matrix m;
+          ReadVector(&value, m.AsFloat(), size);
+          uniformValue = m;
+          break;
+        }
+
+        case 9:
+        {
+          Matrix3 m;
+          ReadVector(&value, m.AsFloat(), size);
+          uniformValue = m;
+          break;
+        }
+
+        case 4:
+        {
+          Vector4 v;
+          ReadVector(&value, v.AsFloat(), size);
+          uniformValue = v;
+          break;
+        }
+
+        case 3:
+        {
+          Vector3 v;
+          ReadVector(&value, v.AsFloat(), size);
+          uniformValue = v;
+          break;
+        }
+
+        case 2:
+        {
+          Vector2 v;
+          ReadVector(&value, v.AsFloat(), size);
+          uniformValue = v;
+          break;
+        }
+
+        default:
+          mOnError(FormatString(
+            "shader %d: Ignoring uniform '%s': failed to infer type from %d elements.",
+            iShader, key.c_str()));
+          break;
+        }
+
+        if (Property::NONE != uniformValue.GetType())
+        {
+          shaderDef.mUniforms.Insert(key, uniformValue);
+        }
+      }
+
+      resources.mShaders.emplace_back(std::move(shaderDef), Shader());
+    }
+    else
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << "shader " << iShader <<
+        ": Missing vertex / fragment shader definition.";
+    }
+  }
+}
+
+void DliLoader::Impl::ParseMeshes(const TreeNode* meshes, ResourceBundle& resources)
+{
+  for (auto i0 = meshes->CBegin(), i1 = meshes->CEnd(); i0 != i1; ++i0)
+  {
+    auto& node = (*i0).second;
+
+    MeshDefinition  meshDef;
+    if (!ReadString(node.GetChild(URI), meshDef.mUri))
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << "mesh " << resources.mMeshes.size() <<
+        ": Missing required attribute '" << URI << "'.";
+    }
+
+    ToUnixFileSeparators(meshDef.mUri);
+
+    std::string primitive;
+    if (ReadString(node.GetChild("primitive"), primitive))
+    {
+      if (primitive == "LINES")
+      {
+        meshDef.mPrimitiveType = Geometry::LINES;
+      }
+      else if (primitive == "POINTS")
+      {
+        meshDef.mPrimitiveType = Geometry::POINTS;
+      }
+      else if (primitive != "TRIANGLES")
+      {
+        mOnError(FormatString(
+          "mesh %d: Using TRIANGLES instead of unsupported primitive type '%s'.",
+          resources.mMeshes.size(), primitive.c_str()));
+      }
+    }
+
+    int attributes;
+    if (ReadInt(node.GetChild("attributes"), attributes))
+    {
+      if (MaskMatch(attributes, MeshDefinition::INDICES) &&
+        !ReadAttribAccessor(node.GetChild("indices"), meshDef.mIndices))
+      {
+        ExceptionFlinger(ASSERT_LOCATION) << FormatString("mesh %d: Failed to read %s.",
+          resources.mMeshes.size(), "indices");
+      }
+
+      if (MaskMatch(attributes, MeshDefinition::POSITIONS) &&
+        !ReadAttribAccessor(node.GetChild("positions"), meshDef.mPositions))
+      {
+        ExceptionFlinger(ASSERT_LOCATION) << FormatString("mesh %d: Failed to read %s.",
+          resources.mMeshes.size(), "positions");
+      }
+
+      if (MaskMatch(attributes, MeshDefinition::NORMALS) &&
+        !ReadAttribAccessor(node.GetChild("normals"), meshDef.mNormals))
+      {
+        mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(),
+          "normals"));
+      }
+
+      if (MaskMatch(attributes, MeshDefinition::TEX_COORDS) &&
+        !ReadAttribAccessor(node.GetChild("textures"), meshDef.mTexCoords))
+      {
+        mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(),
+          "textures"));
+      }
+
+      if (MaskMatch(attributes, MeshDefinition::TANGENTS) &&
+        !ReadAttribAccessor(node.GetChild("tangents"), meshDef.mTangents))
+      {
+        mOnError(FormatString("mesh %d: Failed to read %s.", resources.mMeshes.size(),
+          "tangents"));
+      }
+
+      // NOTE: we're no longer reading bitangents as these are calculated in the shaders.
+      if (ReadIndex(node.GetChild("skeleton"), meshDef.mSkeletonIdx))
+      {
+        if (!MaskMatch(attributes, MeshDefinition::JOINTS_0) &&
+          !MaskMatch(attributes, MeshDefinition::WEIGHTS_0))
+        {
+          mOnError(FormatString("mesh %d: Expected joints0 / weights0 attribute(s) missing.",
+            resources.mMeshes.size()));
+        }
+        else if (!ReadAttribAccessor(node.GetChild("joints0"), meshDef.mJoints0) ||
+          !ReadAttribAccessor(node.GetChild("weights0"), meshDef.mWeights0))
+        {
+          mOnError(FormatString("mesh %d: Failed to read skinning information.",
+            resources.mMeshes.size()));
+        }
+      }
+
+      if (auto blendshapeHeader = node.GetChild(BLEND_SHAPE_HEADER))
+      {
+        std::string blendShapeVersion;
+        ReadString(blendshapeHeader->GetChild(VERSION), blendShapeVersion);
+
+        if (0u == blendShapeVersion.compare(BLEND_SHAPE_VERSION_1_0))
+        {
+          meshDef.mBlendShapeVersion = BlendShapes::Version::VERSION_1_0;
+        }
+        else if (0u == blendShapeVersion.compare(BLEND_SHAPE_VERSION_2_0))
+        {
+          meshDef.mBlendShapeVersion = BlendShapes::Version::VERSION_2_0;
+        }
+
+        switch (meshDef.mBlendShapeVersion)
+        {
+          case BlendShapes::Version::VERSION_1_0:
+          case BlendShapes::Version::VERSION_2_0: // FALL THROUGH
+          {
+            ReadAttribBlob(blendshapeHeader, meshDef.mBlendShapeHeader);
+            break;
+          }
+          default:
+          {
+            // nothing to do
+            break;
+          }
+        }
+      }
+
+      if (auto blendShapes = node.GetChild(BLEND_SHAPES) )
+      {
+        meshDef.mBlendShapes.resize(blendShapes->Size());
+
+        auto index = 0u;
+        for (auto it = blendShapes->CBegin(), endIt = blendShapes->CEnd(); it != endIt; ++it, ++index)
+        {
+          // Each blend shape is stored as the difference with the original mesh.
+
+          auto& blendShapeNode = (*it).second;
+
+          auto& blendShape = meshDef.mBlendShapes[index];
+          ReadString(blendShapeNode.GetChild("name"), blendShape.name);
+          if (auto position = blendShapeNode.GetChild("positions"))
+          {
+            ReadAttribAccessor(position, blendShape.deltas);
+          }
+          if (auto normals = blendShapeNode.GetChild("normals"))
+          {
+            ReadAttribAccessor(normals, blendShape.normals);
+          }
+          if (auto tangents = blendShapeNode.GetChild("tangents"))
+          {
+            ReadAttribAccessor(tangents, blendShape.tangents);
+          }
+          ReadFloat(blendShapeNode.GetChild("weight"), blendShape.weight);
+        }
+      }
+
+      bool flipV;
+      if (ReadBool(node.GetChild("flipV"), flipV))
+      {
+        meshDef.mFlags |= flipV * MeshDefinition::FLIP_UVS_VERTICAL;
+      }
+
+      resources.mMeshes.emplace_back(std::move(meshDef), MeshGeometry());
+    }
+  }
+}
+
+void DliLoader::Impl::ParseMaterials(const TreeNode* materials, ConvertColorCode convertColorCode,
+  ResourceBundle& resources)
+{
+  for (auto i0 = materials->CBegin(), i1 = materials->CEnd(); i0 != i1; ++i0)
+  {
+    auto& node = (*i0).second;
+
+    MaterialDefinition materialDef;
+    if (auto eEnvironment = node.GetChild("environment"))
+    {
+      ReadIndex(eEnvironment, materialDef.mEnvironmentIdx);
+      if (static_cast<unsigned int>(materialDef.mEnvironmentIdx) >= resources.mEnvironmentMaps.size())
+      {
+        ExceptionFlinger(ASSERT_LOCATION) << "material " << resources.mMaterials.size() <<
+          ": Environment index " << materialDef.mEnvironmentIdx << " out of bounds (" <<
+          resources.mEnvironmentMaps.size() << ").";
+      }
+    }
+
+    //TODO : need to consider AGIF
+    std::vector<std::string> texturePaths;
+    std::string texturePath;
+    if (ReadString(node.GetChild("albedoMap"), texturePath))
+    {
+      ToUnixFileSeparators(texturePath);
+      const auto semantic = MaterialDefinition::ALBEDO;
+      materialDef.mTextureStages.push_back({ semantic, TextureDefinition{ std::move(texturePath) } });
+      materialDef.mFlags |= semantic | MaterialDefinition::TRANSPARENCY;  // NOTE: only in dli does single / separate ALBEDO texture mean TRANSPARENCY.
+    }
+    if (ReadString(node.GetChild("albedoMetallicMap"), texturePath))
+    {
+      ToUnixFileSeparators(texturePath);
+
+      if (MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO))
+      {
+        mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "albedo"));
+      }
+
+      const auto semantic = MaterialDefinition::ALBEDO | MaterialDefinition::METALLIC;
+      materialDef.mTextureStages.push_back({ semantic, TextureDefinition{ std::move(texturePath) } });
+      materialDef.mFlags |= semantic;
+    }
+
+    if (ReadString(node.GetChild("metallicRoughnessMap"), texturePath))
+    {
+      ToUnixFileSeparators(texturePath);
+
+      if (MaskMatch(materialDef.mFlags, MaterialDefinition::METALLIC))
+      {
+        mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "metallic"));
+      }
+
+      const auto semantic = MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS;
+      materialDef.mTextureStages.push_back({ semantic, TextureDefinition{ std::move(texturePath) } });
+      materialDef.mFlags |= semantic |
+        // We have a metallic-roughhness map and the first texture did not have albedo semantics - we're in the transparency workflow.
+        (MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO) * MaterialDefinition::TRANSPARENCY);
+    }
+
+    if (ReadString(node.GetChild("normalMap"), texturePath))
+    {
+      ToUnixFileSeparators(texturePath);
+
+      const auto semantic = MaterialDefinition::NORMAL;
+      materialDef.mTextureStages.push_back({ semantic, TextureDefinition{ std::move(texturePath) } });
+      materialDef.mFlags |= semantic |
+        // We have a standalone normal map and the first texture did not have albedo semantics - we're in the transparency workflow.
+        (MaskMatch(materialDef.mFlags, MaterialDefinition::ALBEDO) * MaterialDefinition::TRANSPARENCY);
+    }
+
+    if (ReadString(node.GetChild("normalRoughnessMap"), texturePath))
+    {
+      ToUnixFileSeparators(texturePath);
+
+      if (MaskMatch(materialDef.mFlags, MaterialDefinition::NORMAL))
+      {
+        mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "normal"));
+      }
+
+      if (MaskMatch(materialDef.mFlags, MaterialDefinition::ROUGHNESS))
+      {
+        mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "roughness"));
+      }
+
+      if (MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY))
+      {
+        mOnError(FormatString("material %d: conflicting semantics; already set %s.", resources.mMaterials.size(), "transparency"));
+      }
+
+      const auto semantic = MaterialDefinition::NORMAL | MaterialDefinition::ROUGHNESS;
+      materialDef.mTextureStages.push_back({ semantic, TextureDefinition{ std::move(texturePath) } });
+      materialDef.mFlags |= semantic;
+    }
+
+    if (ReadString(node.GetChild("subsurfaceMap"), texturePath))
+    {
+      ToUnixFileSeparators(texturePath);
+
+      const auto semantic = MaterialDefinition::SUBSURFACE;
+      materialDef.mTextureStages.push_back({ semantic, TextureDefinition{ std::move(texturePath) } });
+      materialDef.mFlags |= semantic;
+    }
+
+    if (ReadColorCodeOrColor(&node, materialDef.mColor, convertColorCode) &&
+      materialDef.mColor.a < 1.0f)
+    {
+      materialDef.mFlags |= MaterialDefinition::TRANSPARENCY;
+    }
+
+    ReadFloat(node.GetChild("metallic"), materialDef.mMetallic);
+    ReadFloat(node.GetChild("roughness"), materialDef.mRoughness);
+
+    bool mipmaps;
+    if (ReadBool(node.GetChild("mipmap"), mipmaps) && mipmaps)
+    {
+      for (auto& ts : materialDef.mTextureStages)
+      {
+        ts.mTexture.mSamplerFlags |= SamplerFlags::FILTER_MIPMAP_LINEAR;
+      }
+    }
+
+    resources.mMaterials.emplace_back(std::move(materialDef), TextureSet());
+  }
+}
+
+void DliLoader::Impl::ParseNodes(const TreeNode* const nodes, Index index, LoadParams& params)
+{
+  std::vector<Index> parents;
+  parents.reserve(8);
+
+  struct IndexMapper : IIndexMapper
+  {
+    IndexMapper(size_t numNodes)
+    {
+      mIndices.reserve(numNodes);
+    }
+
+    virtual bool Map(Index iDli, Index iScene) override
+    {
+      Entry idx{ iDli, iScene };
+      auto iInsert = std::lower_bound(mIndices.begin(), mIndices.end(), idx);
+      if (iInsert == mIndices.end() || iInsert->iDli != iDli)
+      {
+        mIndices.insert(iInsert, idx);
+      }
+      else if (iInsert->iScene != iScene)
+      {
+        return false;
+      }
+      return true;
+    }
+
+    virtual unsigned int Resolve(Index iDli) override
+    {
+      auto iFind = std::lower_bound(mIndices.begin(), mIndices.end(), iDli,
+        [](const Entry& idx, Index iDli) {
+          return idx.iDli < iDli;
+        });
+      DALI_ASSERT_ALWAYS(iFind != mIndices.end());
+      return iFind->iScene;
+    }
+
+  private:
+    struct Entry
+    {
+      unsigned int iDli;
+      unsigned int iScene;
+
+      bool operator<(const Entry& other) const
+      {
+        return iDli < other.iDli;
+      }
+    };
+    std::vector<Entry> mIndices;
+  } mapper(nodes->Size());
+  ParseNodesInternal(nodes, index, parents, params, mapper);
+
+  auto& scene = params.output.mScene;
+  for (size_t i0 = 0, i1 = scene.GetNodeCount(); i0 < i1; ++i0)
+  {
+    for (auto& c : scene.GetNode(i0)->mConstraints)
+    {
+      c.mSourceIdx = mapper.Resolve(c.mSourceIdx);
+    }
+  }
+}
+
+void DliLoader::Impl::ParseNodesInternal(const TreeNode* const nodes, Index index,
+  std::vector<Index>& inOutParentStack, LoadParams& params, IIndexMapper& mapper)
+{
+  // Properties that may be resolved from a JSON value with ReadInt() -- or default to 0.
+  struct IndexProperty { ResourceType::Value type; const TreeNode* source; Index& target; };
+  std::vector<IndexProperty> resourceIds;
+  resourceIds.reserve(4);
+
+  if (auto node = GetNthChild(nodes, index))
+  {
+    NodeDefinition nodeDef;
+    nodeDef.mParentIdx = inOutParentStack.empty() ? INVALID_INDEX : inOutParentStack.back();
+
+    // name
+    ReadString(node->GetChild(NAME), nodeDef.mName);
+
+    // transform
+    ReadModelTransform(node, nodeDef.mOrientation, nodeDef.mPosition, nodeDef.mScale);
+
+    // Reads the size of the node.
+    //
+    // * It can be given as 'size' or 'bounds'.
+    // * The sdk saves the 'size' as a vector2 in some cases.
+    // * To avoid size related issues the following code attemps
+    //   to read the 'size/bounds' as a vector3 first, if it's
+    //   not successful then reads it as a vector2.
+    ReadVector(node->GetChild("size"), nodeDef.mSize.AsFloat(), 3) ||
+      ReadVector(node->GetChild("size"), nodeDef.mSize.AsFloat(), 2) ||
+        ReadVector(node->GetChild("bounds"), nodeDef.mSize.AsFloat(), 3) ||
+          ReadVector(node->GetChild("bounds"), nodeDef.mSize.AsFloat(), 2);
+
+    // visibility
+    ReadBool(node->GetChild("visible"), nodeDef.mIsVisible);
+
+    // type classification
+    if (auto eCustomization = node->GetChild("customization"))  // customization
+    {
+      std::string tag;
+      if (ReadString(eCustomization->GetChild("tag"), tag))
+      {
+        nodeDef.mCustomization.reset(new NodeDefinition::CustomizationDefinition{ tag });
+      }
+    }
+    else // something renderable maybe
+    {
+      std::unique_ptr<NodeDefinition::Renderable> renderable;
+      ModelNode* modelNode = nullptr;  // no ownership, aliasing renderable for the right type.
+
+      const TreeNode*  eRenderable = nullptr;
+      if ((eRenderable = node->GetChild("model")))
+      {
+        // check for mesh before allocating - this can't be missing.
+        auto eMesh = eRenderable->GetChild("mesh");
+        if (!eMesh)
+        {
+          ExceptionFlinger(ASSERT_LOCATION) << "node " << nodeDef.mName << ": Missing mesh definition.";
+        }
+
+        modelNode = new ModelNode();
+        renderable.reset(modelNode);
+
+        resourceIds.push_back({ ResourceType::Mesh, eMesh, modelNode->mMeshIdx });
+      }
+      else if ((eRenderable = node->GetChild("arc")))
+      {
+        // check for mesh before allocating - this can't be missing.
+        auto eMesh = eRenderable->GetChild("mesh");
+        if (!eMesh)
+        {
+          ExceptionFlinger(ASSERT_LOCATION) << "node " << nodeDef.mName << ": Missing mesh definition.";
+        }
+
+        auto arcNode = new ArcNode;
+        renderable.reset(arcNode);
+        modelNode = arcNode;
+
+        resourceIds.push_back({ ResourceType::Mesh, eMesh, arcNode->mMeshIdx });
+
+        ReadArcField(eRenderable, *arcNode);
+      }
+
+      if (renderable)  // process common properties of all renderables + register payload
+      {
+        // shader
+        renderable->mShaderIdx = 0;
+        auto eShader = eRenderable->GetChild("shader");
+        resourceIds.push_back({ ResourceType::Shader, eShader, renderable->mShaderIdx });
+
+        // color
+        if (modelNode)
+        {
+          modelNode->mMaterialIdx = 0;  // must offer default of 0
+          auto eMaterial = eRenderable->GetChild("material");
+          resourceIds.push_back({ ResourceType::Material, eMaterial, modelNode->mMaterialIdx });
+
+          if (!ReadColorCodeOrColor(eRenderable, modelNode->mColor, params.input.mConvertColorCode))
+          {
+            ReadColorCodeOrColor(node, modelNode->mColor, params.input.mConvertColorCode);
+          }
+        }
+
+        nodeDef.mRenderable = std::move(renderable);
+      }
+    }
+
+    // Resolve ints - default to 0 if undefined
+    auto& output = params.output;
+    for (auto& idRes : resourceIds)
+    {
+      Index iCheck = 0;
+      switch (idRes.type)
+      {
+      case ResourceType::Shader:
+        iCheck = output.mResources.mShaders.size();
+        break;
+
+      case ResourceType::Mesh:
+        iCheck = output.mResources.mMeshes.size();
+        break;
+
+      case ResourceType::Material:
+        iCheck = output.mResources.mMaterials.size();
+        break;
+
+      default:
+        ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": Invalid resource type: " <<
+          idRes.type << " (Programmer error)";
+      }
+
+      if (!idRes.source)
+      {
+        idRes.target = 0;
+      }
+      else if (idRes.source->GetType() != TreeNode::INTEGER)
+      {
+        ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": Invalid " <<
+          GetResourceTypeName(idRes.type) << " index type.";
+      }
+      else
+      {
+        idRes.target = idRes.source->GetInteger();
+      }
+
+      if (idRes.target >= iCheck)
+      {
+        ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ": " <<
+          GetResourceTypeName(idRes.type) << " index " << idRes.target << " out of bounds (" <<
+          iCheck << ").";
+      }
+    }
+    resourceIds.clear();
+
+    // Extra properties
+    if (auto eExtras = node->GetChild("extras"))
+    {
+      auto& extras = nodeDef.mExtras;
+      extras.reserve(eExtras->Size());
+
+      NodeDefinition::Extra e;
+      for (auto i0 = eExtras->CBegin(), i1 = eExtras->CEnd(); i0 != i1; ++i0)
+      {
+        auto eExtra = *i0;
+        e.mKey = eExtra.first;
+        if (e.mKey.empty())
+        {
+          mOnError(FormatString("node %d: empty string is invalid for name of extra %d; ignored.",
+            index, extras.size()));
+          continue;
+        }
+
+        e.mValue = ReadPropertyValue(eExtra.second);
+        if (e.mValue.GetType() == Property::Type::NONE)
+        {
+          mOnError(FormatString("node %d: failed to interpret value of extra '%s' : %s; ignored.",
+            index, e.mKey.c_str(), eExtra.second.GetString()));
+        }
+        else
+        {
+          auto iInsert = std::lower_bound(extras.begin(), extras.end(), e);
+          if (iInsert != extras.end() && iInsert->mKey == e.mKey)
+          {
+            mOnError(FormatString("node %d: extra '%s' already defined; overriding with %s.",
+              index, e.mKey.c_str(), eExtra.second.GetString()));
+            *iInsert = std::move(e);
+          }
+          else
+          {
+            extras.insert(iInsert, e);
+          }
+        }
+      }
+    }
+
+    // Constraints
+    if (auto eConstraints = node->GetChild("constraints"))
+    {
+      auto& constraints = nodeDef.mConstraints;
+      constraints.reserve(eConstraints->Size());
+
+      ConstraintDefinition cDef;
+      for (auto i0 = eConstraints->CBegin(), i1 = eConstraints->CEnd(); i0 != i1; ++i0)
+      {
+        auto eConstraint = *i0;
+        if (!ReadIndex(&eConstraint.second, cDef.mSourceIdx))
+        {
+          mOnError(FormatString("node %d: node ID %s for constraint %d is invalid; ignored.",
+            index, eConstraint.second.GetString(), constraints.size()));
+        }
+        else
+        {
+          cDef.mProperty = eConstraint.first;
+
+          auto iInsert = std::lower_bound(constraints.begin(), constraints.end(), cDef);
+          if (iInsert != constraints.end() && *iInsert == cDef)
+          {
+            mOnError(FormatString("node %d: constraint %s@%d already defined; ignoring.",
+              index, cDef.mProperty.c_str(), cDef.mSourceIdx));
+          }
+          else
+          {
+            constraints.insert(iInsert, cDef);
+          }
+        }
+      }
+    }
+
+    // Determine index for mapping
+    const unsigned int myIndex = output.mScene.GetNodeCount();
+    if (!mapper.Map(index, myIndex))
+    {
+      mOnError(FormatString("node %d: error mapping dli index %d: node has multiple parents. Ignoring subtree."));
+      return;
+    }
+
+    // if the node is a bone in a skeletal animation, it will have the inverse bind pose matrix.
+    Matrix invBindMatrix{ false };
+    if (ReadVector(node->GetChild("inverseBindPoseMatrix"), invBindMatrix.AsFloat(), 16u))  // TODO: more robust error checking?
+    {
+      mInverseBindMatrices[myIndex] = invBindMatrix;
+    }
+
+    // Register nodeDef
+    auto rawDef = output.mScene.AddNode(std::make_unique<NodeDefinition>(std::move(nodeDef)));
+    if (rawDef)  // NOTE: no ownership. Guaranteed to stay in scope.
+    {
+      // ...And only then parse children.
+      if (auto children = node->GetChild("children"))
+      {
+        inOutParentStack.push_back(myIndex);
+
+        rawDef->mChildren.reserve(children->Size());
+
+        uint32_t iChild = 0;
+        for (auto j0 = children->CBegin(), j1 = children->CEnd(); j0 != j1; ++j0, ++iChild)
+        {
+          auto& child = (*j0).second;
+          if (child.GetType() == TreeNode::INTEGER)
+          {
+            ParseNodesInternal(nodes, child.GetInteger(), inOutParentStack, params, mapper); // child object is created in scene definition.
+          }
+          else
+          {
+            ExceptionFlinger(ASSERT_LOCATION) << "node " << index << ", child " << iChild <<
+              ": invalid index type.";
+          }
+        }
+
+        inOutParentStack.pop_back();
+      }
+      else if (rawDef->mCustomization)
+      {
+        mOnError(FormatString("node %d: not an actual customization without children.", index));
+      }
+
+      if (auto proc = params.input.mNodePropertyProcessor)  // optional processing
+      {
+        // WARNING: constraint IDs are not resolved at this point.
+        Property::Map nodeData;
+        ParseProperties(*node, nodeData);
+        proc(*rawDef, std::move(nodeData), mOnError);
+      }
+    }
+    else
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << "Node " << index << ": name already used.";
+    }
+  }
+}
+
+void DliLoader::Impl::ParseAnimations(const TreeNode* tnAnimations, LoadParams& params)
+{
+  auto& definitions = params.output.mAnimationDefinitions;
+  definitions.reserve(definitions.size() + tnAnimations->Size());
+
+  for (TreeNode::ConstIterator iAnim = tnAnimations->CBegin(), iAnimEnd = tnAnimations->CEnd();
+    iAnim != iAnimEnd; ++iAnim)
+  {
+    const TreeNode& tnAnim = (*iAnim).second;
+    AnimationDefinition animDef;
+    ReadString(tnAnim.GetChild(NAME), animDef.mName);
+
+    auto iFind = std::lower_bound(definitions.begin(), definitions.end(), animDef,
+      [](const AnimationDefinition& ad0, const AnimationDefinition& ad1) {
+      return ad0.mName < ad1.mName;
+    });
+    const bool overwrite = iFind != definitions.end() && iFind->mName == animDef.mName;
+    if (overwrite)
+    {
+      mOnError(FormatString("Pre-existing animation with name '%s' is being overwritten.", animDef.mName.c_str()));
+    }
+
+    // Duration -- We need something that animated properties' delay / duration can
+    // be expressed as a multiple of; 0 won't work. This is small enough (i.e. shorter
+    // than our frame delay) to not be restrictive WRT replaying. If anything needs
+    // to occur more frequently, then Animations are likely not your solution anyway.
+    animDef.mDuration = AnimationDefinition::MIN_DURATION_SECONDS;
+    if (!ReadFloat(tnAnim.GetChild("duration"), animDef.mDuration))
+    {
+      mOnError(FormatString("Animation '%s' fails to define '%s', defaulting to %f.",
+        animDef.mName.c_str(), "duration", animDef.mDuration));
+    }
+
+    // Get loop count - # of playbacks. Default is once. 0 means repeat indefinitely.
+    animDef.mLoopCount = 1;
+    if (ReadInt(tnAnim.GetChild("loopCount"), animDef.mLoopCount) &&
+      animDef.mLoopCount < 0)
+    {
+      animDef.mLoopCount = 0;
+    }
+
+    std::string endAction;
+    if (ReadString(tnAnim.GetChild("endAction"), endAction))
+    {
+      if ("BAKE" == endAction)
+      {
+        animDef.mEndAction = Animation::BAKE;
+      }
+      else if ("DISCARD" == endAction)
+      {
+        animDef.mEndAction = Animation::DISCARD;
+      }
+      else if ("BAKE_FINAL" == endAction)
+      {
+        animDef.mEndAction = Animation::BAKE_FINAL;
+      }
+    }
+
+    if (ReadString(tnAnim.GetChild("disconnectAction"), endAction))
+    {
+      if ("BAKE" == endAction)
+      {
+        animDef.mDisconnectAction = Animation::BAKE;
+      }
+      else if ("DISCARD" == endAction)
+      {
+        animDef.mDisconnectAction = Animation::DISCARD;
+      }
+      else if ("BAKE_FINAL" == endAction)
+      {
+        animDef.mDisconnectAction = Animation::BAKE_FINAL;
+      }
+    }
+
+    if (const TreeNode* tnProperties = tnAnim.GetChild("properties"))
+    {
+      animDef.mProperties.reserve(tnProperties->Size());
+      for (TreeNode::ConstIterator iProperty = tnProperties->CBegin(), iPropertyEnd = tnProperties->CEnd();
+        iProperty != iPropertyEnd; ++iProperty)
+      {
+        const TreeNode &tnProperty = (*iProperty).second;
+
+        AnimatedProperty animProp;
+        if (!ReadString(tnProperty.GetChild("node"), animProp.mNodeName))
+        {
+          mOnError(FormatString("Animation '%s': Failed to read the 'node' tag.", animDef.mName.c_str()));
+          continue;
+        }
+
+        if (!ReadString(tnProperty.GetChild("property"), animProp.mPropertyName))
+        {
+          mOnError(FormatString("Animation '%s': Failed to read the 'property' tag", animDef.mName.c_str()));
+          continue;
+        }
+
+        // these are the defaults
+        animProp.mTimePeriod.delaySeconds = 0.f;
+        animProp.mTimePeriod.durationSeconds = animDef.mDuration;
+        if (!ReadTimePeriod(tnProperty.GetChild("timePeriod"), animProp.mTimePeriod))
+        {
+          mOnError(FormatString("Animation '%s': timePeriod missing in Property #%d: defaulting to %f.",
+            animDef.mName.c_str(), animDef.mProperties.size(), animProp.mTimePeriod.durationSeconds));
+        }
+
+        std::string alphaFunctionValue;
+        if (ReadString(tnProperty.GetChild("alphaFunction"), alphaFunctionValue))
+        {
+          animProp.mAlphaFunction = GetAlphaFunction(alphaFunctionValue);
+        }
+
+        if (const TreeNode* tnKeyFramesBin = tnProperty.GetChild("keyFramesBin"))
+        {
+          DALI_ASSERT_ALWAYS(!animProp.mPropertyName.empty() && "Animation must specify a property name");
+
+          std::ifstream binAniFile;
+          std::string animationFilename;
+          if (ReadString(tnKeyFramesBin->GetChild(URL), animationFilename))
+          {
+            std::string animationFullPath = params.input.mAnimationsPath + animationFilename;
+            binAniFile.open(animationFullPath, std::ios::binary);
+            if (binAniFile.fail())
+            {
+              ExceptionFlinger(ASSERT_LOCATION) << "Failed to open animation data '" <<
+                animationFullPath << "'";
+            }
+          }
+
+          int byteOffset = 0;
+          ReadInt(tnKeyFramesBin->GetChild("byteOffset"), byteOffset);
+          DALI_ASSERT_ALWAYS(byteOffset >= 0);
+
+          binAniFile.seekg(byteOffset, std::ios::beg);
+
+          int numKeys = 0;
+          ReadInt(tnKeyFramesBin->GetChild("numKeys"), numKeys);
+          DALI_ASSERT_ALWAYS(numKeys >= 0);
+
+          animProp.mKeyFrames = KeyFrames::New();
+
+          //In binary animation file only is saved the position, rotation, scale and blend shape weight keys.
+          //so, if it is vector3 we assume is position or scale keys, if it is vector4 we assume is rotation,
+          // otherwise are blend shape weight keys.
+          // TODO support for binary header with size information
+          Property::Type propType = Property::FLOAT;  // assume blend shape weights
+          if (animProp.mPropertyName == "orientation")
+          {
+            propType = Property::VECTOR4;
+          }
+          else if ((animProp.mPropertyName == "position") || (animProp.mPropertyName == "scale"))
+          {
+            propType = Property::VECTOR3;
+          }
+
+          //alphafunction is reserved for future implementation
+          // NOTE: right now we're just using AlphaFunction::LINEAR.
+          unsigned char dummyAlphaFunction;
+
+          float progress;
+          Property::Value propValue;
+          for (int key = 0; key < numKeys; key++)
+          {
+            binAniFile.read(reinterpret_cast<char*>(&progress), sizeof(float));
+            if (propType == Property::VECTOR3)
+            {
+              Vector3 value;
+              binAniFile.read(reinterpret_cast<char*>(value.AsFloat()), sizeof(float) * 3);
+              propValue = Property::Value(value);
+            }
+            else if (propType == Property::VECTOR4)
+            {
+              Vector4 value;
+              binAniFile.read(reinterpret_cast<char*>(value.AsFloat()), sizeof(float) * 4);
+              propValue = Property::Value(Quaternion(value));
+            }
+            else
+            {
+              float value;
+              binAniFile.read(reinterpret_cast<char*>(&value), sizeof(float));
+              propValue = Property::Value(value);
+            }
+
+            binAniFile.read(reinterpret_cast<char*>(&dummyAlphaFunction), sizeof(unsigned char));
+
+            animProp.mKeyFrames.Add(progress, propValue, AlphaFunction::LINEAR);
+          }
+        }
+        else if (const TreeNode* tnKeyFrames = tnProperty.GetChild("keyFrames"))
+        {
+          DALI_ASSERT_ALWAYS(!animProp.mPropertyName.empty() && "Animation must specify a property name");
+          animProp.mKeyFrames = KeyFrames::New();
+
+          float progress = 0.0f;
+          for (auto i0 = tnKeyFrames->CBegin(), i1 = tnKeyFrames->CEnd(); i1 != i0; ++i0)
+          {
+            const TreeNode::KeyNodePair& kfKeyChild = *i0;
+            bool readResult = ReadFloat(kfKeyChild.second.GetChild("progress"), progress);
+            DALI_ASSERT_ALWAYS(readResult && "Key frame entry must have 'progress'");
+
+            const TreeNode* tnValue = kfKeyChild.second.GetChild("value");
+            DALI_ASSERT_ALWAYS(tnValue && "Key frame entry must have 'value'");
+
+            // For the "orientation" property, convert from Vector4 -> Rotation value
+            // This work-around is preferable to a null-pointer exception in the DALi update thread
+            Property::Value propValue(ReadPropertyValue(*tnValue));
+            if (propValue.GetType() == Property::VECTOR4 &&
+              animProp.mPropertyName == "orientation")
+            {
+              Vector4 v;
+              propValue.Get(v);
+              propValue = Property::Value(Quaternion(v.w, v.x, v.y, v.z));
+            }
+
+            AlphaFunction kfAlphaFunction(AlphaFunction::DEFAULT);
+            std::string alphaFuncStr;
+            if (ReadString(kfKeyChild.second.GetChild("alphaFunction"), alphaFuncStr))
+            {
+              kfAlphaFunction = GetAlphaFunction(alphaFuncStr);
+            }
+
+            animProp.mKeyFrames.Add(progress, propValue, kfAlphaFunction);
+          }
+        }
+        else
+        {
+          const TreeNode* tnValue = tnProperty.GetChild("value");
+          if (tnValue)
+          {
+            animProp.mValue.reset(new AnimatedProperty::Value{ ReadPropertyValue(*tnValue) });
+          }
+          else
+          {
+            mOnError(FormatString("Property '%s' fails to define target value.",
+              animProp.mPropertyName.c_str()));
+          }
+
+          ReadBool(tnProperty.GetChild("relative"), animProp.mValue->mIsRelative);
+        }
+
+        animDef.mProperties.push_back(std::move(animProp));
+      }
+    }
+
+    if (overwrite)
+    {
+      *iFind = std::move(animDef);
+    }
+    else
+    {
+      iFind = definitions.insert(iFind, std::move(animDef));
+    }
+
+    if (auto proc = params.input.mAnimationPropertyProcessor)  // optional processing
+    {
+      Property::Map map;
+      ParseProperties(tnAnim, map);
+      proc(animDef, std::move(map), mOnError);
+    }
+  }
+}
+
+void DliLoader::Impl::ParseAnimationGroups(const Toolkit::TreeNode* tnAnimationGroups, LoadParams& params)
+{
+  auto& animGroups = params.output.mAnimationGroupDefinitions;
+
+  int numGroups = 0;
+  for (auto iGroups = tnAnimationGroups->CBegin(), iGroupsEnd = tnAnimationGroups->CEnd();
+    iGroups != iGroupsEnd; ++iGroups, ++numGroups)
+  {
+    const auto& tnGroup = *iGroups;
+    auto tnName = tnGroup.second.GetChild(NAME);
+    std::string groupName;
+    if (!tnName || !ReadString(tnName, groupName))
+    {
+      mOnError(FormatString("Failed to get the name for the Animation group %d; ignoring.", numGroups));
+      continue;
+    }
+
+    auto iFind = std::lower_bound(animGroups.begin(), animGroups.end(), groupName,
+      [](const AnimationGroupDefinition& group, const std::string& name) {
+      return group.mName < name;
+    });
+    if (iFind != animGroups.end() && iFind->mName == groupName)
+    {
+      mOnError(FormatString("Animation group with name '%s' already exists; new entries will be merged.", groupName.c_str()));
+    }
+    else
+    {
+      iFind = animGroups.insert(iFind, AnimationGroupDefinition{});
+    }
+
+    iFind->mName = groupName;
+
+    auto tnAnims = tnGroup.second.GetChild("animations");
+    if (tnAnims && tnAnims->Size() > 0)
+    {
+      auto& anims = iFind->mAnimations;
+      anims.reserve(anims.size() + tnAnims->Size());
+      for (auto iAnims = tnAnims->CBegin(), iAnimsEnd = tnAnims->CEnd(); iAnims != iAnimsEnd; ++iAnims)
+      {
+        anims.push_back((*iAnims).second.GetString());
+      }
+    }
+  }
+}
+
+void DliLoader::Impl::GetCameraParameters(std::vector<CameraParameters>& cameras) const
+{
+  if (const TreeNode* jsonCameras = mParser.GetRoot()->GetChild("cameras"))
+  {
+    cameras.resize(jsonCameras->Size());
+    auto iCamera = cameras.begin();
+    for (auto i0 = jsonCameras->CBegin(), i1 = jsonCameras->CEnd(); i0 != i1; ++i0)
+    {
+      auto& jsonCamera = (*i0).second;
+
+      ReadFloat(jsonCamera.GetChild("fov"), iCamera->yFov);
+      ReadFloat(jsonCamera.GetChild("near"), iCamera->zNear);
+      ReadFloat(jsonCamera.GetChild("far"), iCamera->zFar);
+      if (ReadVector(jsonCamera.GetChild("orthographic"), iCamera->orthographicSize.AsFloat(), 4u))
+      {
+        iCamera->isPerspective = false;
+      }
+
+      if (auto jsonMatrix = jsonCamera.GetChild("matrix"))
+      {
+        ReadVector(jsonMatrix, iCamera->matrix.AsFloat(), 16u);
+      }
+
+      ++iCamera;
+    }
+  }
+}
+
+void DliLoader::Impl::GetLightParameters(std::vector<LightParameters>& lights) const
+{
+  if (const TreeNode* jsonLights = mParser.GetRoot()->GetChild("lights"))
+  {
+    lights.resize(jsonLights->Size());
+    auto iLight = lights.begin();
+    for (auto i0 = jsonLights->CBegin(), i1 = jsonLights->CEnd(); i0 != i1; ++i0)
+    {
+      auto& jsonLight = (*i0).second;
+      if (!ReadVector(jsonLight.GetChild("matrix"), iLight->transform.AsFloat(), 16))
+      {
+        mOnError(
+          FormatString("Failed to parse light %d - \"matrix\" child with 16 floats expected.\n",
+            std::distance(jsonLights->CBegin(), i0)));
+        continue;
+      }
+
+      int shadowMapSize = 0;
+      if (ReadInt(jsonLight.GetChild(SHADOW_MAP_SIZE), shadowMapSize) && shadowMapSize < 0)
+      {
+        mOnError(
+          FormatString("Failed to parse light %d - %s has an invalid value.",
+            std::distance(jsonLights->CBegin(), i0), SHADOW_MAP_SIZE));
+        continue;
+      }
+      iLight->shadowMapSize = shadowMapSize;
+
+      float orthoSize = 0.f;
+      if (ReadFloat(jsonLight.GetChild(ORTHOGRAPHIC_SIZE), orthoSize) &&
+        (orthoSize < .0f || std::isnan(orthoSize) || std::isinf(orthoSize)))
+      {
+        mOnError(
+          FormatString("Failed to parse light %d - %s has an invalid value.",
+            std::distance(jsonLights->CBegin(), i0), ORTHOGRAPHIC_SIZE));
+        continue;
+      }
+      iLight->orthographicSize = orthoSize;
+
+      if ((iLight->shadowMapSize > 0) != (iLight->orthographicSize > .0f))
+      {
+        mOnError(FormatString(
+          "Light %d: Both shadow map size and orthographic size must be set for shadows to work.",
+            std::distance(jsonLights->CBegin(), i0)));
+      }
+
+      if (!ReadVector(jsonLight.GetChild("color"), iLight->color.AsFloat(), 3))  // color is optional
+      {
+        iLight->color = Vector3::ONE;  // default to white
+      }
+
+      if (!ReadFloat(jsonLight.GetChild("intensity"), iLight->intensity))  // intensity is optional
+      {
+        iLight->intensity = 1.0f;  // default to 1.0
+      }
+
+      if (!ReadFloat(jsonLight.GetChild("shadowIntensity"), iLight->shadowIntensity))  // intensity is optional
+      {
+        iLight->shadowIntensity = 1.0f;  // default to 1.0
+      }
+
+      ++iLight;
+    }
+  }
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/dli-loader.h b/dali-scene-loader/public-api/dli-loader.h
new file mode 100644 (file)
index 0000000..e09d4d0
--- /dev/null
@@ -0,0 +1,139 @@
+#ifndef DALI_SCENE_LOADER_DLI_LOADER_H
+#define DALI_SCENE_LOADER_DLI_LOADER_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+ // INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+#include "dali-scene-loader/public-api/animation-definition.h"
+#include "dali-scene-loader/public-api/customization.h"
+#include "dali-scene-loader/public-api/string-callback.h"
+#include "dali-scene-loader/public-api/index.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/common/vector-wrapper.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+typedef std::pair<std::string, std::string> Metadata;
+
+// Forward declarations
+struct LoadResult;
+struct CameraParameters;
+struct LightParameters;
+struct TextParameters;
+
+class ResourceBundle;
+struct NodeDefinition;
+class SceneDefinition;
+
+class DALI_SCENE_LOADER_API DliLoader
+{
+public:
+  using ConvertFontCode = void(*)(const std::string& code, std::string& fontFamily, std::string& slant, std::string& weight, float& size);
+  using ConvertColorCode = Vector4(*)(const std::string& code);
+
+  using CategoryProcessor = std::function<void(Property::Array&& categoryData, StringCallback onError)>;
+  using CategoryProcessorVector = std::vector<std::pair<std::string /*name*/, CategoryProcessor>>;
+
+  using NodeProcessor = std::function<void(const NodeDefinition& nodeDef,
+    Property::Map&& nodeData, StringCallback onError)>;
+
+  using AnimationProcessor = std::function<void(const AnimationDefinition& animDef,
+    Property::Map&& animData, StringCallback onError)>;
+
+  struct InputParams
+  {
+    /**
+     * @brief The absolute path of animation binaries referenced in the .dli.
+     */
+    std::string mAnimationsPath;
+
+    /**
+     * @brief Provides a facility to determine a color from a code instead of RGB(A) values.
+     */
+    ConvertColorCode mConvertColorCode;
+
+    /**
+     * @brief A collection of handlers, mapped to the names of the top level (i.e. below
+     *  root) element, whom they will attempt to process. This will take place before
+     *  the parsing of scene Nodes and Animations, but after skeletons, environment, mesh,
+     *  shader and material resources.
+     */
+    CategoryProcessorVector mPreNodeCategoryProcessors;
+
+    /**
+     * @brief A collection of handlers, mapped to the names of the top level (i.e. below
+     *  root) element, whom they will attempt to process. This will take place after
+     *  the parsing of the scene Nodes and Animations.
+     */
+    CategoryProcessorVector mPostNodeCategoryProcessors;
+
+    /**
+     * @brief Provides an extension point to nodes. If provided, this function will be
+     *  called with each JSON element and definition, of a scene node.
+     * @note Constraints rely on ID resolution (from .dli to scene definition), which
+     *  takes place after the parsing of the nodes; therefore AT THIS POINT the node
+     *  IDs seen in constraints will still be the .dli IDs - NOT to be relied on for
+     *  indexing into mScene.
+     */
+    NodeProcessor mNodePropertyProcessor;
+
+    /**
+     * @brief Provides an extension point to animations. If provided, this function will be
+     *  called with each JSON element and fully processed definition, of an animation.
+     */
+    AnimationProcessor mAnimationPropertyProcessor;
+  };
+
+  struct LoadParams
+  {
+    InputParams const& input;
+    LoadResult& output;
+  };
+
+  DliLoader();
+  ~DliLoader();
+
+  /**
+   * @brief Sets the callback that messages from non-fatal errors get posted to.
+   *  Uses DefaultErrorCallback by default.
+   */
+  void SetErrorCallback(StringCallback onError);
+
+  /**
+   * @brief Attempts to load and parse a .dli document into a DOM tree.
+   * @return Whether the operation was successful.
+   */
+  bool LoadScene(const std::string& uri, LoadParams& params);
+
+  /**
+   * @return The error string describing how the parse has failed, if any.
+   */
+  std::string GetParseError() const;
+
+private:
+  struct Impl;
+  const std::unique_ptr<Impl> mImpl;
+};
+
+}
+}
+
+#endif // DALI_SCENE_LOADER_DLI_LOADER_H
diff --git a/dali-scene-loader/public-api/environment-definition.cpp b/dali-scene-loader/public-api/environment-definition.cpp
new file mode 100644 (file)
index 0000000..16c8e2c
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/environment-definition.h"
+#include "dali-scene-loader/public-api/utils.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+EnvironmentDefinition::RawData
+  EnvironmentDefinition::LoadRaw(const std::string& environmentsPath) const
+{
+  RawData raw;
+  auto loadFn = [&environmentsPath](const std::string& path, CubeData& cd) {
+    if (path.empty())
+    {
+      cd.data.resize(6);
+      for (auto& face : cd.data)
+      {
+        face.push_back(PixelData::New(new uint8_t[3]{ 0xff, 0xff, 0xff }, 3, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY));
+      }
+    }
+    else if(!LoadCubeMapData(environmentsPath + path, cd))
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << "Failed to load cubemap texture from '" <<
+        path << "'.";
+    }
+  };
+
+  loadFn(mDiffuseMapPath, raw.mDiffuse);
+  loadFn(mSpecularMapPath, raw.mSpecular);
+  return raw;
+}
+
+EnvironmentDefinition::Textures EnvironmentDefinition::Load(RawData&& raw) const
+{
+  Textures textures;
+
+  // This texture should have 6 faces and only one mipmap
+  if (!raw.mDiffuse.data.empty())
+  {
+    textures.mDiffuse = raw.mDiffuse.CreateTexture();
+  }
+
+  // This texture should have 6 faces and 6 mipmaps
+  if (!raw.mSpecular.data.empty())
+  {
+    textures.mSpecular = raw.mSpecular.CreateTexture();
+  }
+  return textures;
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/environment-definition.h b/dali-scene-loader/public-api/environment-definition.h
new file mode 100644 (file)
index 0000000..1b1906f
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef DALI_SCENE_LOADER_ENVIRONMENT_DEFINITION_H
+#define DALI_SCENE_LOADER_ENVIRONMENT_DEFINITION_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+ // INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+#include "dali-scene-loader/public-api/ktx-loader.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/rendering/texture.h"
+#include "dali/public-api/math/quaternion.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/**
+ * @brief Defines an environment map with either or both of radiance
+ *  and irradiance maps.
+ */
+struct DALI_SCENE_LOADER_API EnvironmentDefinition
+{
+  struct Textures
+  {
+    Texture mDiffuse;  // irradiance
+    Texture mSpecular;  // radiance
+
+    bool IsLoaded() const
+    {
+      return mDiffuse || mSpecular;
+    }
+  };
+
+  struct RawData
+  {
+    CubeData mDiffuse;
+    CubeData mSpecular;
+  };
+
+  using EnvironmentData = std::pair<EnvironmentDefinition, Textures>;
+  using Vector = std::vector<EnvironmentData>;
+
+  EnvironmentDefinition() = default;
+
+  EnvironmentDefinition(const EnvironmentDefinition&) = delete;
+  EnvironmentDefinition& operator=(const EnvironmentDefinition&) = delete;
+
+  EnvironmentDefinition(EnvironmentDefinition&&) = default;
+  EnvironmentDefinition& operator=(EnvironmentDefinition&&) = default;
+
+  /**
+   * @brief Loads raw pixel data for the given diffuse and specular maps.
+   * @note This can be done on any thread.
+   */
+  RawData LoadRaw(const std::string& environmentsPath) const;
+
+  /**
+   * @brief Creates DALi cubemap Textures from the pixel data in @a raw, then
+   *  returns them in a Textures object.
+   * @note This must only be called from the event thread.
+   */
+  Textures Load(RawData&& raw) const;
+
+public: // DATA
+  std::string mDiffuseMapPath;
+  std::string mSpecularMapPath;
+  Quaternion mCubeOrientation = Quaternion::IDENTITY;
+  float mIblIntensity = 1.0f;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_ENVIRONMENT_DEFINITION_H
diff --git a/dali-scene-loader/public-api/file.list b/dali-scene-loader/public-api/file.list
new file mode 100644 (file)
index 0000000..9c5fcbf
--- /dev/null
@@ -0,0 +1,28 @@
+set(scene_loader_public_api_dir "${scene_loader_dir}/public-api")
+
+set(scene_loader_src_files ${scene_loader_src_files}
+       ${scene_loader_public_api_dir}/alpha-function-helper.cpp
+       ${scene_loader_public_api_dir}/animated-property.cpp
+       ${scene_loader_public_api_dir}/animation-definition.cpp
+       ${scene_loader_public_api_dir}/blend-shape-details.cpp
+       ${scene_loader_public_api_dir}/camera-parameters.cpp
+       ${scene_loader_public_api_dir}/customization.cpp
+       ${scene_loader_public_api_dir}/dli-loader.cpp
+       ${scene_loader_public_api_dir}/environment-definition.cpp
+       ${scene_loader_public_api_dir}/gltf2-loader.cpp
+       ${scene_loader_public_api_dir}/ktx-loader.cpp
+       ${scene_loader_public_api_dir}/material-definition.cpp
+       ${scene_loader_public_api_dir}/matrix-stack.cpp
+       ${scene_loader_public_api_dir}/mesh-definition.cpp
+       ${scene_loader_public_api_dir}/node-definition.cpp
+       ${scene_loader_public_api_dir}/parse-renderer-state.cpp
+       ${scene_loader_public_api_dir}/renderer-state.cpp
+       ${scene_loader_public_api_dir}/resource-bundle.cpp
+       ${scene_loader_public_api_dir}/scene-definition.cpp
+       ${scene_loader_public_api_dir}/shader-definition.cpp
+       ${scene_loader_public_api_dir}/shader-definition-factory.cpp
+       ${scene_loader_public_api_dir}/skinning-details.cpp
+       ${scene_loader_public_api_dir}/string-callback.cpp
+       ${scene_loader_public_api_dir}/utils.cpp
+       ${scene_loader_public_api_dir}/view-projection.cpp
+)
diff --git a/dali-scene-loader/public-api/gltf2-loader.cpp b/dali-scene-loader/public-api/gltf2-loader.cpp
new file mode 100644 (file)
index 0000000..451060b
--- /dev/null
@@ -0,0 +1,1197 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/public-api/load-result.h"
+#include "dali-scene-loader/public-api/scene-definition.h"
+#include "dali-scene-loader/public-api/resource-bundle.h"
+#include "dali-scene-loader/public-api/gltf2-loader.h"
+#include "dali-scene-loader/public-api/utils.h"
+#include "dali-scene-loader/public-api/shader-definition-factory.h"
+#include "dali-scene-loader/internal/gltf2-asset.h"
+#include "dali/public-api/math/quaternion.h"
+#include <fstream>
+
+#define ENUM_STRING_MAPPING(t, x) { #x, t::x }
+
+namespace gt = gltf2;
+namespace js = json;
+
+namespace Dali
+{
+namespace SceneLoader
+{
+namespace
+{
+const std::string POSITION_PROPERTY("position");
+const std::string ORIENTATION_PROPERTY("orientation");
+const std::string SCALE_PROPERTY("scale");
+const std::string BLEND_SHAPE_WEIGHTS_UNIFORM("uBlendShapeWeight");
+
+const Geometry::Type GLTF2_TO_DALI_PRIMITIVES[]{
+  Geometry::POINTS,
+  Geometry::LINES,
+  Geometry::LINE_LOOP,
+  Geometry::LINE_STRIP,
+  Geometry::TRIANGLES,
+  Geometry::TRIANGLE_STRIP,
+  Geometry::TRIANGLE_FAN
+}; //...because Dali swaps the last two.
+
+struct AttributeMapping
+{
+  gt::Attribute::Type mType;
+  MeshDefinition::Accessor MeshDefinition::* mAccessor;
+  uint16_t mElementSizeRequired;
+} ATTRIBUTE_MAPPINGS[]{
+  { gt::Attribute::NORMAL, &MeshDefinition::mNormals, sizeof(Vector3) },
+  { gt::Attribute::TANGENT, &MeshDefinition::mTangents, sizeof(Vector3) },
+  { gt::Attribute::TEXCOORD_0, &MeshDefinition::mTexCoords, sizeof(Vector2) },
+  { gt::Attribute::JOINTS_0, &MeshDefinition::mJoints0, sizeof(Vector4) },
+  { gt::Attribute::WEIGHTS_0, &MeshDefinition::mWeights0, sizeof(Vector4) },
+};
+
+std::vector<gt::Animation> ReadAnimationArray(const json_value_s& j)
+{
+  gt::Animation proxy;
+  SetRefReaderObject(proxy);
+
+  auto results = js::Read::Array<gt::Animation, js::ObjectReader<gt::Animation>::Read>(j);
+
+  for (auto& animation : results)
+  {
+    for (auto& channel : animation.mChannels)
+    {
+      channel.mSampler.UpdateVector(animation.mSamplers);
+    }
+  }
+
+  return results;
+}
+
+void ApplyAccessorMinMax(const gt::Accessor& acc, float* values)
+{
+  DALI_ASSERT_ALWAYS(acc.mMax.empty() || gt::AccessorType::ElementCount(acc.mType) == acc.mMax.size());
+  DALI_ASSERT_ALWAYS(acc.mMin.empty() || gt::AccessorType::ElementCount(acc.mType) == acc.mMin.size());
+  MeshDefinition::Blob::ApplyMinMax(acc.mMin, acc.mMax, acc.mCount, values);
+}
+
+const auto BUFFER_READER = std::move(js::Reader<gt::Buffer>()
+  .Register(*js::MakeProperty("byteLength", js::Read::Number<uint32_t>, &gt::Buffer::mByteLength))
+  .Register(*js::MakeProperty("uri", js::Read::StringView, &gt::Buffer::mUri))
+);
+
+const auto BUFFER_VIEW_READER = std::move(js::Reader<gt::BufferView>()
+  .Register(*js::MakeProperty("buffer", gt::RefReader<gt::Document>::Read<gt::Buffer, &gt::Document::mBuffers>, &gt::BufferView::mBuffer))
+  .Register(*js::MakeProperty("byteOffset", js::Read::Number<uint32_t>, &gt::BufferView::mByteOffset))
+  .Register(*js::MakeProperty("byteLength", js::Read::Number<uint32_t>, &gt::BufferView::mByteLength))
+  .Register(*js::MakeProperty("byteStride", js::Read::Number<uint32_t>, &gt::BufferView::mByteStride))
+  .Register(*js::MakeProperty("target", js::Read::Number<uint32_t>, &gt::BufferView::mTarget))
+);
+
+const auto BUFFER_VIEW_CLIENT_READER = std::move(js::Reader<gt::BufferViewClient>()
+  .Register(*js::MakeProperty("bufferView", gt::RefReader<gt::Document>::Read<gt::BufferView, &gt::Document::mBufferViews>, &gt::BufferViewClient::mBufferView))
+  .Register(*js::MakeProperty("byteOffset", js::Read::Number<uint32_t>, &gt::BufferViewClient::mByteOffset))
+);
+
+const auto COMPONENT_TYPED_BUFFER_VIEW_CLIENT_READER = std::move(js::Reader<gt::ComponentTypedBufferViewClient>()
+  .Register(*new js::Property<gt::ComponentTypedBufferViewClient, gt::Ref<gt::BufferView>>("bufferView", gt::RefReader<gt::Document>::Read<gt::BufferView, &gt::Document::mBufferViews>, &gt::ComponentTypedBufferViewClient::mBufferView))
+  .Register(*new js::Property<gt::ComponentTypedBufferViewClient, uint32_t>("byteOffset", js::Read::Number<uint32_t>, &gt::ComponentTypedBufferViewClient::mByteOffset))
+  .Register(*js::MakeProperty("componentType", js::Read::Enum<gt::Component::Type>, &gt::ComponentTypedBufferViewClient::mComponentType))
+);
+
+const auto ACCESSOR_SPARSE_READER = std::move(js::Reader<gt::Accessor::Sparse>()
+  .Register(*js::MakeProperty("count", js::Read::Number<uint32_t>, &gt::Accessor::Sparse::mCount))
+  .Register(*js::MakeProperty("indices", js::ObjectReader<gt::ComponentTypedBufferViewClient>::Read,
+    &gt::Accessor::Sparse::mIndices))
+  .Register(*js::MakeProperty("values", js::ObjectReader<gt::BufferViewClient>::Read,
+    &gt::Accessor::Sparse::mValues))
+);
+
+const auto ACCESSOR_READER = std::move(js::Reader<gt::Accessor>()
+  .Register(*new js::Property<gt::Accessor, gt::Ref<gt::BufferView>>("bufferView",
+    gt::RefReader<gt::Document>::Read<gt::BufferView, &gt::Document::mBufferViews>, &gt::Accessor::mBufferView))
+  .Register(*new js::Property<gt::Accessor, uint32_t>("byteOffset",
+    js::Read::Number<uint32_t>, &gt::Accessor::mByteOffset))
+  .Register(*new js::Property<gt::Accessor, gt::Component::Type>("componentType",
+    js::Read::Enum<gt::Component::Type>, &gt::Accessor::mComponentType))
+  .Register(*new js::Property<gt::Accessor, std::string_view>("name", js::Read::StringView, &gt::Accessor::mName))
+  .Register(*js::MakeProperty("count", js::Read::Number<uint32_t>, &gt::Accessor::mCount))
+  .Register(*js::MakeProperty("normalized", js::Read::Boolean, &gt::Accessor::mNormalized))
+  .Register(*js::MakeProperty("type", gt::ReadStringEnum<gt::AccessorType>, &gt::Accessor::mType))
+  .Register(*js::MakeProperty("min", js::Read::Array<float, js::Read::Number>, &gt::Accessor::mMin))
+  .Register(*js::MakeProperty("max", js::Read::Array<float, js::Read::Number>, &gt::Accessor::mMax))
+  .Register(*new js::Property<gt::Accessor, gt::Accessor::Sparse>("sparse", js::ObjectReader<gt::Accessor::Sparse>::Read,
+    &gt::Accessor::SetSparse))
+);
+
+const auto IMAGE_READER = std::move(js::Reader<gt::Image>()
+  .Register(*new js::Property<gt::Image, std::string_view>("name", js::Read::StringView, &gt::Material::mName))
+  .Register(*js::MakeProperty("uri", js::Read::StringView, &gt::Image::mUri))
+  .Register(*js::MakeProperty("mimeType", js::Read::StringView, &gt::Image::mMimeType))
+  .Register(*js::MakeProperty("bufferView", gt::RefReader<gt::Document>::Read<gt::BufferView, &gt::Document::mBufferViews>, &gt::Image::mBufferView))
+);
+
+const auto SAMPLER_READER = std::move(js::Reader<gt::Sampler>()
+  .Register(*js::MakeProperty("minFilter", js::Read::Enum<gt::Filter::Type>, &gt::Sampler::mMinFilter))
+  .Register(*js::MakeProperty("magFilter", js::Read::Enum<gt::Filter::Type>, &gt::Sampler::mMagFilter))
+  .Register(*js::MakeProperty("wrapS", js::Read::Enum<gt::Wrap::Type>, &gt::Sampler::mWrapS))
+  .Register(*js::MakeProperty("wrapT", js::Read::Enum<gt::Wrap::Type>, &gt::Sampler::mWrapT))
+);
+
+const auto TEXURE_READER = std::move(js::Reader<gt::Texture>()
+  .Register(*js::MakeProperty("source", gt::RefReader<gt::Document>::Read<gt::Image, &gt::Document::mImages>, &gt::Texture::mSource))
+  .Register(*js::MakeProperty("sampler", gt::RefReader<gt::Document>::Read<gt::Sampler, &gt::Document::mSamplers>, &gt::Texture::mSampler))
+);
+
+const auto TEXURE_INFO_READER = std::move(js::Reader<gt::TextureInfo>()
+  .Register(*js::MakeProperty("index", gt::RefReader<gt::Document>::Read<gt::Texture, &gt::Document::mTextures>, &gt::TextureInfo::mTexture))
+  .Register(*js::MakeProperty("texCoord", js::Read::Number<uint32_t>, &gt::TextureInfo::mTexCoord))
+  .Register(*js::MakeProperty("scale", js::Read::Number<float>, &gt::TextureInfo::mScale))
+);
+
+const auto MATERIAL_PBR_READER = std::move(js::Reader<gt::Material::Pbr>()
+  .Register(*js::MakeProperty("baseColorFactor", gt::ReadDaliVector<Vector4>, &gt::Material::Pbr::mBaseColorFactor))
+  .Register(*js::MakeProperty("baseColorTexture", js::ObjectReader<gt::TextureInfo>::Read,
+    &gt::Material::Pbr::mBaseColorTexture))
+  .Register(*js::MakeProperty("metallicFactor", js::Read::Number<float>, &gt::Material::Pbr::mMetallicFactor))
+  .Register(*js::MakeProperty("roughnessFactor", js::Read::Number<float>, &gt::Material::Pbr::mRoughnessFactor))
+  .Register(*js::MakeProperty("metallicRoughnessTexture", js::ObjectReader<gt::TextureInfo>::Read,
+    &gt::Material::Pbr::mMetallicRoughnessTexture))
+);
+
+const auto MATERIAL_READER = std::move(js::Reader<gt::Material>()
+  .Register(*new js::Property<gt::Material, std::string_view>("name", js::Read::StringView, &gt::Material::mName))
+  .Register(*js::MakeProperty("pbrMetallicRoughness", js::ObjectReader<gt::Material::Pbr>::Read, &gt::Material::mPbrMetallicRoughness))
+  .Register(*js::MakeProperty("normalTexture", js::ObjectReader<gt::TextureInfo>::Read, &gt::Material::mNormalTexture))
+  .Register(*js::MakeProperty("occlusionTexture", js::ObjectReader<gt::TextureInfo>::Read, &gt::Material::mOcclusionTexture))
+  .Register(*js::MakeProperty("emissiveTexture", js::ObjectReader<gt::TextureInfo>::Read, &gt::Material::mEmissiveTexture))
+  .Register(*js::MakeProperty("emissiveFactor", gt::ReadDaliVector<Vector3>, &gt::Material::mEmissiveFactor))
+  .Register(*js::MakeProperty("alphaMode", gt::ReadStringEnum<gt::AlphaMode>, &gt::Material::mAlphaMode))
+  .Register(*js::MakeProperty("alphaCutoff", js::Read::Number<float>, &gt::Material::mAlphaCutoff))
+);
+
+std::map<gt::Attribute::Type, gt::Ref<gt::Accessor>> ReadMeshPrimitiveAttributes(const json_value_s& j)
+{
+  auto& jo = js::Cast<json_object_s>(j);
+  std::map<gt::Attribute::Type, gt::Ref<gt::Accessor>> result;
+
+  auto i = jo.start;
+  while (i)
+  {
+    auto jstr = *i->name;
+    result[gt::Attribute::FromString(jstr.string, jstr.string_size)] = gt::RefReader<gt::Document>::Read<gt::Accessor, &gt::Document::mAccessors>(*i->value);
+    i = i->next;
+  }
+  return result;
+}
+
+std::vector<std::map<gt::Attribute::Type, gt::Ref<gt::Accessor>>> ReadMeshPrimitiveTargets(const json_value_s& j)
+{
+  auto& jo = js::Cast<json_array_s>(j);
+  std::vector<std::map<gt::Attribute::Type, gt::Ref<gt::Accessor>>> result;
+
+  result.reserve(jo.length);
+
+  auto i = jo.start;
+  while (i)
+  {
+    result.push_back(std::move(ReadMeshPrimitiveAttributes(*i->value)));
+    i = i->next;
+  }
+
+  return result;
+}
+
+const auto MESH_PRIMITIVE_READER = std::move(js::Reader<gt::Mesh::Primitive>()
+  .Register(*js::MakeProperty("attributes", ReadMeshPrimitiveAttributes, &gt::Mesh::Primitive::mAttributes))
+  .Register(*js::MakeProperty("indices", gt::RefReader<gt::Document>::Read<gt::Accessor, &gt::Document::mAccessors>, &gt::Mesh::Primitive::mIndices))
+  .Register(*js::MakeProperty("material", gt::RefReader<gt::Document>::Read<gt::Material, &gt::Document::mMaterials>, &gt::Mesh::Primitive::mMaterial))
+  .Register(*js::MakeProperty("mode", js::Read::Enum<gt::Mesh::Primitive::Mode>, &gt::Mesh::Primitive::mMode))
+  .Register(*js::MakeProperty("targets", ReadMeshPrimitiveTargets, &gt::Mesh::Primitive::mTargets))
+);
+
+const auto MESH_READER = std::move(js::Reader<gt::Mesh>()
+  .Register(*new js::Property<gt::Mesh, std::string_view>("name", js::Read::StringView, &gt::Mesh::mName))
+  .Register(*js::MakeProperty("primitives",
+    js::Read::Array<gt::Mesh::Primitive, js::ObjectReader<gt::Mesh::Primitive>::Read>, &gt::Mesh::mPrimitives))
+  .Register(*js::MakeProperty("weights", js::Read::Array<float, js::Read::Number>, &gt::Mesh::mWeights))
+);
+
+const auto SKIN_READER = std::move(js::Reader<gt::Skin>()
+  .Register(*new js::Property<gt::Skin, std::string_view>("name", js::Read::StringView, &gt::Skin::mName))
+  .Register(*js::MakeProperty("inverseBindMatrices",
+    gt::RefReader<gt::Document>::Read<gt::Accessor, &gt::Document::mAccessors>, &gt::Skin::mInverseBindMatrices))
+  .Register(*js::MakeProperty("skeleton",
+    gt::RefReader<gt::Document>::Read<gt::Node, &gt::Document::mNodes>, &gt::Skin::mSkeleton))
+  .Register(*js::MakeProperty("joints",
+    js::Read::Array<gt::Ref<gt::Node>, gt::RefReader<gt::Document>::Read<gt::Node, &gt::Document::mNodes>>, &gt::Skin::mJoints))
+);
+
+const auto CAMERA_PERSPECTIVE_READER = std::move(js::Reader<gt::Camera::Perspective>()
+  .Register(*js::MakeProperty("aspectRatio", js::Read::Number<float>, &gt::Camera::Perspective::mAspectRatio))
+  .Register(*js::MakeProperty("yfov", js::Read::Number<float>, &gt::Camera::Perspective::mYFov))
+  .Register(*js::MakeProperty("zfar", js::Read::Number<float>, &gt::Camera::Perspective::mZFar))
+  .Register(*js::MakeProperty("znear", js::Read::Number<float>, &gt::Camera::Perspective::mZNear))
+);  // TODO: infinite perspective projection, where znear is omitted
+
+const auto CAMERA_ORTHOGRAPHIC_READER = std::move(js::Reader<gt::Camera::Orthographic>()
+  .Register(*js::MakeProperty("xmag", js::Read::Number<float>, &gt::Camera::Orthographic::mXMag))
+  .Register(*js::MakeProperty("ymag", js::Read::Number<float>, &gt::Camera::Orthographic::mXMag))
+  .Register(*js::MakeProperty("zfar", js::Read::Number<float>, &gt::Camera::Orthographic::mZFar))
+  .Register(*js::MakeProperty("znear", js::Read::Number<float>, &gt::Camera::Orthographic::mZNear))
+);
+
+const auto CAMERA_READER = std::move(js::Reader<gt::Camera>()
+  .Register(*new js::Property<gt::Camera, std::string_view>("name", js::Read::StringView, &gt::Camera::mName))
+  .Register(*js::MakeProperty("type", js::Read::StringView, &gt::Camera::mType))
+  .Register(*js::MakeProperty("perspective", js::ObjectReader<gt::Camera::Perspective>::Read, &gt::Camera::mPerspective))
+  .Register(*js::MakeProperty("orthographic", js::ObjectReader<gt::Camera::Orthographic>::Read, &gt::Camera::mOrthographic))
+);
+
+const auto NODE_READER = std::move(js::Reader<gt::Node>()
+  .Register(*new js::Property<gt::Node, std::string_view>("name", js::Read::StringView, &gt::Node::mName))
+  .Register(*js::MakeProperty("translation", gt::ReadDaliVector<Vector3>, &gt::Node::mTranslation))
+  .Register(*js::MakeProperty("rotation", gt::ReadQuaternion, &gt::Node::mRotation))
+  .Register(*js::MakeProperty("scale", gt::ReadDaliVector<Vector3>, &gt::Node::mScale))
+  .Register(*new js::Property<gt::Node, Matrix>("matrix", gt::ReadDaliVector<Matrix>, &gt::Node::SetMatrix))
+  .Register(*js::MakeProperty("camera", gt::RefReader<gt::Document>::Read<gt::Camera, &gt::Document::mCameras>,
+    &gt::Node::mCamera))
+  .Register(*js::MakeProperty("children", js::Read::Array<gt::Ref<gt::Node>, gt::RefReader<gt::Document>::Read<gt::Node, &gt::Document::mNodes>>,
+    &gt::Node::mChildren))
+  .Register(*js::MakeProperty("mesh", gt::RefReader<gt::Document>::Read<gt::Mesh, &gt::Document::mMeshes>, &gt::Node::mMesh))
+  .Register(*js::MakeProperty("skin", gt::RefReader<gt::Document>::Read<gt::Skin, &gt::Document::mSkins>, &gt::Node::mSkin))
+);
+
+const auto ANIMATION_SAMPLER_READER = std::move(js::Reader<gt::Animation::Sampler>()
+  .Register(*js::MakeProperty("input", gt::RefReader<gt::Document>::Read<gt::Accessor, &gt::Document::mAccessors>,
+    &gt::Animation::Sampler::mInput))
+  .Register(*js::MakeProperty("output", gt::RefReader<gt::Document>::Read<gt::Accessor, &gt::Document::mAccessors>,
+    &gt::Animation::Sampler::mOutput))
+  .Register(*js::MakeProperty("interpolation", gt::ReadStringEnum<gt::Animation::Sampler::Interpolation>, &gt::Animation::Sampler::mInterpolation))
+);
+
+const auto ANIMATION_TARGET_READER = std::move(js::Reader<gt::Animation::Channel::Target>()
+  .Register(*js::MakeProperty("node", gt::RefReader<gt::Document>::Read<gt::Node, &gt::Document::mNodes>,
+    &gt::Animation::Channel::Target::mNode))
+  .Register(*js::MakeProperty("path", gt::ReadStringEnum<gt::Animation::Channel::Target>,
+    &gt::Animation::Channel::Target::mPath))
+);
+
+const auto ANIMATION_CHANNEL_READER = std::move(js::Reader<gt::Animation::Channel>()
+  .Register(*js::MakeProperty("target", js::ObjectReader<gt::Animation::Channel::Target>::Read, &gt::Animation::Channel::mTarget))
+  .Register(*js::MakeProperty("sampler", gt::RefReader<gt::Animation>::Read<gt::Animation::Sampler, &gt::Animation::mSamplers>, &gt::Animation::Channel::mSampler))
+);
+
+const auto ANIMATION_READER = std::move(js::Reader<gt::Animation>()
+  .Register(*new js::Property<gt::Animation, std::string_view>("name", js::Read::StringView, &gt::Animation::mName))
+  .Register(*js::MakeProperty("samplers",
+    js::Read::Array<gt::Animation::Sampler, js::ObjectReader<gt::Animation::Sampler>::Read>, &gt::Animation::mSamplers))
+  .Register(*js::MakeProperty("channels",
+    js::Read::Array<gt::Animation::Channel, js::ObjectReader<gt::Animation::Channel>::Read>, &gt::Animation::mChannels))
+);
+
+const auto SCENE_READER = std::move(js::Reader<gt::Scene>()
+  .Register(*new js::Property<gt::Scene, std::string_view>("name", js::Read::StringView, &gt::Scene::mName))
+  .Register(*js::MakeProperty("nodes",
+    js::Read::Array<gt::Ref<gt::Node>, gt::RefReader<gt::Document>::Read<gt::Node, &gt::Document::mNodes>>, &gt::Scene::mNodes))
+);
+
+const auto DOCUMENT_READER = std::move(js::Reader<gt::Document>()
+  .Register(*js::MakeProperty("buffers",
+    js::Read::Array<gt::Buffer, js::ObjectReader<gt::Buffer>::Read>, &gt::Document::mBuffers))
+  .Register(*js::MakeProperty("bufferViews",
+    js::Read::Array<gt::BufferView, js::ObjectReader<gt::BufferView>::Read>, &gt::Document::mBufferViews))
+  .Register(*js::MakeProperty("accessors",
+    js::Read::Array<gt::Accessor, js::ObjectReader<gt::Accessor>::Read>, &gt::Document::mAccessors))
+  .Register(*js::MakeProperty("images",
+    js::Read::Array<gt::Image, js::ObjectReader<gt::Image>::Read>, &gt::Document::mImages))
+  .Register(*js::MakeProperty("samplers",
+    js::Read::Array<gt::Sampler, js::ObjectReader<gt::Sampler>::Read>, &gt::Document::mSamplers))
+  .Register(*js::MakeProperty("textures",
+    js::Read::Array<gt::Texture, js::ObjectReader<gt::Texture>::Read>, &gt::Document::mTextures))
+  .Register(*js::MakeProperty("materials",
+    js::Read::Array<gt::Material, js::ObjectReader<gt::Material>::Read>, &gt::Document::mMaterials))
+  .Register(*js::MakeProperty("meshes",
+    js::Read::Array<gt::Mesh, js::ObjectReader<gt::Mesh>::Read>, &gt::Document::mMeshes))
+  .Register(*js::MakeProperty("skins",
+    js::Read::Array<gt::Skin, js::ObjectReader<gt::Skin>::Read>, &gt::Document::mSkins))
+  .Register(*js::MakeProperty("cameras",
+    js::Read::Array<gt::Camera, js::ObjectReader<gt::Camera>::Read>, &gt::Document::mCameras))
+  .Register(*js::MakeProperty("nodes",
+    js::Read::Array<gt::Node, js::ObjectReader<gt::Node>::Read>, &gt::Document::mNodes))
+  .Register(*js::MakeProperty("animations",
+    ReadAnimationArray, &gt::Document::mAnimations))
+  .Register(*js::MakeProperty("scenes",
+    js::Read::Array<gt::Scene, js::ObjectReader<gt::Scene>::Read>, &gt::Document::mScenes))
+  .Register(*js::MakeProperty("scene", gt::RefReader<gt::Document>::Read<gt::Scene, &gt::Document::mScenes>, &gt::Document::mScene))
+);
+
+struct NodeMapping
+{
+  Index gltfIdx;
+  Index runtimeIdx;
+};
+
+bool operator<(const NodeMapping& mapping, Index gltfIdx)
+{
+  return mapping.gltfIdx < gltfIdx;
+}
+
+class NodeIndexMapper
+{
+public:
+  NodeIndexMapper() = default;
+  NodeIndexMapper(const NodeIndexMapper&) = delete;
+  NodeIndexMapper& operator=(const NodeIndexMapper&) = delete;
+
+  ///@brief Registers a mapping of the @a gltfIdx of a node to its @a runtimeIdx .
+  ///@note If the indices are the same, the registration is omitted, in order to
+  /// save growing a vector.
+  void RegisterMapping(Index gltfIdx, Index runtimeIdx)
+  {
+    if (gltfIdx != runtimeIdx)
+    {
+      auto iInsert = std::lower_bound(mNodes.begin(), mNodes.end(), gltfIdx);
+      DALI_ASSERT_DEBUG(iInsert == mNodes.end() || iInsert->gltfIdx != gltfIdx);
+      mNodes.insert(iInsert, NodeMapping{ gltfIdx, runtimeIdx });
+    }
+  }
+
+  ///@brief Retrieves the runtime index of a Node, mapped to the given @a gltfIdx.
+  Index GetRuntimeId(Index gltfIdx) const
+  {
+    auto iFind = std::lower_bound(mNodes.begin(), mNodes.end(), gltfIdx);  // using custom operator<
+    return (iFind != mNodes.end() && iFind->gltfIdx == gltfIdx) ? iFind->runtimeIdx : gltfIdx;
+  }
+
+private:
+  std::vector<NodeMapping> mNodes;
+};
+
+struct ConversionContext
+{
+  LoadResult&  mOutput;
+
+  std::string mPath;
+  Index mDefaultMaterial;
+
+  std::vector<Index>  mMeshIds;
+  NodeIndexMapper mNodeIndices;
+};
+
+SamplerFlags::Type ConvertWrapMode(gt::Wrap::Type w)
+{
+  switch (w)
+  {
+  case gt::Wrap::REPEAT:
+    return SamplerFlags::WRAP_REPEAT;
+  case gt::Wrap::CLAMP_TO_EDGE:
+    return SamplerFlags::WRAP_CLAMP;
+  case gt::Wrap::MIRRORED_REPEAT:
+    return SamplerFlags::WRAP_MIRROR;
+  default:
+    throw std::runtime_error("Invalid wrap type.");
+  }
+}
+
+SamplerFlags::Type ConvertSampler(const gt::Ref<gt::Sampler>& s)
+{
+  if (s)
+  {
+    return (s->mMinFilter < gt::Filter::NEAREST_MIPMAP_NEAREST) ? (s->mMinFilter - gt::Filter::NEAREST) :
+      ((s->mMinFilter - gt::Filter::NEAREST_MIPMAP_NEAREST) + 2) |
+      ((s->mMagFilter - gt::Filter::NEAREST) << SamplerFlags::FILTER_MAG_SHIFT) |
+      (ConvertWrapMode(s->mWrapS) << SamplerFlags::WRAP_S_SHIFT) |
+      (ConvertWrapMode(s->mWrapT) << SamplerFlags::WRAP_T_SHIFT);
+  }
+  else
+  {
+    // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#texturesampler
+    // "The index of the sampler used by this texture. When undefined, a sampler with repeat wrapping and auto filtering should be used."
+    // "What is an auto filtering", I hear you ask. Since there's nothing else to determine mipmapping from - including glTF image
+    // properties, if not in some extension -, we will simply assume linear filtering.
+    return SamplerFlags::FILTER_LINEAR | (SamplerFlags::FILTER_LINEAR << SamplerFlags::FILTER_MAG_SHIFT) |
+      (SamplerFlags::WRAP_REPEAT << SamplerFlags::WRAP_S_SHIFT) | (SamplerFlags::WRAP_REPEAT << SamplerFlags::WRAP_T_SHIFT);
+  }
+}
+
+TextureDefinition ConvertTextureInfo(const gt::TextureInfo& mm)
+{
+  return TextureDefinition{ std::string(mm.mTexture->mSource->mUri), ConvertSampler(mm.mTexture->mSampler) };
+}
+
+void ConvertMaterial(const gt::Material& m, decltype(ResourceBundle::mMaterials)& outMaterials)
+{
+  MaterialDefinition matDef;
+
+  auto& pbr = m.mPbrMetallicRoughness;
+  if (m.mAlphaMode != gt::AlphaMode::OPAQUE || pbr.mBaseColorFactor.a < 1.f)
+  {
+    matDef.mFlags |= MaterialDefinition::TRANSPARENCY;
+  }
+
+  if (m.mAlphaMode == gt::AlphaMode::MASK)
+  {
+    matDef.SetAlphaCutoff(std::min(1.f, std::max(0.f, m.mAlphaCutoff)));
+  }
+
+  matDef.mColor = pbr.mBaseColorFactor;
+
+  matDef.mTextureStages.reserve(!!pbr.mBaseColorTexture + !!pbr.mMetallicRoughnessTexture + !!m.mNormalTexture);
+  if (pbr.mBaseColorTexture)
+  {
+    const auto semantic = MaterialDefinition::ALBEDO;
+    matDef.mTextureStages.push_back({ semantic, ConvertTextureInfo(pbr.mBaseColorTexture) });
+    // TODO: and there had better be one
+    matDef.mFlags |= semantic;
+  }
+
+  matDef.mMetallic = pbr.mMetallicFactor;
+  matDef.mRoughness = pbr.mRoughnessFactor;
+
+  if (pbr.mMetallicRoughnessTexture)
+  {
+    const auto semantic = MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS |
+      MaterialDefinition::GLTF_CHANNELS;
+    matDef.mTextureStages.push_back({ semantic, ConvertTextureInfo(pbr.mMetallicRoughnessTexture) });
+    // TODO: and there had better be one
+    matDef.mFlags |= semantic;
+  }
+
+  if (m.mNormalTexture)
+  {
+    const auto semantic = MaterialDefinition::NORMAL;
+    matDef.mTextureStages.push_back({ semantic, ConvertTextureInfo(m.mNormalTexture) });
+    // TODO: and there had better be one
+    matDef.mFlags |= semantic;
+  }
+
+  // TODO: handle doubleSided
+
+  outMaterials.emplace_back(std::move(matDef), TextureSet());
+}
+
+void ConvertMaterials(const gt::Document& doc, ConversionContext& cctx)
+{
+  auto& outMaterials = cctx.mOutput.mResources.mMaterials;
+  outMaterials.reserve(doc.mMaterials.size());
+
+  for (auto& m : doc.mMaterials)
+  {
+    ConvertMaterial(m, outMaterials);
+  }
+}
+
+MeshDefinition::Accessor ConvertMeshPrimitiveAccessor(const gt::Accessor& acc)
+{
+  DALI_ASSERT_ALWAYS((acc.mBufferView &&
+    (acc.mBufferView->mByteStride < std::numeric_limits<uint16_t>::max())) ||
+    (acc.mSparse && !acc.mBufferView));
+
+  DALI_ASSERT_ALWAYS(!acc.mSparse ||
+    ((acc.mSparse->mIndices.mBufferView && (acc.mSparse->mIndices.mBufferView->mByteStride < std::numeric_limits<uint16_t>::max())) &&
+    (acc.mSparse->mValues.mBufferView && (acc.mSparse->mValues.mBufferView->mByteStride < std::numeric_limits<uint16_t>::max()))));
+
+
+  MeshDefinition::SparseBlob sparseBlob;
+  if (acc.mSparse)
+  {
+    const gt::Accessor::Sparse& sparse = *acc.mSparse;
+    const gt::ComponentTypedBufferViewClient& indices = sparse.mIndices;
+    const gt::BufferViewClient& values = sparse.mValues;
+
+    MeshDefinition::Blob indicesBlob(
+      indices.mBufferView->mByteOffset + indices.mByteOffset,
+      sparse.mCount * indices.GetBytesPerComponent(),
+      static_cast<uint16_t>(indices.mBufferView->mByteStride),
+      static_cast<uint16_t>(indices.GetBytesPerComponent()),
+      {}, {}
+    );
+    MeshDefinition::Blob valuesBlob(
+      values.mBufferView->mByteOffset + values.mByteOffset,
+      sparse.mCount * acc.GetElementSizeBytes(),
+      static_cast<uint16_t>(values.mBufferView->mByteStride),
+      static_cast<uint16_t>(acc.GetElementSizeBytes()),
+      {}, {}
+    );
+
+    sparseBlob = std::move(MeshDefinition::SparseBlob(std::move(indicesBlob), std::move(valuesBlob), acc.mSparse->mCount));
+  }
+
+  uint32_t bufferViewOffset = 0u;
+  uint32_t bufferViewStride = 0u;
+  if (acc.mBufferView)
+  {
+    bufferViewOffset = acc.mBufferView->mByteOffset;
+    bufferViewStride = acc.mBufferView->mByteStride;
+  }
+
+  return MeshDefinition::Accessor{
+    std::move(MeshDefinition::Blob{bufferViewOffset + acc.mByteOffset,
+      acc.GetBytesLength(),
+      static_cast<uint16_t>(bufferViewStride),
+      static_cast<uint16_t>(acc.GetElementSizeBytes()),
+      acc.mMin,
+      acc.mMax}),
+    std::move(sparseBlob) };
+}
+
+void ConvertMeshes(const gt::Document& doc, ConversionContext& cctx)
+{
+  uint32_t meshCount = 0;
+  cctx.mMeshIds.reserve(doc.mMeshes.size());
+  for (auto& m : doc.mMeshes)
+  {
+    cctx.mMeshIds.push_back(meshCount);
+    meshCount += m.mPrimitives.size();
+  }
+
+  auto& outMeshes = cctx.mOutput.mResources.mMeshes;
+  outMeshes.reserve(meshCount);
+  for (auto& m : doc.mMeshes)
+  {
+    for (auto& p : m.mPrimitives)
+    {
+      MeshDefinition meshDef;
+
+      auto& attribs = p.mAttributes;
+      meshDef.mUri = attribs.begin()->second->mBufferView->mBuffer->mUri;
+      meshDef.mPrimitiveType = GLTF2_TO_DALI_PRIMITIVES[p.mMode];
+
+      auto& accPositions = *attribs.find(gt::Attribute::POSITION)->second;
+      meshDef.mPositions = ConvertMeshPrimitiveAccessor(accPositions);
+
+      const bool needNormalsTangents = accPositions.mType == gt::AccessorType::VEC3;
+      for (auto& am : ATTRIBUTE_MAPPINGS)
+      {
+        auto iFind = attribs.find(am.mType);
+        if (iFind != attribs.end())
+        {
+          DALI_ASSERT_DEBUG(iFind->second->mBufferView->mBuffer->mUri.compare(meshDef.mUri) == 0);
+          auto& accessor = meshDef.*(am.mAccessor);
+          accessor = ConvertMeshPrimitiveAccessor(*iFind->second);
+
+          // Fixing up -- a few of glTF2 sample models have VEC4 tangents; we need VEC3s.
+          if (iFind->first == gt::Attribute::TANGENT && (accessor.mBlob.mElementSizeHint > am.mElementSizeRequired))
+          {
+            accessor.mBlob.mStride = std::max(static_cast<uint16_t>(accessor.mBlob.mStride + accessor.mBlob.mElementSizeHint - am.mElementSizeRequired),
+              accessor.mBlob.mElementSizeHint);
+            accessor.mBlob.mElementSizeHint = am.mElementSizeRequired;
+          }
+
+          if (iFind->first == gt::Attribute::JOINTS_0)
+          {
+            meshDef.mFlags |= (iFind->second->mComponentType == gt::Component::UNSIGNED_SHORT) * MeshDefinition::U16_JOINT_IDS;
+            DALI_ASSERT_DEBUG(MaskMatch(meshDef.mFlags, MeshDefinition::U16_JOINT_IDS) || iFind->second->mComponentType == gt::Component::FLOAT);
+          }
+        }
+        else if (needNormalsTangents)
+        {
+          switch (am.mType)
+          {
+          case gt::Attribute::NORMAL:
+            meshDef.RequestNormals();
+            break;
+
+          case gt::Attribute::TANGENT:
+            meshDef.RequestTangents();
+            break;
+
+          default:
+            break;
+          }
+        }
+      }
+
+      if (p.mIndices)
+      {
+        meshDef.mIndices = ConvertMeshPrimitiveAccessor(*p.mIndices);
+        meshDef.mFlags |= (p.mIndices->mComponentType == gt::Component::UNSIGNED_INT) * MeshDefinition::U32_INDICES;
+        DALI_ASSERT_DEBUG(MaskMatch(meshDef.mFlags, MeshDefinition::U32_INDICES) || p.mIndices->mComponentType == gt::Component::UNSIGNED_SHORT);
+      }
+
+      if (!p.mTargets.empty())
+      {
+        meshDef.mBlendShapes.reserve(p.mTargets.size());
+        meshDef.mBlendShapeVersion = BlendShapes::Version::VERSION_2_0;
+        for (const auto& target : p.mTargets)
+        {
+          MeshDefinition::BlendShape blendShape;
+
+          auto endIt = target.end();
+          auto it = target.find(gt::Attribute::POSITION);
+          if (it != endIt)
+          {
+            blendShape.deltas = ConvertMeshPrimitiveAccessor(*it->second);
+          }
+          it = target.find(gt::Attribute::NORMAL);
+          if (it != endIt)
+          {
+            blendShape.normals = ConvertMeshPrimitiveAccessor(*it->second);
+          }
+          it = target.find(gt::Attribute::TANGENT);
+          if (it != endIt)
+          {
+            blendShape.tangents = ConvertMeshPrimitiveAccessor(*it->second);
+          }
+
+          if (!m.mWeights.empty())
+          {
+            blendShape.weight = m.mWeights[meshDef.mBlendShapes.size()];
+          }
+
+          meshDef.mBlendShapes.push_back(std::move(blendShape));
+        }
+      }
+
+      outMeshes.push_back({ std::move(meshDef), MeshGeometry{} });
+    }
+  }
+}
+
+ModelNode* MakeModelNode(const gt::Mesh::Primitive& prim, ConversionContext& cctx)
+{
+  auto modelNode = new ModelNode();
+
+  modelNode->mShaderIdx = 0;  // TODO: further thought
+
+  auto materialIdx = prim.mMaterial.GetIndex();
+  if (INVALID_INDEX == materialIdx)
+  {
+    // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#default-material
+    if (INVALID_INDEX == cctx.mDefaultMaterial)
+    {
+      auto& outMaterials = cctx.mOutput.mResources.mMaterials;
+      cctx.mDefaultMaterial = outMaterials.size();
+
+      ConvertMaterial(gt::Material{}, outMaterials);
+    }
+
+    materialIdx = cctx.mDefaultMaterial;
+  }
+
+  modelNode->mMaterialIdx = materialIdx;
+
+  return modelNode;
+}
+
+void ConvertCamera(const gt::Camera& camera, CameraParameters& camParams)
+{
+  camParams.isPerspective = camera.mType.compare("perspective") == 0;
+  if (camParams.isPerspective)
+  {
+    auto& perspective = camera.mPerspective;
+    camParams.yFov = Degree(Radian(perspective.mYFov)).degree;
+    camParams.zNear = perspective.mZNear;
+    camParams.zFar = perspective.mZFar;
+    // TODO: yes, we seem to ignore aspectRatio in CameraParameters.
+  }
+  else
+  {
+    auto& ortho = camera.mOrthographic;
+    camParams.orthographicSize = Vector4(-ortho.mXMag, ortho.mXMag, ortho.mYMag, -ortho.mYMag) * .5f;
+    camParams.zNear = ortho.mZNear;
+    camParams.zFar = ortho.mZFar;
+  }
+}
+
+void ConvertNode(gt::Node const& node, const Index gltfIdx, Index parentIdx, ConversionContext& cctx)
+{
+  auto& output = cctx.mOutput;
+  auto& scene = output.mScene;
+  auto& resources = output.mResources;
+
+  const auto idx = scene.GetNodeCount();
+  auto weakNode = scene.AddNode([&]() {
+    std::unique_ptr<NodeDefinition> nodeDef{ new NodeDefinition() };
+
+    nodeDef->mParentIdx = parentIdx;
+    nodeDef->mName = node.mName;
+    if (nodeDef->mName.empty())
+    {
+      // TODO: Production quality generation of unique names.
+      nodeDef->mName = std::to_string(reinterpret_cast<uintptr_t>(nodeDef.get()));
+    }
+
+    if (!node.mSkin)  // Nodes with skinned meshes are not supposed to have local transforms.
+    {
+      nodeDef->mPosition = node.mTranslation;
+      nodeDef->mOrientation = node.mRotation;
+      nodeDef->mScale = node.mScale;
+    }
+
+    return nodeDef;
+  }());
+  if (!weakNode)
+  {
+    ExceptionFlinger(ASSERT_LOCATION) << "Node name '" << node.mName << "' is not unique; scene is invalid.";
+  }
+
+  cctx.mNodeIndices.RegisterMapping(gltfIdx, idx);
+
+  Index skeletonIdx = node.mSkin ? node.mSkin.GetIndex() : INVALID_INDEX;
+  if (node.mMesh && !node.mMesh->mPrimitives.empty())
+  {
+    auto& mesh = *node.mMesh;
+
+    auto iPrim = mesh.mPrimitives.begin();
+    auto modelNode = MakeModelNode(*iPrim, cctx);
+    auto meshIdx = cctx.mMeshIds[node.mMesh.GetIndex()];
+    modelNode->mMeshIdx = meshIdx;
+
+    weakNode->mRenderable.reset(modelNode);
+
+    DALI_ASSERT_DEBUG(resources.mMeshes[meshIdx].first.mSkeletonIdx == INVALID_INDEX ||
+      resources.mMeshes[meshIdx].first.mSkeletonIdx == skeletonIdx);
+    resources.mMeshes[meshIdx].first.mSkeletonIdx = skeletonIdx;
+
+    // As does model-exporter, we'll create anonymous child nodes for additional mesh( primitiv)es.
+    while (++iPrim != mesh.mPrimitives.end())
+    {
+      std::unique_ptr<NodeDefinition> child{ new NodeDefinition };
+      child->mParentIdx = idx;
+
+      auto childModel = MakeModelNode(*iPrim, cctx);
+
+      ++meshIdx;
+      childModel->mMeshIdx = meshIdx;
+
+      child->mRenderable.reset(childModel);
+
+      scene.AddNode(std::move(child));
+
+      DALI_ASSERT_DEBUG(resources.mMeshes[meshIdx].first.mSkeletonIdx == INVALID_INDEX ||
+        resources.mMeshes[meshIdx].first.mSkeletonIdx == skeletonIdx);
+      resources.mMeshes[meshIdx].first.mSkeletonIdx = skeletonIdx;
+    }
+  }
+
+  if (node.mCamera)
+  {
+    CameraParameters camParams;
+    ConvertCamera(*node.mCamera, camParams);
+
+    camParams.matrix.SetTransformComponents(node.mScale, node.mRotation, node.mTranslation);
+    output.mCameraParameters.push_back(camParams);
+  }
+
+  for (auto& n : node.mChildren)
+  {
+    ConvertNode(*n, n.GetIndex(), idx, cctx);
+  }
+}
+
+void ConvertSceneNodes(const gt::Scene& scene, ConversionContext& cctx)
+{
+  auto& outScene = cctx.mOutput.mScene;
+  Index rootIdx = outScene.GetNodeCount();
+  switch (scene.mNodes.size())
+  {
+  case 0:
+    break;
+
+  case 1:
+    ConvertNode(*scene.mNodes[0], scene.mNodes[0].GetIndex(), INVALID_INDEX, cctx);
+    outScene.AddRootNode(rootIdx);
+    break;
+
+  default:
+    {
+      std::unique_ptr<NodeDefinition> sceneRoot{ new NodeDefinition() };
+      sceneRoot->mName = "GLTF_LOADER_SCENE_ROOT_" + std::to_string(outScene.GetRoots().size());
+
+      outScene.AddNode(std::move(sceneRoot));
+      outScene.AddRootNode(rootIdx);
+
+      for (auto& n : scene.mNodes)
+      {
+        ConvertNode(*n, n.GetIndex(), rootIdx, cctx);
+      }
+      break;
+    }
+  }
+}
+
+void ConvertNodes(const gt::Document& doc, ConversionContext& cctx)
+{
+  ConvertSceneNodes(*doc.mScene, cctx);
+
+  for (uint32_t i = 0, i1 = doc.mScene.GetIndex(); i < i1; ++i)
+  {
+    ConvertSceneNodes(doc.mScenes[i], cctx);
+  }
+
+  for (uint32_t i = doc.mScene.GetIndex() + 1; i < doc.mScenes.size(); ++i)
+  {
+    ConvertSceneNodes(doc.mScenes[i], cctx);
+  }
+}
+
+template <typename T>
+void LoadDataFromAccessor(const std::string& path, Vector<T>& dataBuffer, uint32_t offset, uint32_t size)
+{
+  std::ifstream animationBinaryFile(path, std::ifstream::binary);
+
+  if (!animationBinaryFile.is_open())
+  {
+    throw std::runtime_error("Failed to load " + path);
+  }
+
+  animationBinaryFile.seekg(offset);
+  animationBinaryFile.read(reinterpret_cast<char*>(dataBuffer.Begin()), size);
+  animationBinaryFile.close();
+}
+
+template <typename T>
+float LoadDataFromAccessors(const std::string& path, const gltf2::Accessor& input, const gltf2::Accessor& output, Vector<float>& inputDataBuffer, Vector<T>& outputDataBuffer)
+{
+  inputDataBuffer.Resize(input.mCount);
+  outputDataBuffer.Resize(output.mCount);
+
+  const uint32_t inputDataBufferSize = input.GetBytesLength();
+  const uint32_t outputDataBufferSize = output.GetBytesLength();
+
+  LoadDataFromAccessor<float>(path + std::string(input.mBufferView->mBuffer->mUri), inputDataBuffer,
+    input.mBufferView->mByteOffset + input.mByteOffset, inputDataBufferSize);
+  LoadDataFromAccessor<T>(path + std::string(output.mBufferView->mBuffer->mUri), outputDataBuffer,
+    output.mBufferView->mByteOffset + output.mByteOffset, outputDataBufferSize);
+  ApplyAccessorMinMax(output, reinterpret_cast<float*>(outputDataBuffer.begin()));
+
+  return inputDataBuffer[input.mCount - 1u];
+}
+
+template<typename T>
+float LoadKeyFrames(const std::string& path, const gt::Animation::Channel& channel, KeyFrames& keyFrames, gt::Animation::Channel::Target::Type type)
+{
+  const gltf2::Accessor& input = *channel.mSampler->mInput;
+  const gltf2::Accessor& output = *channel.mSampler->mOutput;
+
+  Vector<float> inputDataBuffer;
+  Vector<T> outputDataBuffer;
+
+  const float duration = LoadDataFromAccessors<T>(path, input, output, inputDataBuffer, outputDataBuffer);
+
+  for (uint32_t i = 0; i < input.mCount; ++i)
+  {
+    keyFrames.Add(inputDataBuffer[i] / duration, outputDataBuffer[i]);
+  }
+
+  return duration;
+}
+
+float LoadBlendShapeKeyFrames(const std::string& path, const gt::Animation::Channel& channel, const std::string& nodeName, uint32_t& propertyIndex, std::vector<SceneLoader::AnimatedProperty>& properties)
+{
+  const gltf2::Accessor& input = *channel.mSampler->mInput;
+  const gltf2::Accessor& output = *channel.mSampler->mOutput;
+
+  Vector<float> inputDataBuffer;
+  Vector<float> outputDataBuffer;
+
+  const float duration = LoadDataFromAccessors<float>(path, input, output, inputDataBuffer, outputDataBuffer);
+
+  char weightNameBuffer[32];
+  auto prefixSize = snprintf(weightNameBuffer, sizeof(weightNameBuffer), "%s[", BLEND_SHAPE_WEIGHTS_UNIFORM.c_str());
+  char* const pWeightName = weightNameBuffer + prefixSize;
+  const auto remainingSize = sizeof(weightNameBuffer) - prefixSize;
+  for (uint32_t weightIndex = 0u, endWeightIndex = channel.mSampler->mOutput->mCount / channel.mSampler->mInput->mCount; weightIndex < endWeightIndex; ++weightIndex)
+  {
+    AnimatedProperty& animatedProperty = properties[propertyIndex++];
+
+    animatedProperty.mNodeName = nodeName;
+    snprintf(pWeightName, remainingSize, "%d]", weightIndex);
+    animatedProperty.mPropertyName = std::string(weightNameBuffer);
+
+    animatedProperty.mKeyFrames = KeyFrames::New();
+    for (uint32_t i = 0; i < input.mCount; ++i)
+    {
+      animatedProperty.mKeyFrames.Add(inputDataBuffer[i] / duration, outputDataBuffer[i*endWeightIndex + weightIndex]);
+    }
+
+    animatedProperty.mTimePeriod = { 0.f, duration };
+  }
+
+  return duration;
+}
+
+void ConvertAnimations(const gt::Document& doc, ConversionContext& cctx)
+{
+  auto& output = cctx.mOutput;
+
+  output.mAnimationDefinitions.reserve(output.mAnimationDefinitions.size() + doc.mAnimations.size());
+
+  for (const auto& animation : doc.mAnimations)
+  {
+    AnimationDefinition animationDef;
+
+    if (!animation.mName.empty())
+    {
+      animationDef.mName = animation.mName;
+    }
+
+    uint32_t numberOfProperties = 0u;
+
+    for (const auto& channel : animation.mChannels)
+    {
+      numberOfProperties += channel.mSampler->mOutput->mCount;
+    }
+    animationDef.mProperties.resize(numberOfProperties);
+
+    Index propertyIndex = 0u;
+    for (const auto& channel : animation.mChannels)
+    {
+      std::string nodeName;
+      if (!channel.mTarget.mNode->mName.empty())
+      {
+        nodeName = channel.mTarget.mNode->mName;
+      }
+      else
+      {
+        Index index = cctx.mNodeIndices.GetRuntimeId(channel.mTarget.mNode.GetIndex());
+        nodeName = cctx.mOutput.mScene.GetNode(index)->mName;
+      }
+
+      float duration = 0.f;
+
+      switch (channel.mTarget.mPath)
+      {
+        case gt::Animation::Channel::Target::TRANSLATION:
+        {
+          AnimatedProperty& animatedProperty = animationDef.mProperties[propertyIndex];
+
+          animatedProperty.mNodeName = nodeName;
+          animatedProperty.mPropertyName = POSITION_PROPERTY;
+
+          animatedProperty.mKeyFrames = KeyFrames::New();
+          duration = LoadKeyFrames<Vector3>(cctx.mPath, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
+
+          animatedProperty.mTimePeriod = { 0.f, duration };
+          break;
+        }
+        case gt::Animation::Channel::Target::ROTATION:
+        {
+          AnimatedProperty& animatedProperty = animationDef.mProperties[propertyIndex];
+
+          animatedProperty.mNodeName = nodeName;
+          animatedProperty.mPropertyName = ORIENTATION_PROPERTY;
+
+          animatedProperty.mKeyFrames = KeyFrames::New();
+          duration = LoadKeyFrames<Quaternion>(cctx.mPath, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
+
+          animatedProperty.mTimePeriod = { 0.f, duration };
+          break;
+        }
+        case gt::Animation::Channel::Target::SCALE:
+        {
+          AnimatedProperty& animatedProperty = animationDef.mProperties[propertyIndex];
+
+          animatedProperty.mNodeName = nodeName;
+          animatedProperty.mPropertyName = SCALE_PROPERTY;
+
+          animatedProperty.mKeyFrames = KeyFrames::New();
+          duration = LoadKeyFrames<Vector3>(cctx.mPath, channel, animatedProperty.mKeyFrames, channel.mTarget.mPath);
+
+          animatedProperty.mTimePeriod = { 0.f, duration };
+          break;
+        }
+        case gt::Animation::Channel::Target::WEIGHTS:
+        {
+          duration = LoadBlendShapeKeyFrames(cctx.mPath, channel, nodeName, propertyIndex, animationDef.mProperties);
+
+          break;
+        }
+        default:
+        {
+          // nothing to animate.
+          break;
+        }
+      }
+
+      animationDef.mDuration = std::max(duration, animationDef.mDuration);
+
+      ++propertyIndex;
+    }
+
+    output.mAnimationDefinitions.push_back(std::move(animationDef));
+  }
+}
+
+void ProcessSkins(const gt::Document& doc, ConversionContext& cctx)
+{
+  // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skininversebindmatrices
+  // If an inverseBindMatrices accessor was provided, we'll load the joint data from the buffer,
+  // otherwise we'll set identity matrices for inverse bind pose.
+  struct IInverseBindMatrixProvider
+  {
+    virtual ~IInverseBindMatrixProvider() {}
+    virtual void Provide(Matrix& ibm) = 0;
+  };
+
+  struct InverseBindMatrixAccessor : public IInverseBindMatrixProvider
+  {
+    std::ifstream mStream;
+    const uint32_t mElementSizeBytes;
+
+    InverseBindMatrixAccessor(const gt::Accessor& accessor, const std::string& path)
+    : mStream(path + std::string(accessor.mBufferView->mBuffer->mUri), std::ios::binary),
+      mElementSizeBytes(accessor.GetElementSizeBytes())
+    {
+      DALI_ASSERT_ALWAYS(mStream);
+      DALI_ASSERT_DEBUG(accessor.mType == gt::AccessorType::MAT4 && accessor.mComponentType == gt::Component::FLOAT);
+
+      mStream.seekg(accessor.mBufferView->mByteOffset + accessor.mByteOffset);
+    }
+
+    virtual void Provide(Matrix& ibm) override
+    {
+      DALI_ASSERT_ALWAYS(mStream.read(reinterpret_cast<char*>(ibm.AsFloat()), mElementSizeBytes));
+    }
+  };
+
+  struct DefaultInverseBindMatrixProvider : public IInverseBindMatrixProvider
+  {
+    virtual void Provide(Matrix& ibm) override
+    {
+      ibm = Matrix::IDENTITY;
+    }
+  };
+
+  auto& resources = cctx.mOutput.mResources;
+  resources.mSkeletons.reserve(doc.mSkins.size());
+
+  for (auto& s : doc.mSkins)
+  {
+    std::unique_ptr<IInverseBindMatrixProvider> ibmProvider;
+    if (s.mInverseBindMatrices)
+    {
+      ibmProvider.reset(new InverseBindMatrixAccessor(*s.mInverseBindMatrices, cctx.mPath));
+    }
+    else
+    {
+      ibmProvider.reset(new DefaultInverseBindMatrixProvider());
+    }
+
+    SkeletonDefinition skeleton;
+    if (s.mSkeleton.GetIndex() != INVALID_INDEX)
+    {
+      skeleton.mRootNodeIdx = cctx.mNodeIndices.GetRuntimeId(s.mSkeleton.GetIndex());
+    }
+
+    skeleton.mJoints.resize(s.mJoints.size());
+    auto iJoint = skeleton.mJoints.begin();
+    for (auto& j : s.mJoints)
+    {
+      iJoint->mNodeIdx = cctx.mNodeIndices.GetRuntimeId(j.GetIndex());
+
+      ibmProvider->Provide(iJoint->mInverseBindMatrix);
+
+      ++iJoint;
+    }
+
+    resources.mSkeletons.push_back(std::move(skeleton));
+  }
+}
+
+void ProduceShaders(ShaderDefinitionFactory& shaderFactory, SceneDefinition& scene)
+{
+  for (size_t i0 = 0, i1 = scene.GetNodeCount(); i0 != i1; ++i0)
+  {
+    auto nodeDef = scene.GetNode(i0);
+    if (auto renderable = nodeDef->mRenderable.get())
+    {
+      renderable->mShaderIdx = shaderFactory.ProduceShader(*nodeDef);
+    }
+  }
+}
+
+void SetObjectReaders()
+{
+  js::SetObjectReader(BUFFER_READER);
+  js::SetObjectReader(BUFFER_VIEW_READER);
+  js::SetObjectReader(BUFFER_VIEW_CLIENT_READER);
+  js::SetObjectReader(COMPONENT_TYPED_BUFFER_VIEW_CLIENT_READER);
+  js::SetObjectReader(ACCESSOR_SPARSE_READER);
+  js::SetObjectReader(ACCESSOR_READER);
+  js::SetObjectReader(IMAGE_READER);
+  js::SetObjectReader(SAMPLER_READER);
+  js::SetObjectReader(TEXURE_READER);
+  js::SetObjectReader(TEXURE_INFO_READER);
+  js::SetObjectReader(MATERIAL_PBR_READER);
+  js::SetObjectReader(MATERIAL_READER);
+  js::SetObjectReader(MESH_PRIMITIVE_READER);
+  js::SetObjectReader(MESH_READER);
+  js::SetObjectReader(SKIN_READER);
+  js::SetObjectReader(CAMERA_PERSPECTIVE_READER);
+  js::SetObjectReader(CAMERA_ORTHOGRAPHIC_READER);
+  js::SetObjectReader(CAMERA_READER);
+  js::SetObjectReader(NODE_READER);
+  js::SetObjectReader(ANIMATION_SAMPLER_READER);
+  js::SetObjectReader(ANIMATION_TARGET_READER);
+  js::SetObjectReader(ANIMATION_CHANNEL_READER);
+  js::SetObjectReader(ANIMATION_READER);
+  js::SetObjectReader(SCENE_READER);
+}
+
+}  // nonamespace
+
+void LoadGltfScene(const std::string& url, ShaderDefinitionFactory& shaderFactory, LoadResult& params)
+{
+  bool failed = false;
+  auto js = LoadTextFile(url.c_str(), &failed);
+  if (failed)
+  {
+    throw std::runtime_error("Failed to load " + url);
+  }
+
+  json::unique_ptr root(json_parse(js.c_str(), js.size()));
+  if (!root)
+  {
+    throw std::runtime_error("Failed to parse " + url);
+  }
+
+  static bool setObjectReaders = true;
+  if (setObjectReaders)
+  {
+    // NOTE: only referencing own, anonymous namespace, const objects; the pointers will never need to change.
+    SetObjectReaders();
+    setObjectReaders = false;
+  }
+
+  gt::Document doc;
+
+  auto& rootObj = js::Cast<json_object_s>(*root);
+  auto jsAsset = js::FindObjectChild("asset", rootObj);
+  auto jsAssetVersion = js::FindObjectChild("version", js::Cast<json_object_s>(*jsAsset));
+  doc.mAsset.mVersion = js::Read::StringView(*jsAssetVersion);
+
+  gt::SetRefReaderObject(doc);
+  DOCUMENT_READER.Read(rootObj, doc);
+
+  auto path = url.substr(0, url.rfind('/') + 1);
+  ConversionContext cctx{ params, path, INVALID_INDEX };
+
+  ConvertMaterials(doc, cctx);
+  ConvertMeshes(doc, cctx);
+  ConvertNodes(doc, cctx);
+  ConvertAnimations(doc, cctx);
+
+  ProcessSkins(doc, cctx);
+
+  ProduceShaders(shaderFactory, params.mScene);
+  params.mScene.EnsureUniqueSkinningShaderInstances(params.mResources);
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/gltf2-loader.h b/dali-scene-loader/public-api/gltf2-loader.h
new file mode 100644 (file)
index 0000000..ce3ec79
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef DALI_SCENE_LOADER_GLTF2_LOADER_H
+#define DALI_SCENE_LOADER_GLTF2_LOADER_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+ // INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include <string>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+struct CameraParameters;
+struct LoadResult;
+class ShaderDefinitionFactory;
+
+/**
+ * @brief Loads the scene from the glTF file located at @a url, storing the results in @a params.
+ * @note Will throw std::runtime_error for JSON entities with types mismatching expectations, carrying
+ *  invalid values, or I/O errors.
+ */
+DALI_SCENE_LOADER_API void LoadGltfScene(const std::string& url, ShaderDefinitionFactory& shaderFactory, LoadResult& params);
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_GLTF2_LOADER_H
diff --git a/dali-scene-loader/public-api/index.h b/dali-scene-loader/public-api/index.h
new file mode 100644 (file)
index 0000000..278f19a
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef DALI_SCENE_LOADER_INDEX_H_
+#define DALI_SCENE_LOADER_INDEX_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include <cstdint>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+using Index = uint32_t;
+
+constexpr Index INVALID_INDEX = static_cast<Index>(-1);
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_INDEX_H_
diff --git a/dali-scene-loader/public-api/ktx-loader.cpp b/dali-scene-loader/public-api/ktx-loader.cpp
new file mode 100644 (file)
index 0000000..839c03c
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+ // FILE HEADER
+#include "dali-scene-loader/public-api/ktx-loader.h"
+
+ // EXTERNAL INCLUDES
+#include "dali/public-api/rendering/texture.h"
+#include <fstream>
+#include <memory>
+
+namespace Dali
+{
+
+namespace
+{
+
+// http://github.khronos.org/KTX-Specification/
+const uint8_t KTX_ID_HEAD[] = { 0xAB, 0x4B, 0x54, 0x58, 0x20 };
+const uint8_t KTX_ID_TAIL[] = { 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
+
+const uint8_t KTX_VERSION_1_1[] = { 0x31, 0x31 };
+const uint8_t KTX_VERSION_2_0[] = { 0x32, 0x30 };
+
+static_assert(sizeof(KTX_ID_HEAD) + sizeof(KTX_ID_TAIL) == 10);
+static_assert(sizeof(KTX_VERSION_1_1) == 2);
+static_assert(sizeof(KTX_VERSION_2_0) == sizeof(KTX_VERSION_1_1));
+
+void FreeBuffer(uint8_t* buffer)
+{
+  delete[] buffer;
+}
+} // namespace
+
+namespace SceneLoader
+{
+struct KtxFileHeader
+{
+  uint8_t   identifier[12];
+  uint32_t  endianness;
+  uint32_t  glType;    //(UNSIGNED_BYTE, UNSIGNED_SHORT_5_6_5, etc.)
+  uint32_t  glTypeSize;
+  uint32_t  glFormat;  //(RGB, RGBA, BGRA, etc.)
+  uint32_t  glInternalFormat; //For uncompressed textures, specifies the internalformat parameter passed to glTexStorage*D or glTexImage*D
+  uint32_t  glBaseInternalFormat;
+  uint32_t  pixelWidth;
+  uint32_t  pixelHeight;
+  uint32_t  pixelDepth;
+  uint32_t  numberOfArrayElements;
+  uint32_t  numberOfFaces; //Cube map faces are stored in the order: +X, -X, +Y, -Y, +Z, -Z.
+  uint32_t  numberOfMipmapLevels;
+  uint32_t  bytesOfKeyValueData;
+
+  bool IsIdentifierValid() const
+  {
+    return std::equal(KTX_ID_HEAD, std::end(KTX_ID_HEAD), identifier) &&
+      (std::equal(KTX_VERSION_1_1, std::end(KTX_VERSION_1_1), identifier + sizeof(KTX_ID_HEAD)) ||
+        std::equal(KTX_VERSION_2_0, std::end(KTX_VERSION_2_0), identifier + sizeof(KTX_ID_HEAD))) &&
+      std::equal(KTX_ID_TAIL, std::end(KTX_ID_TAIL), identifier + (sizeof(KTX_ID_HEAD) + sizeof(KTX_VERSION_1_1)));
+  }
+};
+
+/**
+ * Convert KTX format to Pixel::Format
+ */
+bool ConvertPixelFormat(const uint32_t ktxPixelFormat, Pixel::Format& format)
+{
+  switch (ktxPixelFormat)
+  {
+  case 0x93B0: // GL_COMPRESSED_RGBA_ASTC_4x4
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_4x4_KHR;
+    break;
+  }
+  case 0x93B1: // GL_COMPRESSED_RGBA_ASTC_5x4
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_5x4_KHR;
+    break;
+  }
+  case 0x93B2: // GL_COMPRESSED_RGBA_ASTC_5x5
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_5x5_KHR;
+    break;
+  }
+  case 0x93B3: // GL_COMPRESSED_RGBA_ASTC_6x5
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_6x5_KHR;
+    break;
+  }
+  case 0x93B4: // GL_COMPRESSED_RGBA_ASTC_6x6
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_6x6_KHR;
+    break;
+  }
+  case 0x93B5: // GL_COMPRESSED_RGBA_ASTC_8x5
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_8x5_KHR;
+    break;
+  }
+  case 0x93B6: // GL_COMPRESSED_RGBA_ASTC_8x6
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_8x6_KHR;
+    break;
+  }
+  case 0x93B7: // GL_COMPRESSED_RGBA_ASTC_8x8
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_8x8_KHR;
+    break;
+  }
+  case 0x93B8: // GL_COMPRESSED_RGBA_ASTC_10x5
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_10x5_KHR;
+    break;
+  }
+  case 0x93B9: // GL_COMPRESSED_RGBA_ASTC_10x6
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_10x6_KHR;
+    break;
+  }
+  case 0x93BA: // GL_COMPRESSED_RGBA_ASTC_10x8
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_10x8_KHR;
+    break;
+  }
+  case 0x93BB: // GL_COMPRESSED_RGBA_ASTC_10x10
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_10x10_KHR;
+    break;
+  }
+  case 0x93BC: // GL_COMPRESSED_RGBA_ASTC_12x10
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_12x10_KHR;
+    break;
+  }
+  case 0x93BD: // GL_COMPRESSED_RGBA_ASTC_12x12
+  {
+    format = Pixel::COMPRESSED_RGBA_ASTC_12x12_KHR;
+    break;
+  }
+  case 0x881B: // GL_RGB16F
+  {
+    format = Pixel::RGB16F;
+    break;
+  }
+  case 0x8815: // GL_RGB32F
+  {
+    format = Pixel::RGB32F;
+    break;
+  }
+  case 0x8C3A: // GL_R11F_G11F_B10F
+  {
+    format = Pixel::RGB32F;
+    break;
+  }
+  case 0x8D7C: // GL_RGBA8UI
+  {
+    format = Pixel::RGBA8888;
+    break;
+  }
+  case 0x8D7D: // GL_RGB8UI
+  {
+    format = Pixel::RGB888;
+    break;
+  }
+  default:
+  {
+    return false;
+  }
+  }
+
+  return true;
+}
+
+Texture CubeData::CreateTexture() const
+{
+  Texture texture = Texture::New(TextureType::TEXTURE_CUBE, data[0][0].GetPixelFormat(),
+    data[0][0].GetWidth(), data[0][0].GetHeight());
+  for (size_t iSide = 0u, iEndSize = data.size(); iSide < iEndSize; ++iSide)
+  {
+    auto& side = data[iSide];
+    for (size_t iMipLevel = 0u, iEndMipLevel = data[0].size(); iMipLevel < iEndMipLevel; ++iMipLevel)
+    {
+      texture.Upload(side[iMipLevel], CubeMapLayer::POSITIVE_X + iSide, iMipLevel,
+        0u, 0u, side[iMipLevel].GetWidth(), side[iMipLevel].GetHeight());
+    }
+  }
+
+  return texture;
+}
+
+bool LoadCubeMapData(const std::string& path, CubeData& cubedata)
+{
+  std::fstream fp(path, std::ios::in | std::ios::binary);
+  if (fp.is_open() == false)
+  {
+    return false;
+  }
+
+  KtxFileHeader header;
+  if (fp.read(reinterpret_cast<char*>(&header), sizeof(KtxFileHeader)).good() == false)
+  {
+    return false;
+  }
+
+  if (!header.IsIdentifierValid())
+  {
+    return false;
+  }
+
+  // Skip the key-values:
+  if (fp.seekg(header.bytesOfKeyValueData, fp.cur).good() == false)
+  {
+    return false;
+  }
+
+  header.numberOfMipmapLevels = std::max(header.numberOfMipmapLevels, 1u);
+  header.numberOfArrayElements = std::max(header.numberOfArrayElements, 1u);
+  header.pixelDepth = std::max(header.pixelDepth, 1u);
+  header.pixelHeight = std::max(header.pixelHeight, 1u);
+
+  cubedata.data.resize(header.numberOfFaces);
+  for (uint32_t face = 0u; face < header.numberOfFaces; ++face)
+  {
+    cubedata.data[face].resize(header.numberOfMipmapLevels);
+  }
+
+  Pixel::Format daliformat = Pixel::RGB888;
+
+  ConvertPixelFormat(header.glInternalFormat, daliformat);
+
+  for (uint32_t mipmapLevel = 0u; mipmapLevel < header.numberOfMipmapLevels; ++mipmapLevel)
+  {
+    uint32_t byteSize = 0u;
+    if (fp.read(reinterpret_cast<char*>(&byteSize), sizeof(byteSize)).good() == false)
+    {
+      return false;
+    }
+
+    if (0u != byteSize % 4u)
+    {
+      byteSize += 4u - byteSize % 4u;
+    }
+
+    for (uint32_t arrayElement = 0u; arrayElement < header.numberOfArrayElements; ++arrayElement) //arrayElement must be 0 or 1
+    {
+      for (uint32_t face = 0u; face < header.numberOfFaces; ++face)
+      {
+        std::unique_ptr<uint8_t, void(*)(uint8_t*)>img(new uint8_t[byteSize], FreeBuffer);
+        if (fp.read(reinterpret_cast<char*>(img.get()), byteSize).good() == false)
+        {
+          return false;
+        }
+        cubedata.data[face][mipmapLevel] = PixelData::New(img.release(), byteSize, header.pixelWidth, header.pixelHeight, daliformat, PixelData::DELETE_ARRAY);
+      }
+    }
+
+    header.pixelHeight /= 2u;
+    header.pixelWidth /= 2u;
+  }
+
+  return true;
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/ktx-loader.h b/dali-scene-loader/public-api/ktx-loader.h
new file mode 100644 (file)
index 0000000..6167fef
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef DALI_SCENE_LOADER_KTX_LOADER_H
+#define DALI_SCENE_LOADER_KTX_LOADER_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/common/vector-wrapper.h"
+#include "dali/public-api/images/pixel-data.h"
+#include "dali/public-api/rendering/texture.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/**
+ * @brief Stores the pixel data objects for each face of the cube texture and their mipmaps.
+ */
+struct DALI_SCENE_LOADER_API CubeData
+{
+  std::vector< std::vector<PixelData> > data;
+
+  Texture CreateTexture() const;
+};
+
+/**
+ * @brief Loads cube map data texture from a ktx file.
+ *
+ * @param[in] path The file path.
+ * @param[out] cubedata The data structure with all pixel data objects.
+ */
+DALI_SCENE_LOADER_API bool LoadCubeMapData(const std::string& path, CubeData& cubedata);
+
+}
+}
+
+#endif // DALI_SCENE_LOADER_KTX_LOADER_H
diff --git a/dali-scene-loader/public-api/light-parameters.h b/dali-scene-loader/public-api/light-parameters.h
new file mode 100644 (file)
index 0000000..0d8d514
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef DALI_SCENE_LOADER_LIGHT_PARAMETERS_H
+#define DALI_SCENE_LOADER_LIGHT_PARAMETERS_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/math/matrix.h"
+#include "dali/public-api/math/vector3.h"
+#include <stdint.h>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+struct DALI_SCENE_LOADER_API LightParameters
+{
+  Matrix transform;
+
+  Vector3 color;
+  float intensity;
+  float shadowIntensity;
+  uint32_t shadowMapSize;
+  float orthographicSize;
+};
+
+}
+}
+
+#endif // DALI_SCENE_LOADER_LIGHT_PARAMETERS_H
diff --git a/dali-scene-loader/public-api/load-result.h b/dali-scene-loader/public-api/load-result.h
new file mode 100644 (file)
index 0000000..ca05bfd
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef DALI_SCENE_LOADER_OUTPUT_H
+#define DALI_SCENE_LOADER_OUTPUT_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+#include "dali-scene-loader/public-api/animation-definition.h"
+#include "dali-scene-loader/public-api/light-parameters.h"
+#include "dali-scene-loader/public-api/camera-parameters.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+class ResourceBundle;
+class SceneDefinition;
+
+/**
+ * @brief The outputs from loading and processing a scene.
+ */
+struct DALI_SCENE_LOADER_API LoadResult
+{
+  /**
+   * @brief The bundle to store resources in.
+   */
+  ResourceBundle& mResources;
+
+  /**
+   * @brief The scene definition to populate.
+   */
+  SceneDefinition& mScene;
+
+  /**
+   * @brief The list of animation definitions, in lexicographical order of their names.
+   */
+  std::vector<AnimationDefinition>& mAnimationDefinitions;
+
+  /**
+   * @brief The list of animation group definitions, in lexicographical order of their names.
+   */
+  std::vector<AnimationGroupDefinition>& mAnimationGroupDefinitions;
+
+  /**
+   * @brief The camera parameters that were loaded from the scene.
+   */
+  std::vector<CameraParameters>& mCameraParameters;
+
+  /**
+   * @brief The light parameters that were loaded from the scene.
+   */
+  std::vector<LightParameters>& mLightParameters;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_OUTPUT_H
diff --git a/dali-scene-loader/public-api/material-definition.cpp b/dali-scene-loader/public-api/material-definition.cpp
new file mode 100644 (file)
index 0000000..952a824
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/material-definition.h"
+
+// EXTERNAL INCLUDES
+#include "dali-toolkit/public-api/image-loader/sync-image-loader.h"
+
+namespace Dali
+{
+using namespace Toolkit;
+
+namespace SceneLoader
+{
+namespace
+{
+
+constexpr SamplerFlags::Type FILTER_MODES_FROM_DALI[]{
+  SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_NEAREST,
+  SamplerFlags::FILTER_LINEAR,
+  SamplerFlags::FILTER_NEAREST,
+  SamplerFlags::FILTER_LINEAR,
+  SamplerFlags::FILTER_NEAREST | SamplerFlags::FILTER_MIPMAP_NEAREST,
+  SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_NEAREST,
+  SamplerFlags::FILTER_NEAREST | SamplerFlags::FILTER_MIPMAP_LINEAR,
+  SamplerFlags::FILTER_LINEAR | SamplerFlags::FILTER_MIPMAP_LINEAR,
+};
+
+constexpr SamplerFlags::Type WRAP_MODES_FROM_DALI[]{
+  SamplerFlags::WRAP_CLAMP,
+  SamplerFlags::WRAP_CLAMP,
+  SamplerFlags::WRAP_REPEAT,
+  SamplerFlags::WRAP_MIRROR,
+};
+
+constexpr FilterMode::Type  FILTER_MODES_TO_DALI[]{
+  FilterMode::NEAREST,
+  FilterMode::LINEAR,
+  FilterMode::NEAREST_MIPMAP_NEAREST,
+  FilterMode::LINEAR_MIPMAP_NEAREST,
+  FilterMode::NEAREST_MIPMAP_LINEAR,
+  FilterMode::LINEAR_MIPMAP_LINEAR,
+};
+
+constexpr WrapMode::Type WRAP_MODES_TO_DALI[]{
+  WrapMode::REPEAT,
+  WrapMode::CLAMP_TO_EDGE,
+  WrapMode::MIRRORED_REPEAT
+};
+
+const SamplerFlags::Type SINGLE_VALUE_SAMPLER = SamplerFlags::Encode(FilterMode::NEAREST, FilterMode::NEAREST, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
+}
+
+SamplerFlags::Type SamplerFlags::Encode(FilterMode::Type minFilter, FilterMode::Type magFilter, WrapMode::Type wrapS, WrapMode::Type wrapT)
+{
+  return FILTER_MODES_FROM_DALI[minFilter] | ((FILTER_MODES_FROM_DALI[magFilter] & FILTER_MAG_BITS) << FILTER_MAG_SHIFT) |
+    (WRAP_MODES_FROM_DALI[wrapS] << WRAP_S_SHIFT) | (WRAP_MODES_FROM_DALI[wrapT] << WRAP_T_SHIFT);
+}
+
+FilterMode::Type SamplerFlags::GetMinFilter(Type flags)
+{
+  return FILTER_MODES_TO_DALI[flags & FILTER_MIN_MASK];
+}
+
+FilterMode::Type SamplerFlags::GetMagFilter(Type flags)
+{
+  return FILTER_MODES_TO_DALI[(flags >> FILTER_MAG_SHIFT) & FILTER_MAG_MASK];
+}
+
+WrapMode::Type SamplerFlags::GetWrapS(Type flags)
+{
+  return WRAP_MODES_TO_DALI[(flags >> WRAP_S_SHIFT) & WRAP_S_MASK];
+}
+
+WrapMode::Type SamplerFlags::GetWrapT(Type flags)
+{
+  return WRAP_MODES_TO_DALI[(flags >> WRAP_T_SHIFT) & WRAP_T_MASK];
+}
+
+Sampler SamplerFlags::MakeSampler(Type flags)
+{
+  auto sampler = Sampler::New();
+  sampler.SetFilterMode(GetMinFilter(flags), GetMagFilter(flags));
+  sampler.SetWrapMode(GetWrapS(flags), GetWrapT(flags));
+  return sampler;
+}
+
+TextureDefinition::TextureDefinition(const std::string& imageUri, SamplerFlags::Type samplerFlags)
+: mImageUri(imageUri),
+  mSamplerFlags(samplerFlags)
+{}
+
+MaterialDefinition::RawData
+  MaterialDefinition::LoadRaw(const std::string& imagesPath) const
+{
+  RawData raw;
+
+  const bool hasTransparency = MaskMatch(mFlags, TRANSPARENCY);
+  uint32_t numBuffers = mTextureStages.size() + (hasTransparency ?
+    !CheckTextures(ALBEDO) + !CheckTextures(METALLIC | ROUGHNESS) + !CheckTextures(NORMAL) :
+    !CheckTextures(ALBEDO | METALLIC) + !CheckTextures(NORMAL | ROUGHNESS));
+  if (numBuffers == 0)
+  {
+    return raw;
+  }
+  raw.mTextures.reserve(numBuffers);
+
+  // Load textures
+  auto iTexture = mTextureStages.cbegin();
+  auto checkStage = [&](uint32_t flags) {
+    return iTexture != mTextureStages.end() && MaskMatch(iTexture->mSemantic, flags);
+  };
+
+  // Check for compulsory textures: Albedo, Metallic, Roughness, Normal
+  if (checkStage(ALBEDO | METALLIC))
+  {
+    raw.mTextures.push_back({ SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags });
+    ++iTexture;
+
+    if (checkStage(NORMAL | ROUGHNESS))
+    {
+      raw.mTextures.push_back({ SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags });
+      ++iTexture;
+    }
+    else // single value normal-roughness
+    {
+      const auto bufferSize = 4;
+      uint8_t* buffer = new uint8_t[bufferSize]{ 0x7f, 0x7f, 0xff, 0xff }; // normal of (0, 0, 1), roughness of 1
+      raw.mTextures.push_back({ PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER });
+    }
+  }
+  else
+  {
+    if (checkStage(ALBEDO))
+    {
+      raw.mTextures.push_back({ SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags });
+      ++iTexture;
+    }
+    else // single value albedo, albedo-alpha or albedo-metallic
+    {
+      uint32_t bufferSize = 4;
+      uint8_t* buffer = nullptr;
+      auto format = Pixel::Format::RGBA8888;
+      if (hasTransparency)  // albedo-alpha
+      {
+        buffer = new uint8_t[bufferSize];
+        buffer[3] = static_cast<uint8_t>(mColor.a * 255.f);
+      }
+      else if (!checkStage(METALLIC | ROUGHNESS))  // albedo-metallic
+      {
+        buffer = new uint8_t[bufferSize];
+        buffer[3] = 0xff;  // metallic of 1.0
+      }
+      else  // albedo
+      {
+        bufferSize = 3;
+        buffer = new uint8_t[bufferSize];
+        format = Pixel::Format::RGB888;
+      }
+      buffer[0] = static_cast<uint8_t>(mColor.r * 255.f);
+      buffer[1] = static_cast<uint8_t>(mColor.g * 255.f);
+      buffer[2] = static_cast<uint8_t>(mColor.b * 255.f);
+      raw.mTextures.push_back({ PixelData::New(buffer, bufferSize, 1, 1, format, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER });
+    }
+
+    // If we have transparency, or an image based albedo map, we will have to continue with separate metallicRoughness + normal.
+    const bool createMetallicRoughnessAndNormal = hasTransparency || std::distance(mTextureStages.begin(), iTexture) > 0;
+    if (checkStage(METALLIC | ROUGHNESS))
+    {
+      raw.mTextures.push_back({ SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags });
+      ++iTexture;
+    }
+    else if (createMetallicRoughnessAndNormal)
+    {
+      // NOTE: we want to set both metallic and roughness to 1.0; dli uses the R & A channels,
+      // glTF2 uses B & G, so we might as well just set all components to 1.0.
+      const auto bufferSize = 4;
+      uint8_t* buffer = new uint8_t[bufferSize]{ 0xff, 0xff, 0xff, 0xff };
+      raw.mTextures.push_back({ PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER });
+    }
+
+    if (checkStage(NORMAL))
+    {
+      raw.mTextures.push_back({ SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags });
+      ++iTexture;
+    }
+    else if (createMetallicRoughnessAndNormal)
+    {
+      const auto bufferSize = 3;
+      uint8_t* buffer = new uint8_t[bufferSize]{ 0x7f, 0x7f, 0xff };  // normal of (0, 0, 1)
+      raw.mTextures.push_back({ PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGB888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER });
+    }
+    else // single-value normal-roughness
+    {
+      const auto bufferSize = 4;
+      uint8_t* buffer = new uint8_t[bufferSize]{ 0x7f, 0x7f, 0xff, 0xff };  // normal of (0, 0, 1), roughness of 1.0
+      raw.mTextures.push_back({ PixelData::New(buffer, bufferSize, 1, 1, Pixel::RGBA8888, PixelData::DELETE_ARRAY), SINGLE_VALUE_SAMPLER });
+    }
+  }
+
+  // Extra textures. TODO: emissive, occlusion etc.
+  if (checkStage(SUBSURFACE))
+  {
+    raw.mTextures.push_back({ SyncImageLoader::Load(imagesPath + iTexture->mTexture.mImageUri), iTexture->mTexture.mSamplerFlags });
+    ++iTexture;
+  }
+
+  return raw;
+}
+
+TextureSet MaterialDefinition::Load(const EnvironmentDefinition::Vector& environments, RawData&& raw) const
+{
+  auto textureSet = TextureSet::New();
+
+  uint32_t n = 0;
+  for (auto& tData : raw.mTextures)
+  {
+    auto& pixels = tData.mPixels;
+    auto texture = Texture::New(TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight());
+    texture.Upload(tData.mPixels, 0, 0, 0, 0, pixels.GetWidth(), pixels.GetHeight());
+    if (tData.mSamplerFlags & SamplerFlags::MIPMAP_MASK)
+    {
+      texture.GenerateMipmaps();
+    }
+
+    textureSet.SetTexture(n, texture);
+    textureSet.SetSampler(n, SamplerFlags::MakeSampler(tData.mSamplerFlags));
+
+    ++n;
+  }
+
+  // Assign textures to slots -- starting with 2D ones, then cubemaps, if any.
+  if (mEnvironmentIdx < environments.size())
+  {
+    auto& envTextures = environments[mEnvironmentIdx].second;
+    if (envTextures.mDiffuse)
+    {
+      textureSet.SetTexture(n, envTextures.mDiffuse);
+      ++n;
+    }
+
+    if (envTextures.mSpecular)
+    {
+      auto specularSampler = Sampler::New();
+      specularSampler.SetWrapMode(WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
+      specularSampler.SetFilterMode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR);
+
+      textureSet.SetTexture(n, envTextures.mSpecular);
+      textureSet.SetSampler(n, specularSampler);
+    }
+  }
+  else
+  {
+    ExceptionFlinger(ASSERT_LOCATION) << "Environment index (" << mEnvironmentIdx << ") out of bounds (" <<
+      environments.size() << ").";
+  }
+
+  return textureSet;
+}
+
+bool MaterialDefinition::CheckTextures(uint32_t flags) const
+{
+  return std::find_if(mTextureStages.begin(), mTextureStages.end(), [flags](const TextureStage& ts) {
+    return MaskMatch(ts.mSemantic, flags);
+  }) != mTextureStages.end();
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/material-definition.h b/dali-scene-loader/public-api/material-definition.h
new file mode 100644 (file)
index 0000000..d360c88
--- /dev/null
@@ -0,0 +1,234 @@
+#ifndef DALI_SCENE_LOADER_MATERIAL_DEFINITION_H
+#define DALI_SCENE_LOADER_MATERIAL_DEFINITION_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+#include "dali-scene-loader/public-api/environment-definition.h"
+#include "dali-scene-loader/public-api/index.h"
+#include "dali-scene-loader/public-api/utils.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/math/vector4.h"
+#include "dali/public-api/common/vector-wrapper.h"
+#include <cmath>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/**
+ * @brief Helper enum for encoding and decoding sampler states.
+ */
+struct DALI_SCENE_LOADER_API SamplerFlags
+{
+  using Type = uint8_t;
+
+  enum Values : Type
+  {
+    // Filter - 3 bits
+    FILTER_NEAREST = 0,
+    FILTER_LINEAR = NthBit(0),
+    FILTER_MIPMAP_NEAREST = NthBit(1),
+    FILTER_MIPMAP_LINEAR = NthBit(2),
+
+    // Wrap - 2 bits
+    WRAP_REPEAT = 0,
+    WRAP_CLAMP = NthBit(0),
+    WRAP_MIRROR = NthBit(1),
+
+    // Layout - apply shift, then mask
+    FILTER_MIN_BITS = 3,
+    FILTER_MIN_MASK = NthBit(FILTER_MIN_BITS) - 1,
+
+    FILTER_MAG_BITS = 1,
+    FILTER_MAG_SHIFT = FILTER_MIN_BITS,
+    FILTER_MAG_MASK = NthBit(FILTER_MAG_BITS) - 1,
+
+    WRAP_S_BITS = 2,
+    WRAP_S_SHIFT = FILTER_MAG_SHIFT + FILTER_MAG_BITS,
+    WRAP_S_MASK = NthBit(WRAP_S_BITS) - 1,
+
+    WRAP_T_BITS = 2,
+    WRAP_T_SHIFT = WRAP_S_SHIFT + WRAP_S_BITS,
+    WRAP_T_MASK = NthBit(WRAP_T_BITS) - 1,
+
+    // Diagnostics
+    MIPMAP_MASK = FILTER_MIPMAP_LINEAR | FILTER_MIPMAP_NEAREST,
+
+    // Default
+    DEFAULT = FILTER_LINEAR | (FILTER_LINEAR << FILTER_MAG_SHIFT) | (WRAP_REPEAT << WRAP_S_SHIFT) | (WRAP_REPEAT << WRAP_T_SHIFT),  // LINEAR filters, REPEAT wraps
+  };
+
+  /**
+   * @return SamplerFlags bit pattern calculated from the given Dali Sampler settings.
+   */
+  static Type Encode(FilterMode::Type minFilter, FilterMode::Type magFilter,
+    WrapMode::Type wrapS, WrapMode::Type wrapT);
+
+  /**
+   * @brief Decodes the minification filter patter of @a flags into the corresponding FilterMode.
+   */
+  static FilterMode::Type  GetMinFilter(Type flags);
+
+  /**
+   * @brief Decodes the magnification filter patter of @a flags into the corresponding FilterMode.
+   */
+  static FilterMode::Type  GetMagFilter(Type flags);
+
+  /**
+   * @brief Decodes the horizontal wrap pattern of @a flags into the corresponding WrapMode.
+   */
+  static WrapMode::Type GetWrapS(Type flags);
+
+  /**
+   * @brief Decodes the vertical wrap pattern of @a flags into the corresponding WrapMode.
+   */
+  static WrapMode::Type GetWrapT(Type flags);
+
+  /**
+   * @brief Creates a Sampler with the settings encoded in @a flags.
+   */
+  static Sampler MakeSampler(Type flags);
+};
+
+/**
+ * @brief Defines a texture from a combination of an image URI and its sampler definition.
+ */
+struct DALI_SCENE_LOADER_API TextureDefinition
+{
+  std::string mImageUri;
+  SamplerFlags::Type mSamplerFlags;
+
+  TextureDefinition(const std::string& imageUri = "", SamplerFlags::Type samplerFlags = SamplerFlags::DEFAULT);
+};
+
+/**
+ * @brief Defines a material with a number of texture stages, whether mipmappping
+ *  is enabled, and an index of an environment (usually of all environments in a
+ *  scene). Textures from the environment are added last when the DALi TextureSet
+ *  is being created.
+ */
+struct DALI_SCENE_LOADER_API MaterialDefinition
+{
+  enum Flags : uint32_t
+  {
+    // Texture semantics
+    ALBEDO = NthBit(0),
+    METALLIC = NthBit(1),
+    ROUGHNESS = NthBit(2),
+    NORMAL = NthBit(3),
+    EMISSIVE = NthBit(4),  // TODO: support
+    OCCLUSION = NthBit(5),  // TODO: support
+    SUBSURFACE = NthBit(6),  // Note: dli-only
+
+    // Other binary options
+    TRANSPARENCY = NthBit(20),
+    GLTF_CHANNELS = NthBit(21),  // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#pbrmetallicroughnessmetallicroughnesstexture
+
+    // Alpha cutoff - reserved from the 24th bit
+    ALPHA_CUTOFF_BITS = 8,
+    ALPHA_CUTOFF_SHIFT = sizeof(uint32_t) * 8 - ALPHA_CUTOFF_BITS,
+    ALPHA_CUTOFF_MASK = (1 << ALPHA_CUTOFF_BITS) - 1,
+  };
+
+  /**
+   * @brief A(n image based) texture that's used in a material.
+   */
+  struct TextureStage
+  {
+    uint32_t mSemantic;
+    TextureDefinition mTexture;
+  };
+
+  using Vector = std::vector<std::pair<MaterialDefinition, TextureSet>>;
+
+  struct RawData
+  {
+    struct TextureData
+    {
+      PixelData mPixels;
+      SamplerFlags::Type mSamplerFlags;
+    };
+
+    std::vector<TextureData> mTextures;
+  };
+
+  MaterialDefinition() = default;
+
+  MaterialDefinition(const MaterialDefinition&) = delete;
+  MaterialDefinition& operator=(const MaterialDefinition&) = delete;
+
+  MaterialDefinition(MaterialDefinition&&) = default;
+  MaterialDefinition& operator=(MaterialDefinition&&) = default;
+
+  /**
+   * @brief Loads (or, in the case of solid color materials, creates) raw pixel data,
+   *  which is then returned.
+   * @note This may be called from any thread.
+   */
+  RawData LoadRaw(const std::string& imagesPath) const;
+
+  /**
+   * @brief Creates Textures from the pixel data in @a raw, gets the
+   *  the cube maps from the iEnvironment'th element of @a environments,
+   *  then creates a DALi TextureSet and returns it.
+   * @note This must be called from the event thread.
+   * @note The textures are added in the following order: 2D, cube maps.
+   */
+  TextureSet Load(const EnvironmentDefinition::Vector& environments, RawData&& raw) const;
+
+  /**
+   * @brief Checks if the given mask matches any of the textures defined.
+   */
+  bool CheckTextures(uint32_t flags) const;
+
+  /**
+   * @return The alpha test reference value.
+   * @note A value of 0.f means no alpha testing.
+   */
+  float GetAlphaCutoff() const
+  {
+    return ((mFlags >> ALPHA_CUTOFF_SHIFT) & ALPHA_CUTOFF_MASK) / 255.f;
+  }
+
+  /**
+   * @brief Encodes the alpha test reference @a value in flags.
+   * @note A value of 0.f means no alpha testing.
+   */
+  void SetAlphaCutoff(float value)
+  {
+    DALI_ASSERT_DEBUG(value >= 0.f && value <= 1.f);
+    mFlags |= static_cast<uint8_t>(std::round(value * 255.f)) << ALPHA_CUTOFF_SHIFT;
+  }
+
+public: // DATA
+  uint32_t mFlags = 0x0;
+
+  Index mEnvironmentIdx = 0;
+  Vector4 mColor = Color::WHITE;
+  float mMetallic = 1.f;
+  float mRoughness = 1.f;
+  std::vector<TextureStage> mTextureStages;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_MATERIAL_DEFINITION_H
diff --git a/dali-scene-loader/public-api/matrix-stack.cpp b/dali-scene-loader/public-api/matrix-stack.cpp
new file mode 100644 (file)
index 0000000..9d4454f
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+* Copyright (c) 2020 Samsung Electronics Co., Ltd.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+#include "dali-scene-loader/public-api/matrix-stack.h"
+#include "dali-scene-loader/public-api/utils.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+MatrixStack::MatrixStack()
+{
+  mStack.reserve(16);
+}
+
+bool MatrixStack::IsEmpty() const
+{
+  return mStack.empty();
+}
+
+void MatrixStack::Push(const Matrix& model)
+{
+  if (mStack.empty())
+  {
+    mStack.push_back(model);
+  }
+  else
+  {
+    Matrix m{ false };
+    Matrix::Multiply(m, model, mStack.back());
+    mStack.push_back(m);
+  }
+}
+
+const Matrix& MatrixStack::Top() const
+{
+  return mStack.back();
+}
+
+void MatrixStack::Pop()
+{
+  DALI_ASSERT_ALWAYS(mStack.size() > 0);
+  mStack.pop_back();
+}
+
+void MatrixStack::PopAll()
+{
+  mStack.clear();
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/matrix-stack.h b/dali-scene-loader/public-api/matrix-stack.h
new file mode 100644 (file)
index 0000000..f764da1
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef DALI_SCENE_LOADER_MATRIX_STACK_H_
+#define DALI_SCENE_LOADER_MATRIX_STACK_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/math/matrix.h"
+#include "dali/public-api/common/vector-wrapper.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/**
+ * @brief A stack of matrices whereby each newly pushed matrix is stored
+ *  after being multiplied by the previous one (if any).
+ * @note Current implementation reserves space for 16 matrices.
+ */
+class DALI_SCENE_LOADER_API MatrixStack
+{
+public:
+  MatrixStack();
+
+  bool IsEmpty() const;
+  void Push(const Matrix& model);
+  const Matrix& Top() const;
+  void Pop();
+  void PopAll();  // clears the stack, but retains the storage.
+
+private:
+  std::vector<Matrix> mStack;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_MATRIX_STACK_H_
diff --git a/dali-scene-loader/public-api/mesh-definition.cpp b/dali-scene-loader/public-api/mesh-definition.cpp
new file mode 100644 (file)
index 0000000..2cea908
--- /dev/null
@@ -0,0 +1,880 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/mesh-definition.h"
+
+// EXTERNAL INCLUDES
+#include "dali/devel-api/adaptor-framework/pixel-buffer.h"
+#include <fstream>
+#include <cstring>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+namespace
+{
+
+using Uint16Vector4 = uint16_t[4];
+
+class IndexProvider
+{
+public:
+  IndexProvider(const uint16_t* indices)
+  : mData(reinterpret_cast<uintptr_t>(indices)),
+    mFunc(indices ? IncrementPointer : Increment)
+  {}
+
+  uint16_t operator()()
+  {
+    return mFunc(mData);
+  }
+
+private:
+  static uint16_t Increment(uintptr_t& data)
+  {
+    return static_cast<uint16_t>(data++);
+  }
+
+  static uint16_t IncrementPointer(uintptr_t& data)
+  {
+    auto iPtr = reinterpret_cast<const uint16_t*>(data);
+    auto result = *iPtr;
+    data = reinterpret_cast<uintptr_t>(++iPtr);
+    return result;
+  }
+
+  uintptr_t mData;
+  uint16_t(*mFunc)(uintptr_t&);
+};
+
+
+const std::string QUAD("quad");
+
+///@brief Reads a blob from the given stream @a source into @a target, which must have
+/// at least @a descriptor.length bytes.
+bool ReadBlob(const MeshDefinition::Blob& descriptor, std::istream& source, uint8_t* target)
+{
+  if (!source.seekg(descriptor.mOffset, std::istream::beg))
+  {
+    return false;
+  }
+
+  if (descriptor.IsConsecutive())
+  {
+    return !!source.read(reinterpret_cast<char*>(target), descriptor.mLength);
+  }
+  else
+  {
+    DALI_ASSERT_DEBUG(descriptor.mStride > descriptor.mElementSizeHint);
+    const uint32_t diff = descriptor.mStride - descriptor.mElementSizeHint;
+    uint32_t readSize = 0;
+    while (readSize < descriptor.mLength &&
+      source.read(reinterpret_cast<char*>(target), descriptor.mElementSizeHint) &&
+      source.seekg(diff, std::istream::cur))
+    {
+      readSize += descriptor.mStride;
+      target += descriptor.mElementSizeHint;
+    }
+    return readSize == descriptor.mLength;
+  }
+}
+
+template <typename T>
+void ReadValues(const std::vector<uint8_t>& valuesBuffer, const std::vector<uint8_t>& indicesBuffer, uint8_t* target, uint32_t count, uint32_t elementSizeHint)
+{
+  const T* const indicesPtr = reinterpret_cast<const T* const>(indicesBuffer.data());
+  for (uint32_t index = 0u; index < count; ++index)
+  {
+    uint32_t valuesIndex = indicesPtr[index] * elementSizeHint;
+    memcpy(target + valuesIndex, &valuesBuffer[index * elementSizeHint], elementSizeHint);
+  }
+}
+
+bool ReadAccessor(const MeshDefinition::Accessor& accessor, std::istream& source, uint8_t* target)
+{
+  bool success = false;
+
+  if (accessor.mBlob.IsDefined())
+  {
+    success = ReadBlob(accessor.mBlob, source, target);
+    if (!success)
+    {
+      return false;
+    }
+  }
+
+  if (accessor.mSparse)
+  {
+    const MeshDefinition::Blob& indices = accessor.mSparse->mIndices;
+    const MeshDefinition::Blob& values = accessor.mSparse->mValues;
+
+    if (!indices.IsDefined() || !values.IsDefined())
+    {
+      return false;
+    }
+
+    const auto indicesBufferSize = indices.GetBufferSize();
+    std::vector<uint8_t> indicesBuffer(indicesBufferSize);
+    success = ReadBlob(indices, source, indicesBuffer.data());
+    if (!success)
+    {
+      return false;
+    }
+
+    const auto valuesBufferSize = values.GetBufferSize();
+    std::vector<uint8_t> valuesBuffer(valuesBufferSize);
+    success = ReadBlob(values, source, valuesBuffer.data());
+    if (!success)
+    {
+      return false;
+    }
+
+    switch (indices.mElementSizeHint)
+    {
+    case 1u:
+    {
+      ReadValues<uint8_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
+      break;
+    }
+    case 2u:
+    {
+      ReadValues<uint16_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
+      break;
+    }
+    case 4u:
+    {
+      ReadValues<uint32_t>(valuesBuffer, indicesBuffer, target, accessor.mSparse->mCount, values.mElementSizeHint);
+      break;
+    }
+    default:
+      DALI_ASSERT_DEBUG(!"Unsupported type for an index");
+    }
+  }
+
+  return success;
+}
+
+void GenerateNormals(MeshDefinition::RawData& raw)
+{
+  auto& attribs = raw.mAttribs;
+  DALI_ASSERT_DEBUG(attribs.size() > 0);  // positions
+  IndexProvider getIndex(raw.mIndices.data());
+
+  const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : raw.mIndices.size();
+
+  auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
+
+  std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(Vector3));
+  auto normals = reinterpret_cast<Vector3*>(buffer.data());
+
+  for (uint32_t i = 0; i < numIndices; i += 3)
+  {
+    uint16_t indices[]{ getIndex(), getIndex(), getIndex() };
+    Vector3 pos[]{ positions[indices[0]], positions[indices[1]], positions[indices[2]] };
+
+    Vector3 a = pos[1] - pos[0];
+    Vector3 b = pos[2] - pos[0];
+
+    Vector3 normal(a.Cross(b));
+    normals[indices[0]] += normal;
+    normals[indices[1]] += normal;
+    normals[indices[2]] += normal;
+  }
+
+  auto iEnd = normals + attribs[0].mNumElements;
+  while (normals != iEnd)
+  {
+    normals->Normalize();
+    ++normals;
+  }
+
+  attribs.push_back({ "aNormal", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer) });
+}
+
+void GenerateTangentsWithUvs(MeshDefinition::RawData& raw)
+{
+  auto& attribs = raw.mAttribs;
+  DALI_ASSERT_DEBUG(attribs.size() > 2);  // positions, normals, uvs
+  IndexProvider getIndex(raw.mIndices.data());
+
+  const uint32_t numIndices = raw.mIndices.empty() ? attribs[0].mNumElements : raw.mIndices.size();
+
+  auto* positions = reinterpret_cast<const Vector3*>(attribs[0].mData.data());
+  auto* uvs = reinterpret_cast<const Vector2*>(attribs[2].mData.data());
+
+  std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(Vector3));
+  auto tangents = reinterpret_cast<Vector3*>(buffer.data());
+
+  for (uint32_t i = 0; i < numIndices; i += 3)
+  {
+    uint16_t indices[]{ getIndex(), getIndex(), getIndex() };
+    Vector3 pos[]{ positions[indices[0]], positions[indices[1]], positions[indices[2]] };
+    Vector2 uv[]{ uvs[indices[0]], uvs[indices[1]], uvs[indices[2]] };
+
+    float x0 = pos[1].x - pos[0].x;
+    float y0 = pos[1].y - pos[0].y;
+    float z0 = pos[1].z - pos[0].z;
+
+    float x1 = pos[2].x - pos[0].x;
+    float y1 = pos[2].y - pos[0].y;
+    float z1 = pos[2].z - pos[0].z;
+
+    float s0 = uv[1].x - uv[0].x;
+    float t0 = uv[1].y - uv[0].y;
+
+    float s1 = uv[2].x - uv[0].x;
+    float t1 = uv[2].y - uv[0].y;
+
+    float r = 1.f / (s0 * t1 - t0 * s1);
+    Vector3 tangent((x0 * t1 - t0 * x1) * r, (y0 * t1 - t0 * y1) * r, (z0 * t1 - t0 * z1) * r);
+    tangents[indices[0]] += tangent;
+    tangents[indices[1]] += tangent;
+    tangents[indices[2]] += tangent;
+  }
+
+  auto* normals = reinterpret_cast<const Vector3*>(attribs[1].mData.data());
+  auto iEnd = normals + attribs[1].mNumElements;
+  while (normals != iEnd)
+  {
+    *tangents -= *normals * normals->Dot(*tangents);
+    tangents->Normalize();
+
+    ++tangents;
+    ++normals;
+  }
+  attribs.push_back({ "aTangent", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer) });
+}
+
+void GenerateTangents(MeshDefinition::RawData& raw)
+{
+  auto& attribs = raw.mAttribs;
+  DALI_ASSERT_DEBUG(attribs.size() > 1);  // positions, normals
+
+  auto* normals = reinterpret_cast<const Vector3*>(attribs[1].mData.data());
+
+  std::vector<uint8_t> buffer(attribs[0].mNumElements * sizeof(Vector3));
+  auto tangents = reinterpret_cast<Vector3*>(buffer.data());
+
+  auto iEnd = normals + attribs[1].mNumElements;
+  while (normals != iEnd)
+  {
+    Vector3 t[]{ normals->Cross(Vector3::XAXIS), normals->Cross(Vector3::YAXIS) };
+
+    *tangents = t[t[1].LengthSquared() > t[0].LengthSquared()];
+    *tangents -= *normals * normals->Dot(*tangents);
+    tangents->Normalize();
+
+    ++tangents;
+    ++normals;
+  }
+  attribs.push_back({ "aTangent", Property::VECTOR3, attribs[0].mNumElements, std::move(buffer) });
+}
+
+void CalculateTextureSize(uint32_t totalTextureSize, uint32_t& textureWidth, uint32_t& textureHeight)
+{
+  DALI_ASSERT_DEBUG(0u != totalTextureSize && "totalTextureSize is zero.")
+
+  // Calculate the dimensions of the texture.
+  // The total size of the texture is the length of the blend shapes blob.
+
+  textureWidth = 0u;
+  textureHeight = 0u;
+
+  if (0u == totalTextureSize)
+  {
+    // nothing to do.
+    return;
+  }
+
+  const uint32_t pow2 = static_cast<uint32_t>(ceil(log2(totalTextureSize)));
+  const uint32_t powWidth = pow2 >> 1u;
+  const uint32_t powHeight = pow2 - powWidth;
+
+  textureWidth = 1u << powWidth;
+  textureHeight = 1u << powHeight;
+}
+
+void CalculateGltf2BlendShapes(uint8_t* geometryBuffer, std::ifstream& binFile, const std::vector<MeshDefinition::BlendShape>& blendShapes, uint32_t numberOfVertices, float& blendShapeUnnormalizeFactor)
+{
+  uint32_t geometryBufferIndex = 0u;
+  float maxDistance = 0.f;
+  Vector3* geometryBufferV3 = reinterpret_cast<Vector3*>(geometryBuffer);
+  for (const auto& blendShape : blendShapes)
+  {
+    if (blendShape.deltas.IsDefined())
+    {
+      DALI_ASSERT_ALWAYS(((blendShape.deltas.mBlob.mLength % sizeof(Vector3) == 0u) ||
+        blendShape.deltas.mBlob.mStride >= sizeof(Vector3)) &&
+        "Blend Shape position buffer length not a multiple of element size");
+
+      const auto bufferSize = blendShape.deltas.mBlob.GetBufferSize();
+      std::vector<uint8_t> buffer(bufferSize);
+      if (ReadAccessor(blendShape.deltas, binFile, buffer.data()))
+      {
+        blendShape.deltas.mBlob.ApplyMinMax(bufferSize / sizeof(Vector3), reinterpret_cast<float*>(buffer.data()));
+        // Calculate the difference with the original mesh.
+        // Find the max distance to normalize the deltas.
+        const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
+
+        for (uint32_t index = 0u; index < numberOfVertices; ++index)
+        {
+          Vector3& delta = geometryBufferV3[geometryBufferIndex++];
+          delta = deltasBuffer[index];
+
+          maxDistance = std::max(maxDistance, delta.LengthSquared());
+        }
+      }
+    }
+
+    if (blendShape.normals.IsDefined())
+    {
+      DALI_ASSERT_ALWAYS(((blendShape.normals.mBlob.mLength % sizeof(Vector3) == 0u) ||
+        blendShape.normals.mBlob.mStride >= sizeof(Vector3)) &&
+        "Blend Shape normals buffer length not a multiple of element size");
+
+      const auto bufferSize = blendShape.normals.mBlob.GetBufferSize();
+      std::vector<uint8_t> buffer(bufferSize);
+      if (ReadAccessor(blendShape.normals, binFile, buffer.data()))
+      {
+        blendShape.normals.mBlob.ApplyMinMax(bufferSize / sizeof(Vector3), reinterpret_cast<float*>(buffer.data()));
+
+        // Calculate the difference with the original mesh, and translate to make all values positive.
+        const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
+
+        for (uint32_t index = 0u; index < numberOfVertices; ++index)
+        {
+          Vector3& delta = geometryBufferV3[geometryBufferIndex++];
+          delta = deltasBuffer[index];
+
+          delta.x *= 0.5f;
+          delta.y *= 0.5f;
+          delta.z *= 0.5f;
+
+          delta.x += 0.5f;
+          delta.y += 0.5f;
+          delta.z += 0.5f;
+        }
+      }
+    }
+
+    if (blendShape.tangents.IsDefined())
+    {
+      DALI_ASSERT_ALWAYS(((blendShape.tangents.mBlob.mLength % sizeof(Vector3) == 0u) ||
+        blendShape.tangents.mBlob.mStride >= sizeof(Vector3)) &&
+        "Blend Shape tangents buffer length not a multiple of element size");
+
+      const auto bufferSize = blendShape.tangents.mBlob.GetBufferSize();
+      std::vector<uint8_t> buffer(bufferSize);
+      if (ReadAccessor(blendShape.tangents, binFile, buffer.data()))
+      {
+        blendShape.tangents.mBlob.ApplyMinMax(bufferSize / sizeof(Vector3), reinterpret_cast<float*>(buffer.data()));
+
+        // Calculate the difference with the original mesh, and translate to make all values positive.
+        const Vector3* const deltasBuffer = reinterpret_cast<const Vector3* const>(buffer.data());
+
+        for (uint32_t index = 0u; index < numberOfVertices; ++index)
+        {
+          Vector3& delta = geometryBufferV3[geometryBufferIndex++];
+          delta = deltasBuffer[index];
+
+          delta.x *= 0.5f;
+          delta.y *= 0.5f;
+          delta.z *= 0.5f;
+
+          delta.x += 0.5f;
+          delta.y += 0.5f;
+          delta.z += 0.5f;
+        }
+      }
+    }
+  }
+
+  geometryBufferIndex = 0u;
+  for (const auto& blendShape : blendShapes)
+  {
+    // Normalize all the deltas and translate to a possitive value.
+    // Deltas are going to be passed to the shader in a color texture
+    // whose values that are less than zero are clamped.
+    if (blendShape.deltas.IsDefined())
+    {
+
+      const float normalizeFactor = (fabsf(maxDistance) < Math::MACHINE_EPSILON_1000) ? 1.f : (0.5f / sqrtf(maxDistance));
+
+      for (uint32_t index = 0u; index < numberOfVertices; ++index)
+      {
+        Vector3& delta = geometryBufferV3[geometryBufferIndex++];
+        delta.x = Clamp(((delta.x * normalizeFactor) + 0.5f), 0.f, 1.f);
+        delta.y = Clamp(((delta.y * normalizeFactor) + 0.5f), 0.f, 1.f);
+        delta.z = Clamp(((delta.z * normalizeFactor) + 0.5f), 0.f, 1.f);
+      }
+
+      // Calculate and store the unnormalize factor.
+      blendShapeUnnormalizeFactor = 1.f / normalizeFactor;
+    }
+
+    if (blendShape.normals.IsDefined())
+    {
+      geometryBufferIndex += numberOfVertices;
+    }
+
+    if (blendShape.tangents.IsDefined())
+    {
+      geometryBufferIndex += numberOfVertices;
+    }
+  }
+}
+
+}
+
+MeshDefinition::SparseBlob::SparseBlob(const Blob& indices, const Blob& values, uint32_t count)
+: mIndices{indices},
+  mValues{values},
+  mCount{count}
+{}
+
+MeshDefinition::Accessor::Accessor(const MeshDefinition::Blob& blob,
+  const MeshDefinition::SparseBlob& sparse)
+: mBlob{blob},
+  mSparse{(sparse.mIndices.IsDefined() && sparse.mValues.IsDefined()) ? new SparseBlob{sparse} : nullptr}
+{}
+
+void MeshDefinition::Blob::ApplyMinMax(const std::vector<float>& min, const std::vector<float>& max,
+  uint32_t count, float* values)
+{
+  DALI_ASSERT_DEBUG(max.size() == min.size() || max.size() * min.size() == 0);
+  const auto numComponents = std::max(min.size(), max.size());
+
+  using ClampFn = void(*)(const float*, const float*, uint32_t, float&);
+  ClampFn clampFn = min.empty() ?
+    (max.empty() ?
+      static_cast<ClampFn>(nullptr) :
+      [](const float* min, const float* max, uint32_t i, float& value) {
+        value = std::min(max[i], value);
+      }) :
+    (max.empty() ?
+      [](const float* min, const float* max, uint32_t i, float& value) {
+        value = std::max(min[i], value);
+      } :
+      [](const float* min, const float* max, uint32_t i, float& value) {
+        value = std::min(std::max(min[i], value), max[i]);
+      });
+
+  auto end = values + count * numComponents;
+  while (values != end)
+  {
+    auto nextElement = values + numComponents;
+    uint32_t i = 0;
+    while (values != nextElement)
+    {
+      clampFn(min.data(), max.data(), i, *values);
+      ++values;
+      ++i;
+    }
+  }
+}
+
+MeshDefinition::Blob::Blob(uint32_t offset, uint32_t length, uint16_t stride, uint16_t elementSizeHint, const std::vector<float>& min, const std::vector<float>& max)
+: mOffset(offset),
+  mLength(length),
+  mStride(stride),
+  mElementSizeHint(elementSizeHint),
+  mMin(min),
+  mMax(max)
+{}
+
+uint32_t MeshDefinition::Blob::GetBufferSize() const
+{
+  return IsConsecutive() ? mLength : (mLength * mElementSizeHint / mStride);
+}
+
+void MeshDefinition::Blob::ApplyMinMax(uint32_t count, float* values) const
+{
+  ApplyMinMax(mMin, mMax, count, values);
+}
+
+void MeshDefinition::RawData::Attrib::AttachBuffer(Geometry& g) const
+{
+  Property::Map attribMap;
+  attribMap[mName] = mType;
+  VertexBuffer attribBuffer = VertexBuffer::New(attribMap);
+  attribBuffer.SetData(mData.data(), mNumElements);
+
+  g.AddVertexBuffer(attribBuffer);
+}
+
+bool MeshDefinition::IsQuad() const
+{
+  return CaseInsensitiveStringCompare(QUAD, mUri);
+}
+
+bool MeshDefinition::IsSkinned() const
+{
+  return mJoints0.IsDefined() && mWeights0.IsDefined();
+}
+
+bool MeshDefinition::HasBlendShapes() const
+{
+  return !mBlendShapes.empty();
+}
+
+void MeshDefinition::RequestNormals()
+{
+  mNormals.mBlob.mLength = mPositions.mBlob.GetBufferSize();
+}
+
+void MeshDefinition::RequestTangents()
+{
+  mTangents.mBlob.mLength = mNormals.mBlob.GetBufferSize();
+}
+
+MeshDefinition::RawData
+  MeshDefinition::LoadRaw(const std::string& modelsPath) const
+{
+  RawData raw;
+  if (IsQuad())
+  {
+    return raw;
+  }
+
+  const std::string meshPath = modelsPath + mUri;
+  std::ifstream binFile(meshPath, std::ios::binary);
+  if (!binFile)
+  {
+    ExceptionFlinger(ASSERT_LOCATION) << "Failed to read geometry data from '" << meshPath << "'";
+  }
+
+  if (mIndices.IsDefined())
+  {
+    if (MaskMatch(mFlags, U32_INDICES))
+    {
+      DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(uint32_t) == 0) ||
+        mIndices.mBlob.mStride >= sizeof(uint32_t)) &&
+        "Index buffer length not a multiple of element size");
+      const auto indexCount = mIndices.mBlob.GetBufferSize() / sizeof(uint32_t);
+      raw.mIndices.resize(indexCount * 2);  // NOTE: we need space for uint32_ts initially.
+      if (!ReadAccessor(mIndices, binFile, reinterpret_cast<uint8_t*>(raw.mIndices.data())))
+      {
+        ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << meshPath << "'.";
+      }
+
+      auto u16s = raw.mIndices.data();
+      auto u32s = reinterpret_cast<uint32_t*>(raw.mIndices.data());
+      auto end = u32s + indexCount;
+      while (u32s != end)
+      {
+        *u16s = static_cast<uint16_t>(*u32s);
+        ++u16s;
+        ++u32s;
+      }
+
+      raw.mIndices.resize(indexCount);
+    }
+    else
+    {
+      DALI_ASSERT_ALWAYS(((mIndices.mBlob.mLength % sizeof(unsigned short) == 0) ||
+        mIndices.mBlob.mStride >= sizeof(unsigned short)) &&
+        "Index buffer length not a multiple of element size");
+      raw.mIndices.resize(mIndices.mBlob.mLength / sizeof(unsigned short));
+      if (!ReadAccessor(mIndices, binFile, reinterpret_cast<uint8_t*>(raw.mIndices.data())))
+      {
+        ExceptionFlinger(ASSERT_LOCATION) << "Failed to read indices from '" << meshPath << "'.";
+      }
+    }
+  }
+
+  std::vector<Vector3> positions;
+  if (mPositions.IsDefined())
+  {
+    DALI_ASSERT_ALWAYS(((mPositions.mBlob.mLength % sizeof(Vector3) == 0) ||
+      mPositions.mBlob.mStride >= sizeof(Vector3)) &&
+      "Position buffer length not a multiple of element size");
+    const auto bufferSize = mPositions.mBlob.GetBufferSize();
+    std::vector<uint8_t> buffer(bufferSize);
+    if (!ReadAccessor(mPositions, binFile, buffer.data()))
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << "Failed to read positions from '" << meshPath << "'.";
+    }
+
+    uint32_t numVector3 = bufferSize / sizeof(Vector3);
+    mPositions.mBlob.ApplyMinMax(numVector3, reinterpret_cast<float*>(buffer.data()));
+
+    if (HasBlendShapes())
+    {
+      positions.resize(numVector3);
+      std::copy(buffer.data(), buffer.data() + buffer.size(), reinterpret_cast<uint8_t*>(positions.data()));
+    }
+
+    raw.mAttribs.push_back({ "aPosition", Property::VECTOR3, numVector3, std::move(buffer) });
+  }
+
+  const auto isTriangles = mPrimitiveType == Geometry::TRIANGLES;
+  auto hasNormals = mNormals.IsDefined();
+  if (hasNormals)
+  {
+    DALI_ASSERT_ALWAYS(((mNormals.mBlob.mLength % sizeof(Vector3) == 0) ||
+      mNormals.mBlob.mStride >= sizeof(Vector3)) &&
+      "Normal buffer length not a multiple of element size");
+    const auto bufferSize = mNormals.mBlob.GetBufferSize();
+    std::vector<uint8_t> buffer(bufferSize);
+    if (!ReadAccessor(mNormals, binFile, buffer.data()))
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << "Failed to read normals from '" << meshPath << "'.";
+    }
+
+    mNormals.mBlob.ApplyMinMax(bufferSize / sizeof(Vector3), reinterpret_cast<float*>(buffer.data()));
+
+    raw.mAttribs.push_back({ "aNormal", Property::VECTOR3,
+      static_cast<uint32_t>(bufferSize / sizeof(Vector3)), std::move(buffer) });
+  }
+  else if (mNormals.mBlob.mLength != 0 && isTriangles)
+  {
+    DALI_ASSERT_DEBUG(mNormals.mBlob.mLength == mPositions.mBlob.GetBufferSize());
+    GenerateNormals(raw);
+    hasNormals = true;
+  }
+
+  const auto hasUvs = mTexCoords.IsDefined();
+  if (hasUvs)
+  {
+    DALI_ASSERT_ALWAYS(((mTexCoords.mBlob.mLength % sizeof(Vector2) == 0) ||
+      mTexCoords.mBlob.mStride >= sizeof(Vector2)) &&
+      "Normal buffer length not a multiple of element size");
+    const auto bufferSize = mTexCoords.mBlob.GetBufferSize();
+    std::vector<uint8_t> buffer(bufferSize);
+    if (!ReadAccessor(mTexCoords, binFile, buffer.data()))
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << "Failed to read uv-s from '" << meshPath << "'.";
+    }
+
+    const auto uvCount = bufferSize / sizeof(Vector2);
+    if (MaskMatch(mFlags, FLIP_UVS_VERTICAL))
+    {
+      auto uv = reinterpret_cast<Vector2*>(buffer.data());
+      auto uvEnd = uv + uvCount;
+      while (uv != uvEnd)
+      {
+        uv->y = 1.0f - uv->y;
+        ++uv;
+      }
+    }
+
+    mTexCoords.mBlob.ApplyMinMax(bufferSize / sizeof(Vector2), reinterpret_cast<float*>(buffer.data()));
+
+    raw.mAttribs.push_back({ "aTexCoord", Property::VECTOR2, static_cast<uint32_t>(uvCount),
+      std::move(buffer) });
+  }
+
+  if (mTangents.IsDefined())
+  {
+    DALI_ASSERT_ALWAYS(((mTangents.mBlob.mLength % sizeof(Vector3) == 0) ||
+      mTangents.mBlob.mStride >= sizeof(Vector3)) &&
+      "Tangents buffer length not a multiple of element size");
+    const auto bufferSize = mTangents.mBlob.GetBufferSize();
+    std::vector<uint8_t> buffer(bufferSize);
+    if (!ReadAccessor(mTangents, binFile, buffer.data()))
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << "Failed to read tangents from '" << meshPath << "'.";
+    }
+
+    mTangents.mBlob.ApplyMinMax(bufferSize / sizeof(Vector3), reinterpret_cast<float*>(buffer.data()));
+
+    raw.mAttribs.push_back({ "aTangent", Property::VECTOR3,
+      static_cast<uint32_t>(bufferSize / sizeof(Vector3)), std::move(buffer) });
+  }
+  else if (mTangents.mBlob.mLength != 0 && hasNormals && isTriangles)
+  {
+    DALI_ASSERT_DEBUG(mTangents.mBlob.mLength == mNormals.mBlob.GetBufferSize());
+    hasUvs ? GenerateTangentsWithUvs(raw) : GenerateTangents(raw);
+  }
+
+  if (IsSkinned())
+  {
+    if (MaskMatch(mFlags, U16_JOINT_IDS))
+    {
+      DALI_ASSERT_ALWAYS(((mJoints0.mBlob.mLength % sizeof(Uint16Vector4) == 0) ||
+        mJoints0.mBlob.mStride >= sizeof(Uint16Vector4)) &&
+        "Joints buffer length not a multiple of element size");
+      const auto inBufferSize = mJoints0.mBlob.GetBufferSize();
+      std::vector<uint8_t> buffer(inBufferSize * 2);
+      auto u16s = buffer.data() + inBufferSize;
+      if (!ReadAccessor(mJoints0, binFile, u16s))
+      {
+        ExceptionFlinger(ASSERT_LOCATION) << "Failed to read joints from '" << meshPath << "'.";
+      }
+
+      auto floats = reinterpret_cast<float*>(buffer.data());
+      auto end = u16s + inBufferSize;
+      while (u16s != end)
+      {
+        auto value = *reinterpret_cast<uint16_t*>(u16s);
+        *floats = static_cast<float>(value);
+
+        u16s += sizeof(uint16_t);
+        ++floats;
+      }
+      raw.mAttribs.push_back({ "aJoints", Property::VECTOR4,
+        static_cast<uint32_t>(buffer.size() / sizeof(Vector4)), std::move(buffer) });
+    }
+    else
+    {
+      DALI_ASSERT_ALWAYS(((mJoints0.mBlob.mLength % sizeof(Vector4) == 0) ||
+        mJoints0.mBlob.mStride >= sizeof(Vector4)) &&
+        "Joints buffer length not a multiple of element size");
+      const auto bufferSize = mJoints0.mBlob.GetBufferSize();
+      std::vector<uint8_t> buffer(bufferSize);
+      if (!ReadAccessor(mJoints0, binFile, buffer.data()))
+      {
+        ExceptionFlinger(ASSERT_LOCATION) << "Failed to read joints from '" << meshPath << "'.";
+      }
+
+      raw.mAttribs.push_back({ "aJoints", Property::VECTOR4,
+        static_cast<uint32_t>(bufferSize / sizeof(Vector4)), std::move(buffer) });
+    }
+
+    DALI_ASSERT_ALWAYS(((mWeights0.mBlob.mLength % sizeof(Vector4) == 0) ||
+      mWeights0.mBlob.mStride >= sizeof(Vector4)) &&
+      "Weights buffer length not a multiple of element size");
+    const auto bufferSize = mWeights0.mBlob.GetBufferSize();
+    std::vector<uint8_t> buffer(bufferSize);
+    if (!ReadAccessor(mWeights0, binFile, buffer.data()))
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << "Failed to read weights from '" << meshPath << "'.";
+    }
+
+    raw.mAttribs.push_back({ "aWeights", Property::VECTOR4,
+      static_cast<uint32_t>(bufferSize / sizeof(Vector4)), std::move(buffer) });
+  }
+
+  // Calculate the Blob for the blend shapes.
+  Blob blendShapesBlob;
+  blendShapesBlob.mOffset = std::numeric_limits<unsigned int>::max();
+  blendShapesBlob.mLength = 0u;
+
+  for (const auto& blendShape : mBlendShapes)
+  {
+    for (auto i : { &blendShape.deltas, &blendShape.normals, &blendShape.tangents })
+    {
+      if (i->IsDefined())
+      {
+        blendShapesBlob.mOffset = std::min(blendShapesBlob.mOffset, i->mBlob.mOffset);
+        blendShapesBlob.mLength += i->mBlob.mLength;
+      }
+    }
+  }
+
+  if (HasBlendShapes())
+  {
+    const uint32_t numberOfVertices = mPositions.mBlob.mLength / sizeof(Vector3);
+
+    // Calculate the size of one buffer inside the texture.
+    raw.mBlendShapeBufferOffset = numberOfVertices;
+
+    bool calculateGltf2BlendShapes = false;
+    uint32_t textureWidth = 0u;
+    uint32_t textureHeight = 0u;
+
+    if (!mBlendShapeHeader.IsDefined())
+    {
+      CalculateTextureSize(blendShapesBlob.mLength / sizeof(Vector3), textureWidth, textureHeight);
+      calculateGltf2BlendShapes = true;
+    }
+    else
+    {
+      uint16_t header[2u];
+      ReadBlob(mBlendShapeHeader, binFile, reinterpret_cast<uint8_t*>(header));
+      textureWidth = header[0u];
+      textureHeight = header[1u];
+    }
+
+    const uint32_t numberOfBlendShapes = mBlendShapes.size();
+    raw.mBlendShapeUnnormalizeFactor.Resize(numberOfBlendShapes);
+
+    Devel::PixelBuffer geometryPixelBuffer = Devel::PixelBuffer::New(textureWidth, textureHeight, Pixel::RGB32F);
+    uint8_t* geometryBuffer = geometryPixelBuffer.GetBuffer();
+
+    if (calculateGltf2BlendShapes)
+    {
+      CalculateGltf2BlendShapes(geometryBuffer, binFile, mBlendShapes, numberOfVertices, raw.mBlendShapeUnnormalizeFactor[0u]);
+    }
+    else
+    {
+      Blob unnormalizeFactorBlob;
+      unnormalizeFactorBlob.mLength = sizeof(float) * ((BlendShapes::Version::VERSION_2_0 == mBlendShapeVersion) ? 1u : numberOfBlendShapes);
+
+      if (blendShapesBlob.IsDefined())
+      {
+        if (ReadBlob(blendShapesBlob, binFile, geometryBuffer))
+        {
+          unnormalizeFactorBlob.mOffset = blendShapesBlob.mOffset + blendShapesBlob.mLength;
+        }
+      }
+
+      // Read the unnormalize factors.
+      if (unnormalizeFactorBlob.IsDefined())
+      {
+        ReadBlob(unnormalizeFactorBlob, binFile, reinterpret_cast<uint8_t*>(&raw.mBlendShapeUnnormalizeFactor[0u]));
+      }
+    }
+    raw.mBlendShapeData = Devel::PixelBuffer::Convert(geometryPixelBuffer);
+  }
+
+  return raw;
+}
+
+MeshGeometry MeshDefinition::Load(RawData&& raw) const
+{
+  MeshGeometry meshGeometry;
+  meshGeometry.geometry = Geometry::New();
+  meshGeometry.geometry.SetType(mPrimitiveType);
+
+  if (IsQuad())  // TODO: do this in raw data; provide MakeTexturedQuadGeometry() that only creates buffers.
+  {
+    auto options = MaskMatch(mFlags, FLIP_UVS_VERTICAL) ? TexturedQuadOptions::FLIP_VERTICAL : 0;
+    meshGeometry.geometry = MakeTexturedQuadGeometry(options);
+  }
+  else
+  {
+    if (!raw.mIndices.empty())
+    {
+      meshGeometry.geometry.SetIndexBuffer(raw.mIndices.data(), raw.mIndices.size());
+    }
+
+    for (auto& a : raw.mAttribs)
+    {
+      a.AttachBuffer(meshGeometry.geometry);
+    }
+
+    if (HasBlendShapes())
+    {
+      meshGeometry.blendShapeBufferOffset = raw.mBlendShapeBufferOffset;
+      meshGeometry.blendShapeUnnormalizeFactor = std::move(raw.mBlendShapeUnnormalizeFactor);
+
+      meshGeometry.blendShapeGeometry = Texture::New(  TextureType::TEXTURE_2D,
+                              raw.mBlendShapeData.GetPixelFormat(),
+                              raw.mBlendShapeData.GetWidth(),
+                              raw.mBlendShapeData.GetHeight());
+      meshGeometry.blendShapeGeometry.Upload(raw.mBlendShapeData);
+    }
+  }
+
+  return meshGeometry;
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/mesh-definition.h b/dali-scene-loader/public-api/mesh-definition.h
new file mode 100644 (file)
index 0000000..057aad7
--- /dev/null
@@ -0,0 +1,259 @@
+#ifndef DALI_SCENE_LOADER_MESH_DEFINITION_H
+#define DALI_SCENE_LOADER_MESH_DEFINITION_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+#include "dali-scene-loader/public-api/mesh-geometry.h"
+#include "dali-scene-loader/public-api/blend-shape-details.h"
+#include "dali-scene-loader/public-api/utils.h"
+#include "dali-scene-loader/public-api/index.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/common/vector-wrapper.h"
+#include <memory>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/**
+ * @brief Defines a mesh with its attributes, the primitive type to render it as,
+ *  and the file to load it from with the offset and length information for the
+ *  individual attribute buffers.
+ */
+struct DALI_SCENE_LOADER_API MeshDefinition
+{
+  using Vector = std::vector<std::pair<MeshDefinition, MeshGeometry>>;
+
+  enum : uint32_t { INVALID = std::numeric_limits<uint32_t>::max() };
+
+  enum Flags : uint16_t
+  {
+    FLIP_UVS_VERTICAL = NthBit(0),
+    U32_INDICES = NthBit(1),  // default is unsigned short
+    U16_JOINT_IDS = NthBit(2), // default is floats
+  };
+
+  enum Attributes
+  {
+    INDICES = NthBit(0),
+    POSITIONS = NthBit(1),
+    NORMALS = NthBit(2),
+    TEX_COORDS = NthBit(3),
+    TANGENTS = NthBit(4),
+    LEGACY_BITANGENTS = NthBit(5),  // these are ignored; we're calculating them in the (PBR) shader.
+    JOINTS_0 = NthBit(6),
+    WEIGHTS_0 = NthBit(7),
+  };
+
+  /**
+   * @brief Describes raw data in terms of its position and size in a buffer.
+   *  All units in bytes.
+   */
+  struct Blob
+  {
+    uint32_t mOffset = INVALID;  // the default means that the blob is undefined.
+    uint32_t mLength = 0;  // if the blob is undefined, its data may still be generated. This is enabled by setting length to some non-0 value. Refer to MeshDefinition for details.
+    uint16_t mStride = 0;  // ignore if 0
+    uint16_t mElementSizeHint = 0;  // ignore if 0 or stride == 0
+    std::vector<float> mMin;
+    std::vector<float> mMax;
+
+    static void ApplyMinMax(const std::vector<float>& min, const std::vector<float>& max, uint32_t count, float* values);
+
+    Blob() = default;
+
+    Blob(uint32_t offset, uint32_t length, uint16_t stride = 0, uint16_t elementSizeHint = 0,
+      const std::vector<float>& min = {}, const std::vector<float>& max = {});
+
+    /**
+     * @brief Calculates the size of a tightly-packed buffer for the elements from the blob.
+     */
+    uint32_t GetBufferSize() const;
+
+    /**
+     * @brief Convenience method to tell whether a Blob has meaningful data.
+     */
+    bool IsDefined() const
+    {
+      return mOffset != INVALID;
+    }
+
+    /**
+     * @brief Convenience method to tell whether the elements stored in the blob follow each
+     *  other tightly. The opposite would be interleaving.
+     */
+    bool IsConsecutive() const
+    {
+      return mStride == 0 || mStride == mElementSizeHint;
+    }
+
+    /**
+     * @brief Applies the min / max values, if they're defined.
+     */
+    void ApplyMinMax(uint32_t count, float* values) const;
+  };
+
+  /**
+   * @brief A sparse blob describes a change in a reference Blob.
+   * @p indices describe what positions of the reference Blob change and
+   * @p values describe the new values.
+   */
+  struct SparseBlob
+  {
+    SparseBlob() = default;
+
+    SparseBlob(const Blob& indices, const Blob& values, uint32_t count);
+
+    Blob mIndices;
+    Blob mValues;
+    uint32_t mCount = 0u;
+  };
+
+  struct Accessor
+  {
+    Blob mBlob;
+    std::unique_ptr<SparseBlob> mSparse;
+
+    Accessor() = default;
+
+    Accessor(const Accessor&) = delete;
+    Accessor& operator=(const Accessor&) = delete;
+
+    Accessor(Accessor&&) = default;
+    Accessor& operator=(Accessor&&) = default;
+
+    Accessor(const MeshDefinition::Blob& blob,
+      const MeshDefinition::SparseBlob& sparse);
+
+    bool IsDefined() const
+    {
+      return mBlob.IsDefined() || (mSparse && (mSparse->mIndices.IsDefined() && mSparse->mValues.IsDefined()));
+    }
+  };
+
+  /**
+   * @brief Stores a blend shape.
+   */
+  struct BlendShape
+  {
+    std::string name;
+    Accessor deltas;
+    Accessor normals;
+    Accessor tangents;
+    float weight = 0.f;
+  };
+
+  struct RawData
+  {
+    struct Attrib
+    {
+      std::string mName;
+      Property::Type mType;
+      uint32_t mNumElements;
+      std::vector<uint8_t> mData;
+
+      void AttachBuffer(Geometry& g) const;
+    };
+
+    std::vector<uint16_t> mIndices;
+    std::vector<Attrib> mAttribs;
+
+    unsigned int mBlendShapeBufferOffset;
+    Dali::Vector<float> mBlendShapeUnnormalizeFactor;
+    PixelData mBlendShapeData;
+  };
+
+  MeshDefinition() = default;
+
+  MeshDefinition(const MeshDefinition&) = delete;
+  MeshDefinition& operator=(const MeshDefinition&) = delete;
+
+  MeshDefinition(MeshDefinition&&) = default;
+  MeshDefinition& operator=(MeshDefinition&&) = default;
+
+  /**
+   * @brief Determines whether the mesh definition is that of a quad.
+   */
+  bool IsQuad() const;
+
+  /**
+   * @brief Determines whether the mesh is used for skeletal animation.
+   */
+  bool IsSkinned() const;
+
+  /**
+   * @brief Whether the mesh has blend shapes.
+   */
+  bool HasBlendShapes() const;
+
+  /**
+   * @brief Requests normals to be generated.
+   * @note Generation happens in LoadRaw().
+   * @note Must have Vector3 positions defined.
+   */
+  void RequestNormals();
+
+  /**
+   * @brief Requests tangents to be generated.
+   * @note Generation happens in LoadRaw().
+   * @note Must have Vector3 normals defined.
+   */
+  void RequestTangents();
+
+  /**
+   * @brief Loads raw geometry data, which includes index (optional) and
+   *  attribute buffers, as well as blend shape data. This is then returned.
+   * @note This can be done on any thread.
+   */
+  RawData LoadRaw(const std::string& modelsPath) const;
+
+  /**
+   * @brief Creates a MeshGeometry based firstly on the value of the uri member:
+   *  if it is "quad", a textured quad is created; otherwise it uses the
+   *  attribute (and index) buffers and blend shape information (if available)
+   *  from @a raw.
+   *  If mFlipVertical was set, the UVs are flipped in Y, i.e. v = 1.0 - v.
+   */
+  MeshGeometry Load(RawData&& raw) const;
+
+public: // DATA
+  uint32_t mFlags = 0x0;
+  Geometry::Type mPrimitiveType = Geometry::TRIANGLES;
+  std::string mUri;
+  Accessor mIndices;
+  Accessor mPositions;
+  Accessor mNormals;  // data can be generated based on positions
+  Accessor mTexCoords;
+  Accessor mTangents;  // data can be generated based on normals and texCoords (the latter isn't mandatory; the results will be better if available)
+  Accessor mJoints0;
+  Accessor mWeights0;
+
+  Blob mBlendShapeHeader;
+  std::vector<BlendShape> mBlendShapes;
+  BlendShapes::Version mBlendShapeVersion = BlendShapes::Version::INVALID;
+
+  Index mSkeletonIdx = INVALID_INDEX;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_MESH_DEFINITION_H
diff --git a/dali-scene-loader/public-api/mesh-geometry.h b/dali-scene-loader/public-api/mesh-geometry.h
new file mode 100644 (file)
index 0000000..bf282da
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef DALI_SCENE_LOADER_MESH_GEOMETRY_H
+#define DALI_SCENE_LOADER_MESH_GEOMETRY_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/rendering/geometry.h"
+#include "dali/public-api/rendering/texture.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+struct DALI_SCENE_LOADER_API MeshGeometry
+{
+  Geometry geometry;                         ///< The array of vertices.
+  Texture blendShapeGeometry;                ///< The array of vertices of the different blend shapes encoded inside a texture with power of two dimensions.
+  Vector<float> blendShapeUnnormalizeFactor; ///< Factor used to unnormalize the geometry of the blend shape.
+  unsigned int blendShapeBufferOffset;             ///< Offset used to calculate the start of each blend shape.
+};
+
+}
+}
+
+#endif // DALI_SCENE_LOADER_MESH_GEOMETRY_H
diff --git a/dali-scene-loader/public-api/node-definition.cpp b/dali-scene-loader/public-api/node-definition.cpp
new file mode 100644 (file)
index 0000000..a277371
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL
+#include "dali-scene-loader/public-api/node-definition.h"
+#include "dali-scene-loader/public-api/renderer-state.h"
+#include "dali-scene-loader/public-api/utils.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+void NodeDefinition::Renderable::RegisterResources(IResourceReceiver& receiver) const
+{
+  receiver.Register(ResourceType::Shader, mShaderIdx);
+}
+
+void NodeDefinition::Renderable::ReflectResources(IResourceReflector& reflector)
+{
+  reflector.Reflect(ResourceType::Shader, mShaderIdx);
+}
+
+void NodeDefinition::Renderable::OnCreate(const NodeDefinition& node, CreateParams& params,
+  Actor& actor) const
+{
+  DALI_ASSERT_DEBUG(mShaderIdx != INVALID_INDEX);
+  auto& resources = params.mResources;
+  Shader shader = resources.mShaders[mShaderIdx].second;
+
+  static Geometry defaultGeometry = Geometry::New();
+  Renderer renderer = Renderer::New(defaultGeometry, shader);
+
+  RendererState::Apply(resources.mShaders[mShaderIdx].first.mRendererState, renderer);
+
+  actor.AddRenderer(renderer);
+}
+
+const std::string NodeDefinition::ORIGINAL_MATRIX_PROPERTY_NAME = "originalMatrix";
+
+Actor NodeDefinition::CreateActor(CreateParams& params) const
+{
+  Actor actor = Actor::New();
+  SetActorCentered(actor);
+
+  actor.SetProperty(Actor::Property::NAME, mName);
+  actor.SetProperty(Actor::Property::POSITION, mPosition);
+  actor.SetProperty(Actor::Property::ORIENTATION, mOrientation);
+  actor.SetProperty(Actor::Property::SCALE, mScale);
+  actor.SetProperty(Actor::Property::SIZE, mSize);
+  actor.SetProperty(Actor::Property::VISIBLE, mIsVisible);
+
+  actor.RegisterProperty(ORIGINAL_MATRIX_PROPERTY_NAME, GetLocalSpace(), Property::AccessMode::READ_ONLY);
+
+  if (mRenderable)
+  {
+    mRenderable->OnCreate(*this, params, actor);
+  }
+
+  for (auto& e : mExtras)
+  {
+    actor.RegisterProperty(e.mKey, e.mValue);
+  }
+
+  for (auto& c : mConstraints)
+  {
+    params.mConstrainables.push_back(ConstraintRequest{ &c, actor });
+  }
+
+  return actor;
+}
+
+Matrix NodeDefinition::GetLocalSpace() const
+{
+  Matrix localSpace{ false };
+  localSpace.SetTransformComponents(mScale, mOrientation, mPosition);
+  return localSpace;
+}
+
+void ModelNode::RegisterResources(IResourceReceiver& receiver) const
+{
+  Renderable::RegisterResources(receiver);
+  receiver.Register(ResourceType::Mesh, mMeshIdx);
+  receiver.Register(ResourceType::Material, mMaterialIdx);
+}
+
+void ModelNode::ReflectResources(IResourceReflector& reflector)
+{
+  Renderable::ReflectResources(reflector);
+  reflector.Reflect(ResourceType::Mesh, mMeshIdx);
+  reflector.Reflect(ResourceType::Material, mMaterialIdx);
+}
+
+void ModelNode::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params, Actor& actor) const
+{
+  DALI_ASSERT_DEBUG(mMeshIdx != INVALID_INDEX);
+  Renderable::OnCreate(node, params, actor);
+
+  auto& resources = params.mResources;
+  auto& mesh = resources.mMeshes[mMeshIdx];
+
+  auto renderer = actor.GetRendererAt(0);
+  Geometry geometry = mesh.second.geometry;
+  renderer.SetGeometry(geometry);
+
+  auto shader = renderer.GetShader();
+
+  if (mesh.first.IsSkinned())
+  {
+    params.mSkinnables.push_back(SkinningShaderConfigurationRequest{ mesh.first.mSkeletonIdx, shader });
+  }
+
+  if (mesh.first.HasBlendShapes())
+  {
+    params.mBlendshapeRequests.push_back(BlendshapeShaderConfigurationRequest{ node.mName, mMeshIdx, shader });
+  }
+
+  TextureSet textures = resources.mMaterials[mMaterialIdx].second;
+
+  // Set the blend shape texture.
+  if (mesh.second.blendShapeGeometry)
+  {
+    TextureSet newTextureSet = TextureSet::New();
+    newTextureSet.SetTexture(0u, mesh.second.blendShapeGeometry);
+
+    const unsigned int numberOfTextures = textures.GetTextureCount();
+    for (unsigned int index = 0u; index < numberOfTextures; ++index)
+    {
+      const unsigned int newIndex = index + 1u;
+      newTextureSet.SetTexture(newIndex, textures.GetTexture(index));
+      newTextureSet.SetSampler(newIndex, textures.GetSampler(index));
+    }
+
+    textures = newTextureSet;
+  }
+
+  renderer.SetTextures(textures);
+
+  actor.SetProperty(Actor::Property::COLOR, mColor);
+
+  auto& matDef = resources.mMaterials[mMaterialIdx].first;
+  actor.RegisterProperty("uMetallicFactor", matDef.mMetallic);
+  actor.RegisterProperty("uRoughnessFactor", matDef.mRoughness);
+
+  Index envIdx = matDef.mEnvironmentIdx;
+  actor.RegisterProperty("uIblIntensity", resources.mEnvironmentMaps[envIdx].first.mIblIntensity);
+
+  const auto alphaCutoff = matDef.GetAlphaCutoff();
+  if (alphaCutoff > 0.f)
+  {
+    actor.RegisterProperty("uAlphaThreshold", alphaCutoff);
+  }
+}
+
+void ArcNode::OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params,
+  Actor& actor) const
+{
+  ModelNode::OnCreate(node, params, actor);
+
+  actor.RegisterProperty("antiAliasing", mAntiAliasing ? 1 : 0);
+  actor.RegisterProperty("arcCaps", mArcCaps);
+  actor.RegisterProperty("radius", mRadius);
+
+  const float startAngleRadians = mStartAngleDegrees * Math::PI_OVER_180;
+  Vector2 startPolar{ std::cos(startAngleRadians), std::sin(startAngleRadians) };
+  actor.RegisterProperty("startAngle", startPolar);
+
+  const float endAngleRadians = mEndAngleDegrees * Math::PI_OVER_180;
+  Vector2 endPolar{ std::cos(endAngleRadians), std::sin(endAngleRadians) };
+  actor.RegisterProperty("endAngle", endPolar);
+}
+
+void ArcNode::GetEndVectorWithDiffAngle(float startAngle, float diffAngle, Vector2& endVector)
+{
+  float endAngle = 0.f;
+
+  if (diffAngle <= 0.001f)
+  {
+    //0.001 is used to ensure is empty arc when startAngle = endAngle + 360 * N
+    endAngle = startAngle + 0.001f;
+  }
+  else if (diffAngle >= 360.f)
+  {
+    endAngle = diffAngle + 359.99f;
+  }
+  else
+  {
+    endAngle = startAngle + 360.0f + diffAngle;
+  }
+  endVector.x = cosf(endAngle * Math::PI_OVER_180);
+  endVector.y = sinf(endAngle * Math::PI_OVER_180);
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/node-definition.h b/dali-scene-loader/public-api/node-definition.h
new file mode 100644 (file)
index 0000000..3dec499
--- /dev/null
@@ -0,0 +1,278 @@
+#ifndef DALI_SCENE_LOADER_NODE_DEFINITION_H_
+#define DALI_SCENE_LOADER_NODE_DEFINITION_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/customization.h"
+#include "dali-scene-loader/public-api/matrix-stack.h"
+#include "dali-scene-loader/public-api/resource-bundle.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/math/quaternion.h"
+#include "dali/public-api/math/matrix.h"
+#include "dali/public-api/math/vector4.h"
+#include "dali/public-api/actors/actor.h"
+#include <string>
+#include <memory>
+#include <functional>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+class ViewProjection;
+
+/**
+ * @brief Interface to report (const) resource ids to.
+ */
+class DALI_SCENE_LOADER_API IResourceReceiver
+{
+public:
+  virtual ~IResourceReceiver() = default;
+
+  virtual void Register(ResourceType::Value type, Index id) = 0;
+};
+
+/**
+ * @brief Interface to report modifiable resource ids to.
+ * @note These are supposed to be transient. Obviously, the references collected
+ *  this way must not outlive the objects that they came from.
+ */
+class DALI_SCENE_LOADER_API IResourceReflector
+{
+public:
+  virtual ~IResourceReflector() = default;
+
+  virtual void Reflect(ResourceType::Value type, Index& id) = 0;
+};
+
+/**
+ * @brief Intermediate representation for a constraint that shall be
+ *  set up after the Actors were created. The target of the constraint
+ *  is the node definition that carries it.
+ */
+struct DALI_SCENE_LOADER_API ConstraintDefinition
+{
+  std::string mProperty;  ///< name of the property to constrain.
+  Index mSourceIdx;  ///< index of the node to serve as the source of the constraint.
+
+  bool operator<(const ConstraintDefinition& other) const
+  {
+    return mProperty < other.mProperty;
+  }
+
+  bool operator==(const ConstraintDefinition& other) const
+  {
+    return mSourceIdx == other.mSourceIdx && mProperty == other.mProperty;
+  }
+
+  bool operator!=(const ConstraintDefinition& other) const
+  {
+    return !operator==(other);
+  }
+};
+
+struct DALI_SCENE_LOADER_API Transforms
+{
+  MatrixStack modelStack;
+  const ViewProjection& viewProjection;
+};
+
+/**
+ * @brief Information about a skeleton and the shader that needs to be configured with it.
+ * @note Multiple skeletons shalt not share the same shader.
+ */
+struct DALI_SCENE_LOADER_API SkinningShaderConfigurationRequest
+{
+  Index mSkeletonIdx;
+  Shader mShader;
+
+  bool operator<(const SkinningShaderConfigurationRequest& other) const
+  {
+    return mShader < other.mShader;
+  }
+};
+
+/**
+ * @brief Needed to configure blend shape properties.
+ */
+struct DALI_SCENE_LOADER_API BlendshapeShaderConfigurationRequest
+{
+  std::string mNodeName;
+  Index mMeshIdx;
+  Shader mShader;
+
+  bool operator<(const BlendshapeShaderConfigurationRequest& other) const
+  {
+    return mShader < other.mShader;
+  }
+};
+
+/**
+ * @brief Request for creating a constraint, output from NodeDefinition::OnCreate.
+ */
+struct DALI_SCENE_LOADER_API ConstraintRequest
+{
+  const ConstraintDefinition* const mConstraint;  ///< Definition of the constraint to create.
+  Actor mTarget;  ///< Target of the constraint.
+};
+
+/**
+ * @brief Defines a node, consisting of a name, a transform, a size, a list of child nodes,
+ *  and slots for customization and rendering logic, which are mutually exclusive in the
+ *  current implementation.
+ */
+struct DALI_SCENE_LOADER_API NodeDefinition
+{
+public:  // TYPES
+  using Vector = std::vector<NodeDefinition>;
+
+  struct CreateParams
+  {
+  public: // input
+    const ResourceBundle& mResources;
+    Transforms& mXforms;
+
+  public: // output
+    std::vector<ConstraintRequest> mConstrainables;
+    std::vector<SkinningShaderConfigurationRequest> mSkinnables;
+    std::vector<BlendshapeShaderConfigurationRequest> mBlendshapeRequests;
+  };
+
+  class DALI_SCENE_LOADER_API Renderable
+  {
+  public: // DATA
+    Index mShaderIdx = INVALID_INDEX;
+
+  public: // METHODS
+    virtual ~Renderable() = default;
+
+    virtual void RegisterResources(IResourceReceiver& receiver) const;
+    virtual void ReflectResources(IResourceReflector& reflector);
+    virtual void OnCreate(const NodeDefinition& node, CreateParams& params, Actor& actor) const;
+  };
+
+  struct CustomizationDefinition
+  {
+    std::string mTag;
+
+    Index GetChildId(const Customization::Choices& choices, const NodeDefinition& node)
+    {
+      auto choice = choices.Get(mTag);
+      return std::min(choice != Customization::NONE ? choice : 0,
+        static_cast<Index>(node.mChildren.size() - 1));
+    }
+  };
+
+  class IVisitor
+  {
+  public:
+    virtual void Start(NodeDefinition& n) = 0;
+    virtual void Finish(NodeDefinition& n) = 0;
+
+  protected:
+    ~IVisitor() = default; // deliberately non-virtual these are transient objects and we don't want to pay for the vtable.
+  };
+
+  class IConstVisitor
+  {
+  public:
+    virtual void Start(const NodeDefinition& n) = 0;
+    virtual void Finish(const NodeDefinition& n) = 0;
+
+  protected:
+    ~IConstVisitor() = default; // deliberately non-virtual these are transient objects and we don't want to pay for the vtable.
+  };
+
+  struct Extra
+  {
+    std::string mKey;
+    Property::Value mValue;
+
+    bool operator<(const Extra& other) const
+    {
+      return mKey < other.mKey;
+    }
+  };
+
+public:  // METHODS
+  /**
+   * @brief Creates a DALi Actor from this definition only.
+   * @note Not recursive.
+   */
+  Actor CreateActor(CreateParams& params) const;
+
+  Matrix GetLocalSpace() const;
+
+public: // DATA
+  static const std::string ORIGINAL_MATRIX_PROPERTY_NAME;
+
+  std::string mName;
+
+  Vector3 mPosition = Vector3::ZERO;
+  Quaternion mOrientation = Quaternion::IDENTITY;
+  Vector3 mScale = Vector3::ONE;
+  Vector3 mSize = Vector3::ONE;
+
+  bool mIsVisible = true;
+
+  std::unique_ptr<Renderable> mRenderable;
+  std::unique_ptr<CustomizationDefinition> mCustomization;
+  std::vector<Extra> mExtras;
+  std::vector<ConstraintDefinition> mConstraints;
+
+  std::vector<Index> mChildren;
+  Index mParentIdx = INVALID_INDEX;
+};
+
+class DALI_SCENE_LOADER_API ModelNode : public NodeDefinition::Renderable
+{
+public: // DATA
+  Vector4 mColor = Color::WHITE;
+  Index mMeshIdx = INVALID_INDEX;
+  Index mMaterialIdx = INVALID_INDEX;
+
+public: // METHODS
+  void RegisterResources(IResourceReceiver& receiver) const override;
+  void ReflectResources(IResourceReflector& reflector) override;
+  void OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params, Actor& actor) const override;
+};
+
+/**
+ * @brief Parameters for an Arc node.
+ */
+class DALI_SCENE_LOADER_API ArcNode : public ModelNode
+{
+public: // DATA
+  bool mAntiAliasing = true;
+  int mArcCaps = 0;
+  float mStartAngleDegrees = .0f;
+  float mEndAngleDegrees = .0f;
+  float mRadius = .0f;
+
+public: // METHODS
+  static void GetEndVectorWithDiffAngle(float startAngle, float endAngle, Vector2& endVector);
+
+  void OnCreate(const NodeDefinition& node, NodeDefinition::CreateParams& params, Actor& actor) const override;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_NODE_DEFINITION_H_
diff --git a/dali-scene-loader/public-api/parse-renderer-state.cpp b/dali-scene-loader/public-api/parse-renderer-state.cpp
new file mode 100644 (file)
index 0000000..c60b1d4
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "dali-scene-loader/public-api/parse-renderer-state.h"
+#include "dali/devel-api/common/map-wrapper.h"
+#include <cstring>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+namespace RendererState
+{
+namespace
+{
+
+std::map<std::string_view, Type> COMPARISONS{
+#define DECL_COMPARISON(x) { #x, Comparison::x }
+  DECL_COMPARISON(NEVER),
+  DECL_COMPARISON(ALWAYS),
+  DECL_COMPARISON(LESS),
+  DECL_COMPARISON(GREATER),
+  DECL_COMPARISON(EQUAL),
+  DECL_COMPARISON(NOT_EQUAL),
+  DECL_COMPARISON(LESS_EQUAL),
+  DECL_COMPARISON(GREATER_EQUAL),
+#undef DECL_COMPARISON
+};
+
+Type InterpretComparison(const std::string_view& str)
+{
+  Type value = 0x0;
+  auto iFind = COMPARISONS.find(str);
+  if (iFind != COMPARISONS.end())
+  {
+    value = iFind->second;
+  }
+  return value;
+}
+
+std::map<std::string_view, Type> BLEND_FACTORS{
+#define DECL_BLEND_FACTOR(x) { #x, SceneLoader::BlendFactor::x }
+  DECL_BLEND_FACTOR(ZERO),
+  DECL_BLEND_FACTOR(ONE),
+  DECL_BLEND_FACTOR(SRC_COLOR),
+  DECL_BLEND_FACTOR(ONE_MINUS_SRC_COLOR),
+  DECL_BLEND_FACTOR(SRC_ALPHA),
+  DECL_BLEND_FACTOR(ONE_MINUS_SRC_ALPHA),
+  DECL_BLEND_FACTOR(DST_ALPHA),
+  DECL_BLEND_FACTOR(ONE_MINUS_DST_ALPHA),
+  DECL_BLEND_FACTOR(DST_COLOR),
+  DECL_BLEND_FACTOR(ONE_MINUS_DST_COLOR),
+  DECL_BLEND_FACTOR(SRC_ALPHA_SATURATE),
+  DECL_BLEND_FACTOR(CONSTANT_COLOR),
+  DECL_BLEND_FACTOR(ONE_MINUS_CONSTANT_COLOR),
+  DECL_BLEND_FACTOR(CONSTANT_ALPHA),
+  DECL_BLEND_FACTOR(ONE_MINUS_CONSTANT_ALPHA),
+#undef DECL_BLEND_FACTOR
+};
+
+Type InterpretBlendFactor(const std::string_view& str, uint8_t item)
+{
+  Type value = 0x0;
+  auto iFind = BLEND_FACTORS.find(str);
+  if (iFind != BLEND_FACTORS.end())
+  {
+    value = iFind->second << (BLEND_FACTOR_BASE_SHIFT + BLEND_FACTOR_ITEM_BITS * item);
+  }
+  return value;
+}
+
+std::map<std::string_view, Type> BUFFER_MODES{
+#define DECL_BUFFER_MODE(x) { #x, BufferMode::x }
+  DECL_BUFFER_MODE(NONE),
+  DECL_BUFFER_MODE(AUTO),
+  DECL_BUFFER_MODE(COLOR),
+  DECL_BUFFER_MODE(STENCIL),
+  DECL_BUFFER_MODE(COLOR_STENCIL),
+};
+
+Type InterpretBufferMode(const std::string_view& str)
+{
+  Type value = 0x0;
+  auto iFind = BUFFER_MODES.find(str);
+  if (iFind != BUFFER_MODES.end())
+  {
+    value = iFind->second << BUFFER_MODE_SHIFT;
+  }
+  return value;
+}
+
+std::map<std::string_view, Type(*)(const std::string_view&)> RENDERER_STATE_PROCESSORS{
+  { "DEPTH_WRITE", [](const std::string_view&) -> Type { return DEPTH_WRITE; } },
+  { "DEPTH_TEST", [](const std::string_view&) -> Type { return DEPTH_TEST; } },
+  { "CULL_FRONT", [](const std::string_view&) -> Type { return CULL_FRONT; } },
+  { "CULL_BACK", [](const std::string_view&) -> Type { return CULL_BACK; } },
+  { "ALPHA_BLEND", [](const std::string_view&) -> Type { return ALPHA_BLEND; } },
+  { "DEPTH_FUNC", [](const std::string_view& arg) -> Type {
+    Type value = (arg[0] == ':') ?
+      (InterpretComparison(std::string_view(arg.data() + 1, arg.size() - 1)) << DEPTH_FUNCTION_SHIFT) : 0x0;
+    return value;
+  } },
+  { "BLEND_SRC_RGB", [](const std::string_view& arg) -> Type {
+    Type value = (arg[0] == ':') ? InterpretBlendFactor(std::string_view(arg.data() + 1, arg.size() - 1), 0) : 0x0;
+    return value;
+  }},
+  { "BLEND_DST_RGB", [](const std::string_view& arg) -> Type {
+    Type value = (arg[0] == ':') ? InterpretBlendFactor(std::string_view(arg.data() + 1, arg.size() - 1), 1) : 0x0;
+    return value;
+  }},
+  { "BLEND_SRC_ALPHA", [](const std::string_view& arg) -> Type {
+    Type value = (arg[0] == ':') ? InterpretBlendFactor(std::string_view(arg.data() + 1, arg.size() - 1), 2) : 0x0;
+    return value;
+  }},
+  { "BLEND_DST_ALPHA", [](const std::string_view& arg) -> Type {
+    Type value = (arg[0] == ':') ? InterpretBlendFactor(std::string_view(arg.data() + 1, arg.size() - 1), 3) : 0x0;
+    return value;
+  }},
+  { "BUFFER_MODE", [](const std::string_view& arg) -> Type {
+    Type value = (arg[0] == ':') ? InterpretBufferMode(std::string_view(arg.data() + 1, arg.size() - 1)) : 0x0;
+    return value;
+  }},
+};
+
+}
+
+Type Parse(const char* string, size_t length, StringCallback onError)
+{
+  if (length == 0)
+  {
+    length = strlen(string);
+  }
+
+  Type value = 0x0;
+  auto iEnd = string + length;
+  while (string != iEnd)
+  {
+    auto iNextToken = std::find(string, iEnd, '|');
+    auto iColon = std::find(string, iNextToken, ':');
+    auto i = RENDERER_STATE_PROCESSORS.find(std::string_view(string, iColon - string));
+    if (i != RENDERER_STATE_PROCESSORS.end() && size_t(std::distance(string, iNextToken)) >= i->first.size())
+    {
+      value |= i->second(std::string_view(string + i->first.size(), iNextToken - iColon));
+    }
+    else
+    {
+      onError("Not a valid RendererState: " + std::string(string, iNextToken));
+    }
+
+    string = iNextToken + (iNextToken != iEnd);
+  }
+
+  return value;
+}
+
+}  // RendererState
+}
+}
diff --git a/dali-scene-loader/public-api/parse-renderer-state.h b/dali-scene-loader/public-api/parse-renderer-state.h
new file mode 100644 (file)
index 0000000..97792aa
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef DALI_SCENE_LOADER_INTERPRET_RENDERER_STATE_H
+#define DALI_SCENE_LOADER_INTERPRET_RENDERER_STATE_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "dali-scene-loader/public-api/renderer-state.h"
+#include "dali-scene-loader/public-api/string-callback.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+namespace RendererState
+{
+
+/*
+ * @brief Attempts to interpret a string for renderer states, which can be a combination of the following
+ *  (using '|' as a delimiter, if multiple specified):<br/>
+ *  - one of Value, exc. for NONE or those with the _BITS, _SHIFT, _MASK or _ITEMS suffix;<br/>
+ *  - DEPTH_FUNC:${one of Comparison::Type, exc. OMIT};<br/>
+ *  - BLEND_(SRC_DST)_(RGB|ALPHA):${one of BlendFactor::Type, exc. OMIT};<br/>
+ *  - BUFFER_MODE:${one of BufferMode::Type, exc. OMIT};
+ * @param string The string to parse;
+ * @param length The length of the string; If left at 0, strlen() will be called, which of
+ *   course is only suitable i the string is null-terminated.
+ * @param onError The callback to call when an error occurred.
+ * @note No other characters are allowed.
+ */
+DALI_SCENE_LOADER_API Type Parse(const char* string, size_t length = 0, StringCallback onError = DefaultErrorCallback);
+
+}
+}
+}
+
+#endif //DALI_SCENE_LOADER_INTERPRET_RENDERER_STATE_H
diff --git a/dali-scene-loader/public-api/renderer-state.cpp b/dali-scene-loader/public-api/renderer-state.cpp
new file mode 100644 (file)
index 0000000..c3888a0
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/public-api/renderer-state.h"
+#include "dali-scene-loader/public-api/utils.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+namespace
+{
+// NOTE: values for BlendFactor aren't contiguous, hence we need a mapping.
+const Dali::BlendFactor::Type  kBlendFactors[] = {
+  Dali::BlendFactor::ZERO,
+  Dali::BlendFactor::ONE,
+  Dali::BlendFactor::SRC_COLOR,
+  Dali::BlendFactor::ONE_MINUS_SRC_COLOR,
+  Dali::BlendFactor::SRC_ALPHA,
+  Dali::BlendFactor::ONE_MINUS_SRC_ALPHA,
+  Dali::BlendFactor::DST_ALPHA,
+  Dali::BlendFactor::ONE_MINUS_DST_ALPHA,
+  Dali::BlendFactor::DST_COLOR,
+  Dali::BlendFactor::ONE_MINUS_DST_COLOR,
+  Dali::BlendFactor::SRC_ALPHA_SATURATE,
+  Dali::BlendFactor::CONSTANT_COLOR,
+  Dali::BlendFactor::ONE_MINUS_CONSTANT_COLOR,
+  Dali::BlendFactor::CONSTANT_ALPHA,
+  Dali::BlendFactor::ONE_MINUS_CONSTANT_ALPHA,
+};
+}
+
+namespace RendererState
+{
+#define RENDERER_SET_PROPERTY(name, value) renderer.SetProperty(Renderer::Property::name, (value))
+
+void Apply(Type rendererState, Renderer& renderer)
+{
+  RENDERER_SET_PROPERTY(DEPTH_WRITE_MODE, MaskMatch(rendererState, DEPTH_WRITE) ? DepthWriteMode::ON : DepthWriteMode::OFF);
+  RENDERER_SET_PROPERTY(DEPTH_TEST_MODE, MaskMatch(rendererState, DEPTH_TEST) ? DepthTestMode::ON : DepthTestMode::OFF);
+
+  RENDERER_SET_PROPERTY(BLEND_MODE, MaskMatch(rendererState, ALPHA_BLEND) ? BlendMode::ON : BlendMode::OFF);
+
+  const bool cullBack = MaskMatch(rendererState, CULL_BACK);
+  RENDERER_SET_PROPERTY(FACE_CULLING_MODE, MaskMatch(rendererState, CULL_FRONT) ?
+    (cullBack ? FaceCullingMode::FRONT_AND_BACK : FaceCullingMode::FRONT) :
+    (cullBack ? FaceCullingMode::BACK : FaceCullingMode::NONE));
+
+  if (auto depthFunc = (rendererState & DEPTH_FUNCTION_MASK) >> DEPTH_FUNCTION_SHIFT)
+  {
+    RENDERER_SET_PROPERTY(DEPTH_FUNCTION, static_cast<DepthFunction::Type>(depthFunc - 1));
+  }
+
+  if (auto blendFactors = (rendererState & BLEND_FACTOR_MASK) >> BLEND_FACTOR_BASE_SHIFT)
+  {
+    if (auto srcRgb = (blendFactors & BLEND_FACTOR_ITEM_MASK))
+    {
+      RENDERER_SET_PROPERTY(BLEND_FACTOR_SRC_RGB, kBlendFactors[static_cast<BlendFactor::Type>(srcRgb - 1)]);
+    }
+
+    blendFactors >>= BLEND_FACTOR_ITEM_BITS;
+    if (auto dstRgb = (blendFactors & BLEND_FACTOR_ITEM_MASK))
+    {
+      RENDERER_SET_PROPERTY(BLEND_FACTOR_DEST_RGB, kBlendFactors[static_cast<BlendFactor::Type>(dstRgb - 1)]);
+    }
+
+    blendFactors >>= BLEND_FACTOR_ITEM_BITS;
+    if (auto srcAlpha = (blendFactors & BLEND_FACTOR_ITEM_MASK))
+    {
+      RENDERER_SET_PROPERTY(BLEND_FACTOR_SRC_ALPHA, kBlendFactors[static_cast<BlendFactor::Type>(srcAlpha - 1)]);
+    }
+
+    blendFactors >>= BLEND_FACTOR_ITEM_BITS;
+    if (auto dstAlpha = (blendFactors & BLEND_FACTOR_ITEM_MASK))
+    {
+      RENDERER_SET_PROPERTY(BLEND_FACTOR_DEST_ALPHA, kBlendFactors[static_cast<BlendFactor::Type>(dstAlpha - 1)]);
+    }
+  }
+
+  if (auto bufferMode = (rendererState & BUFFER_MODE_MASK) >> BUFFER_MODE_SHIFT)
+  {
+    RENDERER_SET_PROPERTY(RENDER_MODE, static_cast<RenderMode::Type>(bufferMode - 1));
+  }
+}
+
+} // RendererState
+
+}
+}
diff --git a/dali-scene-loader/public-api/renderer-state.h b/dali-scene-loader/public-api/renderer-state.h
new file mode 100644 (file)
index 0000000..dc1bf63
--- /dev/null
@@ -0,0 +1,165 @@
+#ifndef DALI_SCENE_LOADER_RENDERER_STATE_H
+#define DALI_SCENE_LOADER_RENDERER_STATE_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/rendering/renderer.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/*
+ * @brief Contains values for comparison functions used in depth and stencil testing.
+ * @note Relative order of members must match DepthFunction::Type and StencilFunction::Type.
+ */
+struct DALI_SCENE_LOADER_API Comparison
+{
+  enum Type
+  {
+    OMIT,  // not specified; will not be set.
+    NEVER,
+    ALWAYS,
+    LESS,
+    GREATER,
+    EQUAL,
+    NOT_EQUAL,
+    LESS_EQUAL,
+    GREATER_EQUAL
+  };
+
+  Comparison() = delete;
+};
+
+/*
+ * @brief Determines the blend factor used.
+ * @note Relative order of members must match BlendFactor::Type.
+ */
+struct DALI_SCENE_LOADER_API BlendFactor
+{
+  enum Type
+  {
+    OMIT,  // not specified - will not be updated
+    ZERO,
+    ONE,  // default for source alpha
+    SRC_COLOR,
+    ONE_MINUS_SRC_COLOR,
+    SRC_ALPHA,  // default for source RGB
+    ONE_MINUS_SRC_ALPHA,  // default for destination RGB and destination alpha
+    DST_ALPHA,
+    ONE_MINUS_DST_ALPHA,
+    DST_COLOR,
+    ONE_MINUS_DST_COLOR,
+    SRC_ALPHA_SATURATE,
+    CONSTANT_COLOR,
+    ONE_MINUS_CONSTANT_COLOR,
+    CONSTANT_ALPHA,
+    ONE_MINUS_CONSTANT_ALPHA,
+  };
+
+  BlendFactor() = delete;
+};
+
+/*
+ * @brief Determines which buffers shall the Renderer write into.
+ * @note Relative order of members must match RenderMode::Type.
+ */
+struct DALI_SCENE_LOADER_API BufferMode
+{
+  enum Type
+  {
+    OMIT,  ///< not specified - will not be updated
+    NONE,  ///< Don’t write to either color or stencil buffer (But will potentially render to depth buffer).
+    AUTO,  ///< Writes are managed by the Actor Clipping API. This is DALi's default.
+    COLOR,  ///< Ignore stencil properties.  Write to the color buffer.
+    STENCIL,  ///< Use the stencil properties. Do not write to the color buffer.
+    COLOR_STENCIL  ///< Use the stencil properties AND Write to the color buffer.
+  };
+
+  BufferMode() = delete;
+};
+
+/*
+ * @brief Contains values and functionality for configuring Renderers.
+ */
+namespace RendererState
+{
+
+using Type = uint32_t;  // 8 bits reserved for flags, 4 * 4 bit for blend factors, 4 bits for depth function
+
+enum DALI_SCENE_LOADER_API Value : Type
+{
+  NONE = 0x0,
+
+  DEPTH_WRITE = 0x01,
+  DEPTH_TEST = 0x02,
+
+  CULL_FRONT = 0x04,
+  CULL_BACK = 0x08,
+
+  ALPHA_BLEND = 0x10,
+
+  DEPTH_FUNCTION_SHIFT = 6,
+  DEPTH_FUNCTION_BITS = 4,
+  DEPTH_FUNCTION_MASK = ((1 << DEPTH_FUNCTION_BITS) - 1) << DEPTH_FUNCTION_SHIFT,
+
+  BLEND_FACTOR_BASE_SHIFT = DEPTH_FUNCTION_SHIFT + DEPTH_FUNCTION_BITS,
+  BLEND_FACTOR_ITEM_BITS = 4,
+  BLEND_FACTOR_ITEMS = 4,
+  BLEND_FACTOR_BITS = BLEND_FACTOR_ITEM_BITS * BLEND_FACTOR_ITEMS,
+  BLEND_FACTOR_MASK = ((1 << BLEND_FACTOR_BITS) - 1) << BLEND_FACTOR_BASE_SHIFT,
+  BLEND_FACTOR_ITEM_MASK = (1 << BLEND_FACTOR_ITEM_BITS) - 1,  // after rshifting by BLEND_FACTOR_BASE_SHIFT
+
+  // Buffer mode is DALi's RenderMode, just to avoid too much conflation.
+  BUFFER_MODE_BITS = 3u,
+  BUFFER_MODE_SHIFT = 32u - BUFFER_MODE_BITS,  // from end
+  BUFFER_MODE_MASK = ((1u << BUFFER_MODE_BITS) - 1u) << BUFFER_MODE_SHIFT,
+
+  DEFAULT = DEPTH_WRITE | DEPTH_TEST | CULL_BACK | (Comparison::LESS_EQUAL << DEPTH_FUNCTION_SHIFT),
+};
+
+/*
+ * @brief Encodes the given blend factors into a RenderMode value, maskable into other options,
+ *  passable into ApplyRenderMode().
+ */
+inline
+DALI_SCENE_LOADER_API constexpr uint32_t FromBlendFactors(BlendFactor::Type srcRgb, BlendFactor::Type destRgb,
+  BlendFactor::Type srcAlpha, BlendFactor::Type destAlpha)
+{
+  return (srcRgb | (destRgb << BLEND_FACTOR_ITEM_BITS) | (srcAlpha << (BLEND_FACTOR_ITEM_BITS * 2)) |
+    (destAlpha << (BLEND_FACTOR_ITEM_BITS * 3))) << BLEND_FACTOR_BASE_SHIFT;
+}
+
+/*
+ * @brief Applies the settings encoded in @a rendererState, to a @a renderer.
+ * @note Depth function is only set if not Comparison::OMIT.
+ * @note Blend factors are only set if not BlendFactor::OMIT.
+ * @note Buffer mode is only set is not BufferMode::OMIT.
+ */
+DALI_SCENE_LOADER_API void Apply(Type rendererState, Renderer& renderer);
+
+} // RendererState
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_RENDERER_STATE_H
diff --git a/dali-scene-loader/public-api/resource-bundle.cpp b/dali-scene-loader/public-api/resource-bundle.cpp
new file mode 100644 (file)
index 0000000..9ff10f6
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// FILE HEADER
+#include "dali-scene-loader/public-api/resource-bundle.h"
+
+// EXTERNAL
+#include "dali/public-api/rendering/sampler.h"
+#include "dali-toolkit/public-api/image-loader/sync-image-loader.h"
+#include <fstream>
+#include <istream>
+#include <cstring>
+#include <fstream>
+
+namespace Dali
+{
+using namespace Toolkit;
+
+namespace SceneLoader
+{
+namespace
+{
+
+const char* const RESOURCE_TYPE_NAMES[] = {
+  "Environment",
+  "Shader",
+  "Mesh",
+  "Material",
+};
+
+}  // nonamespace
+
+const char* GetResourceTypeName(ResourceType::Value type)
+{
+  return RESOURCE_TYPE_NAMES[static_cast<int>(type)];
+}
+
+ResourceRefCounts ResourceBundle::CreateRefCounter() const
+{
+  ResourceRefCounts refCounts(4);
+  refCounts[ResourceType::Environment].Resize(mEnvironmentMaps.size(), 0u);
+  refCounts[ResourceType::Shader].Resize(mShaders.size(), 0u);
+  refCounts[ResourceType::Mesh].Resize(mMeshes.size(), 0u);
+  refCounts[ResourceType::Material].Resize(mMaterials.size(), 0u);
+  return refCounts;
+}
+
+void ResourceBundle::CountEnvironmentReferences(ResourceRefCounts& refCounts) const
+{
+  auto& environmentRefCounts = refCounts[ResourceType::Environment];
+
+  const auto& materialRefs = refCounts[ResourceType::Material];
+  for (uint32_t i = 0, iEnd = materialRefs.Size(); i != iEnd; ++i)
+  {
+    if (materialRefs[i] > 0)
+    {
+      ++environmentRefCounts[mMaterials[i].first.mEnvironmentIdx];
+    }
+  }
+}
+
+void ResourceBundle::LoadResources(const ResourceRefCounts& refCounts, PathProvider pathProvider, Options::Type options)
+{
+  const auto kForceLoad = MaskMatch(options, Options::ForceReload);
+  const auto kKeepUnused = MaskMatch(options, Options::KeepUnused);
+
+  const auto& refCountEnvMaps = refCounts[ResourceType::Environment];
+  auto environmentsPath = pathProvider(ResourceType::Environment);
+  for (uint32_t i = 0, iEnd = refCountEnvMaps.Size(); i != iEnd; ++i)
+  {
+    auto refCount = refCountEnvMaps[i];
+    auto& iEnvMap = mEnvironmentMaps[i];
+    if (refCount > 0 && (kForceLoad || !iEnvMap.second.IsLoaded()))
+    {
+      auto raw = iEnvMap.first.LoadRaw(environmentsPath);
+      iEnvMap.second = iEnvMap.first.Load(std::move(raw));
+    }
+    else if (!kKeepUnused && refCount == 0 && iEnvMap.second.IsLoaded())
+    {
+      iEnvMap.second.mDiffuse = Texture();
+      iEnvMap.second.mSpecular = Texture();
+    }
+  }
+
+  const auto& refCountShaders = refCounts[ResourceType::Shader];
+  auto shadersPath = pathProvider(ResourceType::Shader);
+  for (uint32_t i = 0, iEnd = refCountShaders.Size(); i != iEnd; ++i)
+  {
+    auto refCount = refCountShaders[i];
+    auto& iShader = mShaders[i];
+    if (refCount > 0 && (kForceLoad || !iShader.second))
+    {
+      auto raw = iShader.first.LoadRaw(shadersPath);
+      iShader.second = iShader.first.Load(std::move(raw));
+    }
+    else if(!kKeepUnused && refCount == 0 && iShader.second)
+    {
+      iShader.second = Shader();
+    }
+  }
+
+  const auto& refCountMeshes = refCounts[ResourceType::Mesh];
+  auto modelsPath = pathProvider(ResourceType::Mesh);
+  for (uint32_t i = 0, iEnd = refCountMeshes.Size(); i != iEnd; ++i)
+  {
+    auto refCount = refCountMeshes[i];
+    auto& iMesh = mMeshes[i];
+    if (refCount > 0 && (kForceLoad || !iMesh.second.geometry))
+    {
+      auto raw = iMesh.first.LoadRaw(modelsPath);
+      iMesh.second = iMesh.first.Load(std::move(raw));
+    }
+    else if (!kKeepUnused && refCount == 0 && iMesh.second.geometry)
+    {
+      iMesh.second.geometry = Geometry();
+    }
+  }
+
+  const auto& refCountMaterials = refCounts[ResourceType::Material];
+  auto imagesPath = pathProvider(ResourceType::Material);
+  for (uint32_t i = 0, iEnd = refCountMaterials.Size(); i != iEnd; ++i)
+  {
+    auto refCount = refCountMaterials[i];
+    auto& iMaterial = mMaterials[i];
+    if (refCount > 0 && (kForceLoad || !iMaterial.second))
+    {
+      auto raw = iMaterial.first.LoadRaw(imagesPath);
+      iMaterial.second = iMaterial.first.Load(mEnvironmentMaps, std::move(raw));
+    }
+    else if (!kKeepUnused && refCount == 0 && iMaterial.second)
+    {
+      iMaterial.second = TextureSet();
+    }
+  }
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/resource-bundle.h b/dali-scene-loader/public-api/resource-bundle.h
new file mode 100644 (file)
index 0000000..16860ec
--- /dev/null
@@ -0,0 +1,129 @@
+#ifndef DALI_SCENE_LOADER_RESOURCE_BUNDLE_H_
+#define DALI_SCENE_LOADER_RESOURCE_BUNDLE_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL
+#include "dali-scene-loader/public-api/mesh-definition.h"
+#include "dali-scene-loader/public-api/material-definition.h"
+#include "dali-scene-loader/public-api/environment-definition.h"
+#include "dali-scene-loader/public-api/shader-definition.h"
+#include "dali-scene-loader/public-api/skeleton-definition.h"
+
+// EXTERNAL
+#include "dali/public-api/common/vector-wrapper.h"
+#include "dali/public-api/rendering/shader.h"
+#include "dali/public-api/rendering/texture-set.h"
+#include <memory>
+#include <functional>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/*
+ * @brief The types of resources that .dli may define.
+ */
+struct DALI_SCENE_LOADER_API ResourceType
+{
+  enum Value
+  {
+    Environment,
+    Shader,
+    Mesh,
+    Material,
+  };
+
+  ResourceType() = delete;
+};
+
+/*
+ * @return The string value corresponding to the given resource @a type.
+ */
+DALI_SCENE_LOADER_API const char* GetResourceTypeName(ResourceType::Value type);
+
+using ResourceRefCounts = std::vector<Vector<uint32_t>>;
+
+/*
+ * @brief Stores all resource definitions along with the DALi resources that
+ *  could be created from them, directly indexible into with values from a dli
+ *  document.
+ */
+class DALI_SCENE_LOADER_API ResourceBundle
+{
+public:
+  struct Options
+  {
+    using Type = uint8_t;
+
+    enum Value : Type
+    {
+      None = 0,
+      ForceReload = NthBit(0),  ///< Load resources [again] even if they were already loaded.
+      KeepUnused = NthBit(1)  ///<s Don't reset handles to resources that had a 0 reference count.
+    };
+  };
+
+  using PathProvider = std::function<std::string(ResourceType::Value)>;
+
+  ResourceBundle() = default;
+
+  ResourceBundle(const ResourceBundle&) = delete;
+  ResourceBundle& operator=(const ResourceBundle&) = delete;
+
+  ResourceBundle(ResourceBundle&&) = default;
+  ResourceBundle& operator=(ResourceBundle&&) = default;
+
+  /*
+   * @return A ResourceRefCounts object with the correct number of entries for
+   *  all resource types (based on the various resource definition vectors),
+   *  with all reference counts set to 0.
+   */
+  ResourceRefCounts CreateRefCounter() const;
+
+  /*
+   * @brief Based on a ResourceRefCounts, and more specifically the reference
+   *  count of materials therein, it will calculate the reference count of
+   *  environment maps.
+   */
+  void CountEnvironmentReferences(ResourceRefCounts& refCounts) const;
+
+  /*
+   * @brief Performs the loading of all resources based on their respective
+   *  reference count in @a refCounts. Resources that had a non-zero ref count will be
+   *  loaded unless we already have a handle to them (OR the ForceReload option was specified).
+   *  Any handles we have to resources that come in with a zero ref count will be reset,
+   *  UNLESS the KeepUnused option was specified.
+   */
+  void LoadResources(const ResourceRefCounts& refCounts,
+    PathProvider pathProvider,
+    Options::Type options = Options::None);
+
+public: // DATA
+  EnvironmentDefinition::Vector mEnvironmentMaps;
+  ShaderDefinition::Vector mShaders;
+  MeshDefinition::Vector mMeshes;
+  MaterialDefinition::Vector mMaterials;
+
+  SkeletonDefinition::Vector mSkeletons;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_RESOURCE_BUNDLE_H_
diff --git a/dali-scene-loader/public-api/scene-definition.cpp b/dali-scene-loader/public-api/scene-definition.cpp
new file mode 100644 (file)
index 0000000..409d285
--- /dev/null
@@ -0,0 +1,1236 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL
+#include "dali/public-api/animation/constraints.h"
+#include "dali/devel-api/common/map-wrapper.h"
+
+// INTERNAL
+#include "dali-scene-loader/public-api/scene-definition.h"
+#include "dali-scene-loader/public-api/blend-shape-details.h"
+#include "dali-scene-loader/public-api/utils.h"
+#include "dali-scene-loader/public-api/skinning-details.h"
+
+//#define DEBUG_SCENE_DEFINITION
+//#define DEBUG_JOINTS
+
+#if defined(DEBUG_SCENE_DEFINITION) || defined(DEBUG_JOINTS)
+#define DEBUG_ONLY(x) x
+#else
+#define DEBUG_ONLY(x)
+#endif
+
+#define LOGD(x) DEBUG_ONLY(printf x ; printf("\n"); fflush(stdout))
+
+namespace Dali
+{
+namespace SceneLoader
+{
+namespace
+{
+
+const std::string JOINT_MATRIX{ "jointMatrix" };
+
+const std::map<Property::Type, Constraint(*)(Actor&, Property::Index)> sConstraintFactory = {
+  {
+    Property::Type::BOOLEAN,
+    [](Actor& a, Property::Index i) {
+      return Constraint::New<bool>(a, i, [](bool& current, const PropertyInputContainer& inputs) {
+        current = inputs[0]->GetBoolean();
+      });
+    }
+  },
+  {
+    Property::Type::INTEGER,
+    [](Actor& a, Property::Index i) {
+      return Constraint::New<int>(a, i, [](int& current, const PropertyInputContainer& inputs) {
+        current = inputs[0]->GetInteger();
+      });
+    }
+  },
+  {
+    Property::Type::FLOAT,
+    [](Actor& a, Property::Index i) {
+      return Constraint::New<float>(a, i, EqualToConstraint());
+    }
+  },
+  {
+    Property::Type::VECTOR2,
+    [](Actor& a, Property::Index i) {
+      return Constraint::New<Vector2>(a, i, EqualToConstraint());
+    }
+  },
+  {
+    Property::Type::VECTOR3,
+    [](Actor& a, Property::Index i) {
+      return Constraint::New<Vector3>(a, i, EqualToConstraint());
+    }
+  },
+  {
+    Property::Type::VECTOR4,
+    [](Actor& a, Property::Index i) {
+      return Constraint::New<Vector4>(a, i, EqualToConstraint());
+    }
+  },
+  {
+    Property::Type::MATRIX,
+    [](Actor& a, Property::Index i) {
+      return Constraint::New<Matrix>(a, i, EqualToConstraint());
+    }
+  },
+  {
+    Property::Type::MATRIX3,
+    [](Actor& a, Property::Index i) {
+      return Constraint::New<Matrix3>(a, i, EqualToConstraint());
+    }
+  },
+  {
+    Property::Type::ROTATION,
+    [](Actor& a, Property::Index i) {
+      return Constraint::New<Quaternion>(a, i, EqualToConstraint());
+    }
+  },
+};
+
+struct ResourceReflector : IResourceReflector
+{
+  Index* iMesh = nullptr;
+  Index* iShader = nullptr;
+
+  void Reflect(ResourceType::Value type, Index& id)
+  {
+    switch (type)
+    {
+    case ResourceType::Shader:
+      DALI_ASSERT_ALWAYS(!iShader && "Shader index already assigned!");
+      iShader = &id;
+      break;
+
+    case ResourceType::Mesh:
+      DALI_ASSERT_ALWAYS(!iMesh && "Mesh index already assigned!");
+      iMesh = &id;
+      break;
+
+    default:  // Other resource types are not relevant to the problem at hand.
+      break;
+    }
+  }
+};
+
+
+#ifdef DEBUG_JOINTS
+const char* JOINT_DEBUG_VSH = "#version 300 es\n"
+DALI_COMPOSE_SHADER(
+  precision mediump float;
+  uniform mat4 uMvpMatrix;
+  in vec3 aPosition;
+  in float aColor;
+  flat out float vColor;
+  void main() {
+    vColor = aColor;
+    gl_Position = uMvpMatrix * vec4(aPosition, 1.0);
+  });
+
+const char* JOINT_DEBUG_FSH = "#version 300 es\n"
+DALI_COMPOSE_SHADER(
+  precision mediump float;
+  flat in float vColor;
+  out vec4 FragColor;
+  void main() {
+    vec3 rgb = vec3(fract(vColor), fract(vColor * 0.00390625), fract(vColor * 0.00390625 * 0.00390625));
+    FragColor = vec4(rgb, 1.);
+  });
+
+Shader sJointDebugShader;
+int sNumScenes = 0;
+
+void EnsureJointDebugShaderCreated()
+{
+  if (0 == sNumScenes)
+  {
+    sJointDebugShader = Shader::New(JOINT_DEBUG_VSH, JOINT_DEBUG_FSH);
+  }
+  ++sNumScenes;
+}
+
+void AddJointDebugVisual(Actor aJoint)
+{
+  Property::Map attribs;
+  attribs["aPosition"] = Property::Type::VECTOR3;
+  attribs["aColor"] = Property::Type::FLOAT;
+
+  PropertyBuffer vbo = PropertyBuffer::New(attribs);
+
+  struct Vertex
+  {
+    Vector3 pos;
+    float color;
+  } vertices[] = {
+    { Vector3::ZERO, .999f + .999f * 256.f + .999f * 256.f * 256.f },
+    { Vector3::XAXIS, .999f },
+    { Vector3::YAXIS, .999f * 256.f },
+    { Vector3::ZAXIS, .999f * 256.f * 256.f },
+  };
+
+  vbo.SetData(&vertices, std::extent<decltype(vertices)>::value);
+
+  uint16_t indices[] = { 0, 1, 0, 2, 0, 3 };
+
+  Geometry geo = Geometry::New();
+  geo.AddVertexBuffer(vbo);
+  geo.SetIndexBuffer(indices, std::extent<decltype(indices)>::value);
+  geo.SetType(Geometry::LINES);
+
+  Renderer r = Renderer::New(geo, sJointDebugShader);
+  aJoint.AddRenderer(r);
+
+  aJoint.SetVisible(true);
+}
+#endif  //DEBUG_JOINTS
+
+class ActorCreatorVisitor : public NodeDefinition::IConstVisitor
+{
+public:
+  ActorCreatorVisitor(NodeDefinition::CreateParams& params)
+  : mCreationContext(params)
+  {}
+
+  void Start(const NodeDefinition& n)
+  {
+    mCreationContext.mXforms.modelStack.Push(n.GetLocalSpace());
+
+    Actor a = n.CreateActor(mCreationContext);
+    if (!mActorStack.empty())
+    {
+      mActorStack.back().Add(a);
+    }
+    else
+    {
+      mRoot = a;
+    }
+    mActorStack.push_back(a);
+  }
+
+  void Finish(const NodeDefinition& n)
+  {
+    mActorStack.pop_back();
+    mCreationContext.mXforms.modelStack.Pop();
+  }
+
+  Actor GetRoot() const
+  {
+    return mRoot;
+  }
+
+private:
+  NodeDefinition::CreateParams& mCreationContext;
+  std::vector<Actor> mActorStack;
+  Actor mRoot;
+};
+
+bool IsAncestor(const SceneDefinition& scene, Index ancestor, Index node, Index rootHint = INVALID_INDEX)
+{
+  bool isAncestor = false;
+  while (node != rootHint && !isAncestor)
+  {
+    node = scene.GetNode(node)->mParentIdx;
+    isAncestor = ancestor == node;
+  }
+  return isAncestor;
+}
+
+void InsertUniqueSorted(std::vector<Index>& data, Index value)
+{
+  auto iInsert = std::lower_bound(data.begin(), data.end(), value);
+  if (iInsert == data.end() || *iInsert != value)
+  {
+    data.insert(iInsert, value);
+  }
+}
+
+void RemoveFromSorted(std::vector<Index>& data, Index value)
+{
+  auto iRemove = std::lower_bound(data.begin(), data.end(), value);
+  if (iRemove != data.end() && *iRemove == value)
+  {
+    data.erase(iRemove);
+  }
+}
+
+Property::Index ConfigureJointMatrix(Actor actor, Actor ancestor, Property::Index propJointMatrix)
+{
+  Actor parent = actor.GetParent();
+  if (parent != ancestor)
+  {
+    propJointMatrix = ConfigureJointMatrix(parent, ancestor, propJointMatrix);
+  }
+
+  auto myPropJointMatrix = actor.GetPropertyIndex(JOINT_MATRIX);
+  if (myPropJointMatrix == Property::INVALID_INDEX)
+  {
+    myPropJointMatrix = actor.RegisterProperty(JOINT_MATRIX, Matrix{ false });
+    Constraint constraint = Constraint::New<Matrix>(actor, propJointMatrix,
+      [](Matrix& output, const PropertyInputContainer& inputs)
+      {
+        Matrix jointMatrix{ false };
+        jointMatrix.SetTransformComponents(Vector3::ONE, inputs[0]->GetQuaternion(), inputs[1]->GetVector3());
+
+        Matrix::Multiply(output, jointMatrix, inputs[2]->GetMatrix());
+      });
+    constraint.AddSource(Source{ actor, Actor::Property::ORIENTATION });
+    constraint.AddSource(Source{ actor, Actor::Property::POSITION });
+    constraint.AddSource(Source{ parent, propJointMatrix });
+    constraint.Apply();
+  }
+
+  return myPropJointMatrix;
+}
+
+void SortAndDeduplicateSkinningRequests(std::vector<SkinningShaderConfigurationRequest>& requests)
+{
+  // Sort requests by shaders.
+  std::sort(requests.begin(), requests.end());
+
+  // Remove duplicates.
+  auto i = requests.begin();
+  auto iEnd = requests.end();
+  Shader s = i->mShader;
+  Index skeletonIdx = i->mSkeletonIdx;
+  ++i;
+  do
+  {
+    // Multiple identical shader instances are removed.
+    while (i != iEnd && i->mShader == s)
+    {
+      // Cannot have multiple skeletons input to the same shader.
+      // NOTE: DliModel now makes sure this doesn't happen.
+      DALI_ASSERT_ALWAYS(i->mSkeletonIdx == skeletonIdx &&
+        "Skinning shader must not be shared between different skeletons.");
+
+      i->mShader = Shader();
+      ++i;
+    }
+
+    if (i == iEnd)
+    {
+      break;
+    }
+    s = i->mShader;
+    skeletonIdx = i->mSkeletonIdx;
+    ++i;
+  } while (true);
+
+  requests.erase(std::remove_if(requests.begin(), requests.end(), [](const SkinningShaderConfigurationRequest& sscr)
+  {
+    return !sscr.mShader;
+  }), requests.end());
+}
+
+void ConfigureBoneMatrix(const Matrix& ibm, Actor joint, Shader& shader, Index& boneIdx)
+{
+  // Register bone transform on shader.
+  char propertyNameBuffer[32];
+  snprintf(propertyNameBuffer, sizeof(propertyNameBuffer), "%s[%d]", Skinning::BONE_UNIFORM_NAME.c_str(), boneIdx);
+  DALI_ASSERT_DEBUG(shader.GetPropertyIndex(propertyNameBuffer) == Property::INVALID_INDEX);
+  auto propBoneXform = shader.RegisterProperty(propertyNameBuffer, Matrix{ false });
+
+  // Constrain bone matrix to joint transform.
+  Constraint constraint = Constraint::New<Matrix>(shader, propBoneXform,
+    [ibm](Matrix& output, const PropertyInputContainer& inputs)
+    {
+      Matrix::Multiply(output, ibm, inputs[0]->GetMatrix());
+    });
+
+  auto propJointMatrix = joint.GetPropertyIndex(JOINT_MATRIX);
+  constraint.AddSource(Source{ joint, propJointMatrix });
+  constraint.Apply();
+
+  ++boneIdx;
+}
+
+template <class Visitor, class SceneDefinition>
+void VisitInternal(Index iNode, const Customization::Choices& choices, Visitor& v, SceneDefinition& sd)
+{
+  auto& node = *sd.GetNode(iNode);
+  v.Start(node);
+
+  if (node.mCustomization)
+  {
+    if (!node.mChildren.empty())
+    {
+      auto choice = choices.Get(node.mCustomization->mTag);
+      Index i = std::min(choice != Customization::NONE ? choice : 0, static_cast<Index>(node.mChildren.size() - 1));
+      sd.Visit(node.mChildren[i], choices, v);
+    }
+  }
+  else
+  {
+    for (auto i : node.mChildren)
+    {
+      sd.Visit(i, choices, v);
+    }
+  }
+
+  v.Finish(node);
+}
+
+} // nonamespace
+
+SceneDefinition::SceneDefinition()
+{
+  mNodes.reserve(128);
+
+#ifdef DEBUG_JOINTS
+  EnsureJointDebugShaderCreated();
+#endif
+}
+
+SceneDefinition::SceneDefinition(SceneDefinition&& other)
+: mNodes(std::move(other.mNodes)),
+  mRootNodeIds(std::move(other.mRootNodeIds))
+{
+#ifdef DEBUG_JOINTS
+  EnsureJointDebugShaderCreated();
+#endif
+}
+
+SceneDefinition::~SceneDefinition()
+{
+#ifdef DEBUG_JOINTS
+  --sNumScenes;
+  if (sNumScenes == 0)
+  {
+    sJointDebugShader = Shader();
+  }
+#endif
+}
+
+uint32_t SceneLoader::SceneDefinition::AddRootNode(Index iNode)
+{
+  if (iNode < mNodes.size())
+  {
+    uint32_t result = mRootNodeIds.size();
+    mRootNodeIds.push_back(iNode);
+    return result;
+  }
+  else
+  {
+    ExceptionFlinger(ASSERT_LOCATION) << "Failed to add new root with node " << iNode << " -- index out of bounds.";
+    return -1;
+  }
+}
+
+const std::vector<Index>& SceneDefinition::GetRoots() const
+{
+  return mRootNodeIds;
+}
+
+void SceneDefinition::RemoveRootNode(Index iRoot)
+{
+  if (iRoot < mRootNodeIds.size())
+  {
+    mRootNodeIds.erase(mRootNodeIds.begin() + iRoot);
+  }
+  else
+  {
+    ExceptionFlinger(ASSERT_LOCATION) << "Failed to remove root " << iRoot << " -- index out of bounds.";
+  }
+}
+
+uint32_t SceneDefinition::GetNodeCount() const
+{
+  return mNodes.size();
+}
+
+const NodeDefinition* SceneDefinition::GetNode(Index iNode) const
+{
+  return mNodes[iNode].get();
+}
+
+NodeDefinition* SceneDefinition::GetNode(Index iNode)
+{
+  return mNodes[iNode].get();
+}
+
+void SceneDefinition::Visit(Index iNode, const Customization::Choices& choices, NodeDefinition::IVisitor& v)
+{
+  VisitInternal(iNode, choices, v, *this);
+}
+
+void SceneDefinition::Visit(Index iNode, const Customization::Choices& choices, NodeDefinition::IConstVisitor& v) const
+{
+  VisitInternal(iNode, choices, v, *this);
+}
+
+void SceneDefinition::CountResourceRefs(Index iNode, const Customization::Choices& choices, ResourceRefCounts& refCounts) const
+{
+  struct RefCounter : IResourceReceiver
+  {
+    ResourceRefCounts* refCounts;
+
+    void Register(ResourceType::Value type, Index id)
+    {
+      ++(*refCounts)[type][id];
+    }
+  };
+
+  struct : NodeDefinition::IConstVisitor
+  {
+    RefCounter counter;
+
+    void Start(const NodeDefinition& n)
+    {
+      if (n.mRenderable)
+      {
+        n.mRenderable->RegisterResources(counter);
+      }
+    }
+
+    void Finish(const NodeDefinition& n)
+    {}
+
+  } refCounterVisitor;
+  refCounterVisitor.counter.refCounts = &refCounts;
+
+  Visit(iNode, choices, refCounterVisitor);
+}
+
+Actor SceneDefinition::CreateNodes(Index iNode, const Customization::Choices & choices,
+  NodeDefinition::CreateParams& params) const
+{
+  ActorCreatorVisitor actorCreatorVisitor(params);
+
+  Visit(iNode, choices, actorCreatorVisitor);
+
+  return actorCreatorVisitor.GetRoot();
+}
+
+void SceneDefinition::GetCustomizationOptions(const Customization::Choices& choices,
+  Customization::Map& outCustomizationOptions, Customization::Choices* outMissingChoices) const
+{
+  struct : NodeDefinition::IConstVisitor
+  {
+    const Customization::Choices* choices;  // choices that we know about.
+    Customization::Map* options;  // tags are registered here. NO OWNERSHIP.
+    Customization::Choices* missingChoices;  // tags will be registered with the default 0. NO OWNERSHIP.
+
+    void Start(const NodeDefinition& n)
+    {
+      if (n.mCustomization)
+      {
+        const std::string& tag = n.mCustomization->mTag;
+        if (missingChoices != nullptr && choices->Get(tag) == Customization::NONE)
+        {
+          missingChoices->Set(tag, 0);
+        }
+
+        auto customization = options->Get(tag);
+        if (!customization)
+        {
+          customization = options->Set(tag, {});
+        }
+        customization->nodes.push_back(n.mName);
+        customization->numOptions = std::max(customization->numOptions,
+          static_cast<uint32_t>(n.mChildren.size()));
+      }
+    }
+
+    void Finish(const NodeDefinition& n)
+    {}
+
+  } customizationRegistrationVisitor;
+  customizationRegistrationVisitor.choices = &choices;
+  customizationRegistrationVisitor.options = &outCustomizationOptions;
+  customizationRegistrationVisitor.missingChoices = outMissingChoices;
+
+  for (auto i : mRootNodeIds)
+  {
+    Visit(i, choices, customizationRegistrationVisitor);
+  }
+}
+
+NodeDefinition* SceneDefinition::AddNode(std::unique_ptr<NodeDefinition>&& nodeDef)
+{
+  if (FindNode(nodeDef->mName))
+  {
+    return nullptr;
+  }
+
+  // add next index (to which we're about to push) as a child to the designated parent, if any.
+  if (nodeDef->mParentIdx != INVALID_INDEX)
+  {
+    mNodes[nodeDef->mParentIdx]->mChildren.push_back(mNodes.size());
+  }
+
+  mNodes.push_back(std::move(nodeDef));
+
+  return mNodes.back().get();
+}
+
+bool SceneDefinition::ReparentNode(const std::string& name, const std::string& newParentName, Index siblingOrder)
+{
+  LOGD(("reparenting %s to %s @ %d", name.c_str(), newParentName.c_str(), siblingOrder));
+
+  std::unique_ptr<NodeDefinition>* nodePtr = nullptr;
+  std::unique_ptr<NodeDefinition>* newParentPtr = nullptr;
+  if (!FindNode(name, &nodePtr) || !FindNode(newParentName, &newParentPtr))
+  {
+    return false;
+  }
+
+  auto& node = *nodePtr;
+  auto iNode = std::distance(mNodes.data(), nodePtr);
+
+  DEBUG_ONLY(auto dumpNode = [](NodeDefinition const& n) {
+    std::ostringstream stream;
+    stream << n.mName << " (" << n.mParentIdx << "):";
+    for (auto i : n.mChildren)
+    {
+      stream << i << ", ";
+    }
+    LOGD(("%s", stream.str().c_str()));
+  };)
+
+  // Remove node from children of previous parent (if any).
+  if (node->mParentIdx != INVALID_INDEX)
+  {
+    LOGD(("old parent:"));
+    DEBUG_ONLY(dumpNode(*mNodes[node->mParentIdx]);)
+
+    auto& children = mNodes[node->mParentIdx]->mChildren;
+    children.erase(std::remove(children.begin(), children.end(), iNode), children.end());
+
+    DEBUG_ONLY(dumpNode(*mNodes[node->mParentIdx]);)
+  }
+
+  // Register node to new parent.
+  LOGD(("new parent:"));
+  DEBUG_ONLY(dumpNode(**newParentPtr);)
+  auto& children = (*newParentPtr)->mChildren;
+  if (siblingOrder > children.size())
+  {
+    siblingOrder = children.size();
+  }
+  children.insert(children.begin() + siblingOrder, 1, iNode);
+  DEBUG_ONLY(dumpNode(**newParentPtr);)
+
+  // Update parent index.
+  LOGD(("node:"));
+  DEBUG_ONLY(dumpNode(*node);)
+  auto iParent = std::distance(mNodes.data(), newParentPtr);
+  node->mParentIdx = iParent;
+  DEBUG_ONLY(dumpNode(*node);)
+  return true;
+}
+
+bool SceneDefinition::RemoveNode(const std::string& name)
+{
+  std::unique_ptr<NodeDefinition>* node = nullptr;
+  if (!FindNode(name, &node))
+  {
+    return false;
+  }
+
+  // Reset node def pointers recursively.
+  auto& thisNodes = mNodes;
+  unsigned int numReset = 0;
+  std::function<void(std::unique_ptr<NodeDefinition>&)> resetFn =
+    [&thisNodes, &resetFn, &numReset](std::unique_ptr<NodeDefinition>& nd) {
+    LOGD(("resetting %d", &nd - thisNodes.data()));
+    for (auto i : nd->mChildren)
+    {
+      resetFn(thisNodes[i]);
+    }
+    nd.reset();
+    ++numReset;
+  };
+
+  resetFn(*node);
+
+  // Gather indices of dead nodes into a vector which we sort on insertion.
+  std::vector<Index> offsets;
+  offsets.reserve(numReset);
+  for (auto& n : mNodes)
+  {
+    if (!n)
+    {
+      offsets.push_back(std::distance(mNodes.data(), &n));
+    }
+  }
+
+  // Erase dead nodes as they don't have to be processed anymore.
+  mNodes.erase(std::remove(mNodes.begin(), mNodes.end(), decltype(mNodes)::value_type()), mNodes.end());
+
+  // Offset all indices (parent and child) by the index they'd sort into in offsets.
+  enum { INDEX_FOR_REMOVAL = INVALID_INDEX };
+  auto offsetter = [&offsets](Index& i) {
+    auto iFind = std::lower_bound(offsets.begin(), offsets.end(), i);
+    if (iFind != offsets.end() && *iFind == i)
+    {
+      LOGD(("marking %d for removal.", i));
+      i = INDEX_FOR_REMOVAL;
+      return false;
+    }
+    else
+    {
+      auto distance = std::distance(offsets.begin(), iFind);
+      if (distance > 0)
+      {
+        LOGD(("offsetting %d by %d.", i, distance));
+        i -= distance;
+      }
+      return true;
+    }
+  };
+
+  for (auto& nd : mNodes)
+  {
+    bool parentOffsetResult = offsetter(nd->mParentIdx);
+    DALI_ASSERT_ALWAYS(parentOffsetResult);  // since nodes were recursively removed, we should not be finding invalid parents at this point.
+
+    auto& children = nd->mChildren;
+    for (auto i0 = children.begin(), i1 = children.end(); i0 != i1; ++i0)
+    {
+      offsetter(*i0);
+    }
+
+    children.erase(std::remove(children.begin(), children.end(), INDEX_FOR_REMOVAL), children.end());
+  }
+
+  return true;
+}
+
+void SceneDefinition::GetNodeModelStack(Index index, MatrixStack& model) const
+{
+  auto& thisNodes = mNodes;
+  std::function<void(int)> buildStack = [&model, &thisNodes, &buildStack](int i) {
+    auto node = thisNodes[i].get();
+    if (node->mParentIdx != INVALID_INDEX)
+    {
+      buildStack(node->mParentIdx);
+    }
+    model.Push(node->GetLocalSpace());
+  };
+  buildStack(index);
+}
+
+NodeDefinition* SceneDefinition::FindNode(const std::string &name, Index* outIndex)
+{
+  auto iBegin = mNodes.begin();
+  auto iEnd = mNodes.end();
+  auto iFind = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) {
+    return nd->mName == name;
+  });
+
+  auto result = iFind != iEnd ? iFind->get() : nullptr;
+  if (result && outIndex)
+  {
+    *outIndex = std::distance(iBegin, iFind);
+  }
+  return result;
+}
+
+const NodeDefinition* SceneDefinition::FindNode(const std::string &name, Index* outIndex) const
+{
+  auto iBegin = mNodes.begin();
+  auto iEnd = mNodes.end();
+  auto iFind = std::find_if(iBegin, iEnd, [&name](const std::unique_ptr<NodeDefinition>& nd) {
+    return nd->mName == name;
+  });
+
+  auto result = iFind != iEnd ? iFind->get() : nullptr;
+  if (result && outIndex)
+  {
+    *outIndex = std::distance(iBegin, iFind);
+  }
+  return result;
+}
+
+Index SceneDefinition::FindNodeIndex(const NodeDefinition& node) const
+{
+  auto iBegin = mNodes.begin();
+  auto iEnd = mNodes.end();
+  auto iFind = std::find_if(iBegin, iEnd, [&node](const std::unique_ptr<NodeDefinition>& n) {
+    return n.get() == &node;
+  });
+  return iFind != iEnd ? std::distance(iBegin, iFind) : INVALID_INDEX;
+}
+
+void SceneDefinition::FindNodes(NodePredicate predicate, NodeConsumer consumer,
+  unsigned int limit)
+{
+  unsigned int n = 0;
+  for (auto& defp : mNodes)
+  {
+    if (predicate(*defp))
+    {
+      consumer(*defp);
+      ++n;
+      if (n == limit)
+      {
+        break;
+      }
+    }
+  }
+}
+
+void SceneDefinition::FindNodes(NodePredicate predicate, ConstNodeConsumer consumer,
+  unsigned int limit) const
+{
+  unsigned int n = 0;
+  for (auto& defp : mNodes)
+  {
+    if (predicate(*defp))
+    {
+      consumer(*defp);
+      ++n;
+      if (n == limit)
+      {
+        break;
+      }
+    }
+  }
+}
+
+void SceneDefinition::ApplyConstraints(Actor& root,
+  std::vector<ConstraintRequest>&& constrainables, StringCallback onError) const
+{
+  for (auto& cr : constrainables)
+  {
+    auto& nodeDef = mNodes[cr.mConstraint->mSourceIdx];
+    auto sourceName = nodeDef->mName.c_str();
+    Property::Index iTarget = cr.mTarget.GetPropertyIndex(cr.mConstraint->mProperty);
+    if (iTarget != Property::INVALID_INDEX)
+    {
+      auto propertyType = cr.mTarget.GetPropertyType(iTarget);
+      auto iFind = sConstraintFactory.find(propertyType);
+      if (iFind == sConstraintFactory.end())
+      {
+        onError(FormatString("node '%s': Property '%s' has unsupported type '%s'; ignored.",
+          sourceName, cr.mConstraint->mProperty.c_str(), PropertyTypes::GetName(propertyType)));
+        continue;
+      }
+
+      Constraint constraint = iFind->second(cr.mTarget, iTarget);
+
+      Actor source = root.FindChildByName(nodeDef->mName);
+      if (!source)
+      {
+        auto targetName = cr.mTarget.GetProperty(Actor::Property::NAME).Get<std::string>();
+        onError(FormatString("node '%s': Failed to locate constraint source %s@%s; ignored.",
+          sourceName, cr.mConstraint->mProperty.c_str(), targetName.c_str()));
+        continue;
+      }
+      else if (source == cr.mTarget)
+      {
+        onError(FormatString("node '%s': Cyclic constraint definition for property '%s'; ignored.",
+          sourceName, cr.mConstraint->mProperty.c_str()));
+        continue;
+      }
+
+      Property::Index iSource = source.GetPropertyIndex(cr.mConstraint->mProperty);
+      constraint.AddSource(Source{ source, iSource });
+      constraint.Apply();
+    }
+    else
+    {
+      auto targetName = cr.mTarget.GetProperty(Actor::Property::NAME).Get<std::string>();
+      onError(FormatString("node '%s': Failed to create constraint for property %s@%s; ignored.",
+        sourceName, cr.mConstraint->mProperty.c_str(), targetName.c_str()));
+    }
+  }
+}
+
+void SceneDefinition::ConfigureSkeletonJoints(uint32_t iRoot, const SkeletonDefinition::Vector& skeletons, Actor root) const
+{
+  // 1, For each skeleton, for each joint, walk upwards until we reach mNodes[iRoot]. If we do, record +1
+  // to the refcount of each node we have visited, in our temporary registry. Those with refcount 1
+  // are the leaves, while the most descendant node with the highest refcount is the root of the skeleton.
+  std::map<Index, std::vector<Index>> rootsJoints;
+  std::vector<Index> path;
+  path.reserve(16);
+  for (auto& s : skeletons)
+  {
+    std::map<uint32_t, uint32_t>  jointRefs;
+    for (auto& j : s.mJoints)
+    {
+      auto nodeIdx = j.mNodeIdx;
+      do // Traverse upwards and record each node we have visited until we reach the scene root.
+      {
+        path.push_back(nodeIdx);
+        if (nodeIdx == iRoot)
+        {
+          break;
+        }
+        auto node = GetNode(nodeIdx);
+        nodeIdx = node->mParentIdx;
+      }
+      while (nodeIdx != INVALID_INDEX);
+
+      if (nodeIdx == iRoot)  // If the joint is in the correct scene, increment the reference count for all visited nodes.
+      {
+        for (auto i : path)
+        {
+          ++jointRefs[i];
+        }
+      }
+
+      path.clear();
+    }
+
+    // Only record the skeleton if we have encountered the root of the current scene.
+    if (jointRefs.empty())
+    {
+      continue;
+    }
+
+    Index root = s.mRootNodeIdx;
+    uint32_t maxRef = 0;
+    auto iFind = jointRefs.find(root);
+    if (iFind != jointRefs.end())
+    {
+      maxRef = iFind->second;
+    }
+
+    std::vector<Index> joints;
+    for (auto& j : jointRefs)  // NOTE: jointRefs are sorted, so joints will also be.
+    {
+      // The most descendant node with the highest ref count is the root of the skeleton.
+      if (j.second > maxRef || (j.second == maxRef && IsAncestor(*this, root, j.first, iRoot)))
+      {
+        maxRef = j.second;
+
+        RemoveFromSorted(joints, root);
+        root = j.first;
+      }
+      else if (j.second == 1)  // This one's a leaf.
+      {
+        InsertUniqueSorted(joints, j.first);
+      }
+    }
+
+    // Merge skeletons that share the same root.
+    auto& finalJoints = rootsJoints[root];
+    for (auto j : joints)
+    {
+      if (std::find_if(finalJoints.begin(), finalJoints.end(), [this, j, root](Index jj) {
+        return IsAncestor(*this, j, jj, root);
+      }) != finalJoints.end())
+      {
+        continue;  // if the joint is found to be an ancestor of another joint already registered, move on.
+      }
+
+      auto i = j;
+      while (i != root)  // See if the current joint is a better leaf, i.e. descended from another leaf - which we'll then remove.
+      {
+        auto node = GetNode(i);
+        i = node->mParentIdx;
+
+        RemoveFromSorted(finalJoints, i);
+      }
+
+      InsertUniqueSorted(finalJoints, j);
+    }
+  }
+
+  // 2, Merge records where one root joint is descendant of another. Handle leaf node changes - remove previous
+  // leaf nodes that now have descendants, and add new ones.
+  auto iRoots = rootsJoints.begin();
+  auto iRootsEnd = rootsJoints.end();
+  while (iRoots != iRootsEnd)
+  {
+    auto i = iRoots->first;
+    bool merged = false;
+    while (i != iRoot)  // Starting with the root joint of the skeleton, traverse upwards.
+    {
+      auto node = GetNode(i);
+      i = node->mParentIdx;
+
+      auto iFind = rootsJoints.find(i);
+      if (iFind != rootsJoints.end())  // Check if we've reached the root of another skeleton.
+      {
+        // Now find out which leaf of iFind is an ancestor, if any.
+        auto iFindLeaf = std::find_if(iFind->second.begin(), iFind->second.end(), [this, iRoots, iFind](Index j) {
+          return IsAncestor(*this, j, iRoots->first, iFind->first);
+        });
+        if (iFindLeaf != iFind->second.end())
+        {
+          iFind->second.erase(iFindLeaf);  // Will no longer be a leaf -- remove it.
+        }
+
+        // Merge iRoots with iFind
+        auto& targetJoints = iFind->second;
+        if (iRoots->second.empty())  // The root is a leaf.
+        {
+          InsertUniqueSorted(targetJoints, iRoots->first);
+        }
+        else for (auto j : iRoots->second)
+        {
+          InsertUniqueSorted(targetJoints, j);
+        }
+
+        merged = true;
+        break;  // Traverse no more
+      }
+    }
+
+    iRoots = merged ? rootsJoints.erase(iRoots) : std::next(iRoots);
+  }
+
+  // 3, For each root, register joint matrices and constraints
+  for (auto r : rootsJoints)
+  {
+    auto node = GetNode(r.first);
+    auto rootJoint = root.FindChildByName(node->mName);
+    DALI_ASSERT_ALWAYS(!!rootJoint);
+
+    DALI_ASSERT_DEBUG(rootJoint.GetPropertyIndex(JOINT_MATRIX) == Property::INVALID_INDEX);
+    auto propJointMatrix = rootJoint.RegisterProperty(JOINT_MATRIX, Matrix{ false });
+    Constraint constraint = Constraint::New<Matrix>(rootJoint, propJointMatrix,
+      [](Matrix& output, const PropertyInputContainer& inputs)
+      {
+        output.SetTransformComponents(Vector3::ONE, inputs[0]->GetQuaternion(), inputs[1]->GetVector3());
+      });
+    constraint.AddSource(Source(rootJoint, Actor::Property::ORIENTATION));
+    constraint.AddSource(Source(rootJoint, Actor::Property::POSITION));
+    constraint.Apply();
+
+    for (auto j : r.second)
+    {
+      node = GetNode(j);
+      auto joint = rootJoint.FindChildByName(node->mName);
+      ConfigureJointMatrix(joint, rootJoint, propJointMatrix);
+    }
+  }
+}
+
+void SceneDefinition::EnsureUniqueSkinningShaderInstances(ResourceBundle& resources) const
+{
+  std::map<Index, std::map<Index, std::vector<Index*>>> skinningShaderUsers;
+  for (auto& node : mNodes)
+  {
+    if (node->mRenderable)
+    {
+      ResourceReflector reflector;
+      node->mRenderable->ReflectResources(reflector);
+
+      if (reflector.iMesh)
+      {
+        const auto& mesh = resources.mMeshes[*reflector.iMesh].first;
+        if (mesh.IsSkinned())
+        {
+          skinningShaderUsers[*reflector.iShader][mesh.mSkeletonIdx].push_back(reflector.iShader);
+        }
+      }
+    }
+  }
+
+  // For each shader, and each skeleton using the same shader as the first skeleton,
+  // update the shader references (from nodes with skinned meshes) with a new copy of
+  // the shader definition from the node using the first skeleton.
+  for (auto& users : skinningShaderUsers)
+  {
+    auto& skeletons = users.second;
+    auto iterSkeleton = skeletons.begin();
+    // skipping the first skeleton.
+    ++iterSkeleton;
+
+    resources.mShaders.reserve(resources.mShaders.size() + std::distance(iterSkeleton, skeletons.end()));
+    const ShaderDefinition& shaderDef = resources.mShaders[users.first].first;
+
+    while (iterSkeleton != skeletons.end())
+    {
+      Index iShader = resources.mShaders.size();
+      resources.mShaders.push_back({ shaderDef, Shader() });
+
+      for (auto& i : iterSkeleton->second)
+      {
+        *i = iShader;
+      }
+      ++iterSkeleton;
+    }
+  }
+}
+
+void SceneDefinition::ConfigureSkinningShaders(const ResourceBundle& resources,
+  Actor rootActor, std::vector<SkinningShaderConfigurationRequest>&& requests) const
+{
+  if (requests.empty())
+  {
+    return;
+  }
+
+  SortAndDeduplicateSkinningRequests(requests);
+
+  for (auto& i : requests)
+  {
+    auto& skeleton = resources.mSkeletons[i.mSkeletonIdx];
+    if (skeleton.mJoints.empty())
+    {
+      LOGD(("Skeleton %d has no joints.", i.mSkeletonIdx));
+      continue;
+    }
+
+    Index boneIdx = 0;
+    for (auto& j : skeleton.mJoints)
+    {
+      auto node = GetNode(j.mNodeIdx);
+      Actor actor = rootActor.FindChildByName(node->mName);
+      ConfigureBoneMatrix(j.mInverseBindMatrix, actor, i.mShader, boneIdx);
+    }
+  }
+}
+
+bool SceneDefinition::ConfigureBlendshapeShaders(const ResourceBundle& resources,
+  Actor rootActor, std::vector<BlendshapeShaderConfigurationRequest>&& requests,
+  StringCallback onError ) const
+{
+  if (requests.empty())
+  {
+    return true;
+  }
+
+  // Sort requests by shaders.
+  std::sort(requests.begin(), requests.end());
+
+  // Remove duplicates.
+  auto i = requests.begin();
+  auto iEnd = requests.end();
+  Shader s = i->mShader;
+  ++i;
+  do
+  {
+    // Multiple identical shader instances are removed.
+    while (i != iEnd && i->mShader == s)
+    {
+      i->mShader = Shader();
+      ++i;
+    }
+
+    if (i == iEnd)
+    {
+      break;
+    }
+    s = i->mShader;
+    ++i;
+  } while (true);
+
+  requests.erase(std::remove_if(requests.begin(), requests.end(), [](const BlendshapeShaderConfigurationRequest& bscr)
+  {
+    return !bscr.mShader;
+  }), requests.end());
+
+  // Configure the rest.
+  bool ok = true;
+
+  for (auto& i : requests)
+  {
+    Index iNode;
+    if (FindNode(i.mNodeName, &iNode))
+    {
+      const auto& node = GetNode(iNode);
+
+      const auto& mesh = resources.mMeshes[i.mMeshIdx];
+
+      if (mesh.first.HasBlendShapes())
+      {
+        Actor actor = rootActor.FindChildByName(node->mName);
+
+        // Sets the property to be animated.
+        BlendShapes::ConfigureProperties(mesh, i.mShader, actor);
+      }
+    }
+  }
+
+  return ok;
+}
+
+void SceneDefinition::EnsureUniqueBlendShapeShaderInstances(ResourceBundle& resources) const
+{
+  std::map<Index, std::map<std::string, std::vector<Index*>>> blendShapeShaderUsers;
+  for (auto& node : mNodes)
+  {
+    if (node->mRenderable)
+    {
+      ResourceReflector reflector;
+      node->mRenderable->ReflectResources(reflector);
+
+      if (reflector.iMesh)
+      {
+        const auto& mesh = resources.mMeshes[*reflector.iMesh].first;
+        if (mesh.HasBlendShapes())
+        {
+          blendShapeShaderUsers[*reflector.iShader][node->mName].push_back(reflector.iShader);
+        }
+      }
+    }
+  }
+
+  for (auto& users : blendShapeShaderUsers)
+  {
+    resources.mShaders.reserve(resources.mShaders.size() + users.second.size() - 1u);
+    const ShaderDefinition& shaderDef = resources.mShaders[users.first].first;
+
+    auto nodesIt = users.second.begin();
+    auto nodesEndIt = users.second.end();
+    // skipping the first node.
+    ++nodesIt;
+    while(nodesIt != nodesEndIt)
+    {
+      Index iShader = resources.mShaders.size();
+      resources.mShaders.push_back({ shaderDef, Shader() });
+
+      auto& nodes = *nodesIt;
+      for (auto& shader : nodes.second)
+      {
+        *shader = iShader;
+      }
+      ++nodesIt;
+    }
+  }
+}
+
+SceneDefinition& SceneDefinition::operator=(SceneDefinition&& other)
+{
+  SceneDefinition temp(std::move(other));
+  std::swap(mNodes, temp.mNodes);
+  std::swap(mRootNodeIds, temp.mRootNodeIds);
+  return *this;
+}
+
+bool SceneDefinition::FindNode(const std::string& name, std::unique_ptr<NodeDefinition>** result)
+{
+  // We're searching from the end assuming a higher probability of operations targeting
+  // recently added nodes. (conf.: root, which is immovable, cannot be removed, and was
+  // the first to be added, is index 0.)
+  auto iFind = std::find_if(mNodes.rbegin(), mNodes.rend(),
+    [&name](const std::unique_ptr<NodeDefinition>& nd) {
+      return nd->mName == name;
+    }).base();
+
+  const bool success = iFind != mNodes.begin();
+  if (success && result)
+  {
+    --iFind;
+    *result = &*iFind;
+  }
+
+  return success;
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/scene-definition.h b/dali-scene-loader/public-api/scene-definition.h
new file mode 100644 (file)
index 0000000..66ea758
--- /dev/null
@@ -0,0 +1,278 @@
+#ifndef DALI_SCENE_LOADER_SCENE_DEFINITION_H_
+#define DALI_SCENE_LOADER_SCENE_DEFINITION_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/customization.h"
+#include "dali-scene-loader/public-api/utils.h"
+#include "dali-scene-loader/public-api/node-definition.h"
+#include "dali-scene-loader/public-api/string-callback.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/math/quaternion.h"
+#include "dali/public-api/math/matrix.h"
+#include "dali/public-api/math/vector4.h"
+#include "dali/public-api/actors/actor.h"
+#include <string>
+#include <memory>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+class MatrixStack;
+
+/*
+ * @brief Intermediate representation of a scene with functionality required to
+ *  create DALi objects (Actors, Renderers) from it.
+ */
+class DALI_SCENE_LOADER_API SceneDefinition
+{
+public:  // TYPES
+  using NodePredicate = std::function<bool(const NodeDefinition&)>;
+  using NodeConsumer = std::function<void(NodeDefinition&)>;
+  using ConstNodeConsumer = std::function<void(const NodeDefinition&)>;
+
+public: // METHODS
+  SceneDefinition();
+  SceneDefinition(SceneDefinition&& other);
+  ~SceneDefinition();
+
+  /*
+   * @brief Registers a scene root node.
+   * @return The index of the scene root node *registration*.
+   */
+  Index AddRootNode(Index iNode);
+
+  /*
+   * @return the list of scene root node IDs in the order of their loading.
+   */
+  const std::vector<Index>& GetRoots() const;
+
+  /*
+   * @brief Removes scene root registration at the given index @a iRoot.
+   * @note @a iRoot is the index of the registration (i.e. into the vector returned by GetRoots()),
+   *  not of the node.
+   */
+  void RemoveRootNode(Index iRoot);
+
+  /*
+   * @return The number of node( definition)s in the scene.
+   */
+  uint32_t GetNodeCount() const;
+
+  /*
+   * @return Const pointer to the node (definition) at the given index.
+   */
+  const NodeDefinition* GetNode(Index iNode) const;
+
+  /*
+   * @return Pointer to the node (definition) at the given index.
+   */
+  NodeDefinition* GetNode(Index iNode);
+
+  /*
+   * @brief Traverses the scene starting from the node at the given index into
+   *  nodes, using the given customization @a choices and the visitor @a v.
+   */
+  void Visit(Index iNode, const Customization::Choices& choices, NodeDefinition::IVisitor& v);
+
+  /*
+   * @brief Traverses the scene starting from the node at the given index into
+   *  nodes, using the given customization @a choices and the visitor @a v.
+   */
+  void Visit(Index iNode, const Customization::Choices& choices, NodeDefinition::IConstVisitor& v) const;
+
+  /*
+   * @brief Counts the references to meshes, shaders, materials that nodes in
+   *  the scene are holding, writing the results into @a refCounts.
+   * @note @a refCounts' entries must have the correct size. Use ResourceBundle::GetRefCounter().
+   */
+  void CountResourceRefs(Index iNode, const Customization::Choices& choices, ResourceRefCounts& refCounts) const;
+
+  /*
+   * @brief Given a bundle of @a resources that are loaded, and customization
+   *  @a choices, this method traverses the scene, creating the actors and renderers
+   *  from node definitions.
+   * @return Handle to the root actor.
+   */
+  Actor CreateNodes(Index iNode, const Customization::Choices& choices,
+    NodeDefinition::CreateParams& params) const;
+
+  /*
+   * @brief Creates / update a registry of mappings from customization tags to
+   *  a lists of names of customizable nodes under each tag, and the number of
+   *  options. If @a outMissingChoices was specified, each tag that it encounters
+   *  in the scene but not in @a choices, will be registered on it with the default
+   *  choice of 0.
+   */
+  void GetCustomizationOptions(const Customization::Choices& choices,
+    Customization::Map& outCustomizationOptions,
+    Customization::Choices* outMissingChoices) const;
+
+  /*
+   * @brief Attempts to add @a nodeDef to the end of nodes, and its index to the end of
+   *  its parent's list of children (if iParent != NodeDefinition::INVALID_PARENT).
+   * @return If the operation was successful - which requires nodeDef->name to be unique -
+   *  a pointer to the stored node definition; nullptr otherwise.
+   */
+  NodeDefinition* AddNode(std::unique_ptr<NodeDefinition>&& nodeDef);
+
+  /*
+   * @brief Moves the node to some other parent and / or to a different index.
+   * @return Whether the operation was successful.
+   * @note This is currently breaking an assumption of never having a child of a node at a lower
+   *  index as that of the node itself, due to the fact that we're only changing parent ids (and
+   *  entries into the vector of children of node definitions), to save the complication of having
+   *  to move about, and offset indices to, everything past the reparented node. This should be
+   *  sufficient AT LEAST AS LONG AS we recreate the SceneDefinition when loading the scene; if
+   *  we ever needed to serialize it, we should ensure correct ordering of nodes.
+   */
+  bool ReparentNode(const std::string& name, const std::string& newParentName, Index siblingOrder);
+
+  /*
+   * @brief Removes a node with the given name, including all of its children, and updating
+   *  the indices on all remaining node definitions.
+   * @return Whether the operation was successful.
+   */
+  bool RemoveNode(const std::string& name);
+
+  /*
+   * @brief Builds the model matrix stack for the node at the given @a index.
+   * @note It only pushes new matrices; does not require the stack to be empty (or cares if it was not).
+   */
+  void GetNodeModelStack(Index index, MatrixStack& model) const;
+
+  /*
+   * @brief Attempts to find the definition of a node with the given @a name. Only upon
+   *  success, and if @a outIndex is non-null, the index of the node is written to it.
+   * @return Pointer to the node definition; nullptr if not found.
+   * @note No ownership transfer.
+   */
+  NodeDefinition* FindNode(const std::string& name, Index* outIndex = nullptr);
+
+  /*
+   * @brief Attempts to find the definition of a node with the given @a name. Only upon
+   *  success, and if @a outIndex is non-null, the index of the node is written to it.
+   * @return Pointer to the node definition; nullptr if not found.
+   * @note No ownership transfer.
+   */
+  const NodeDefinition* FindNode(const std::string& name, Index* outIndex = nullptr) const;
+
+  /*
+   * @return The index of the given NodeDefinition@ a node, or -1 if the node definition
+   *  was not found.
+   */
+  Index FindNodeIndex(const NodeDefinition& node) const;
+
+  /*
+   * @brief Calls @a consumer with up to @a limit NodeDefinitions that evaluate to true
+   *  with @a predicate.
+   * @note A @a limit value of 0 means no limit.
+   */
+  void FindNodes(NodePredicate predicate, NodeConsumer consumer, unsigned int limit = 0);
+
+  /*
+   * @brief Calls @a consumer with up to @a limit NodeDefinitions that evaluate to true
+   *  with @a predicate.
+   * @note A @a limit value of 0 means no limit.
+   */
+  void FindNodes(NodePredicate predicate, ConstNodeConsumer consumer, unsigned int limit = 0) const;
+
+  /*
+   * @brief Applies constraints from the given requests.
+   */
+  void ApplyConstraints(Actor& root,
+    std::vector<ConstraintRequest>&& constrainables,
+    StringCallback onError = DefaultErrorCallback) const;
+
+  /*
+   * @brief Sets up joint matrix properties and constraints on actors that are involved in skeletal
+   *  animation (i.e. those that are between (inclusive) the lower and upper bounds of any skeleton),
+   *  to ensure the correct update of meshes skinned to these skeletons.
+   * @param iRoot The index of the scene root node. Skeletons that aren't descendants of this node
+   *  will be ignored.
+   * @param skeletons The list of skeletons that require setting up.
+   * @param rootActor The Actor corresponding to the root node, which will be used to locate
+   *  other actors.
+   */
+  void ConfigureSkeletonJoints(uint32_t iRoot, const SkeletonDefinition::Vector& skeletons, Actor rootActor) const;
+
+  /*
+   * @brief Ensures that there is no overlap between shaders used by nodes that have
+   *  meshes skinned to different skeletons.
+   */
+  void EnsureUniqueSkinningShaderInstances(ResourceBundle& resources) const;
+
+  /*
+   * @brief Performs the configuration of the given skinning shaders with the given skeleton
+   *   This means that the absolute transforms of the joints are calculated and set as one of
+   *   the uniforms in the mat4 @b uBone array (in depth first traversal). Further, the following
+   *   are created:<br />
+   *   - a @b jointMatrix property on each joint Actor;<br />
+   *   - constraint from the Actor's local position and rotation (and if it has a @e joint
+   *     parent, the jointMatrix of the parent) to its @b jointMatrix property;<br />
+   *   - a constraint from the the Actor's @b jointMatrix property to the related entry in
+   *     the shader's @b uBone property;<br />
+   *   This ensures the automatic update of the skeletal animation, should any of the joints'
+   *   transform changes, by whatever means.
+   * @return The success of the operations. Error messages will be posted to the optional
+   *   @a onError callback.
+   * @note A maximum of SkinningDetails::MAX_JOINTS joints per skeleton are supported at the moment.
+   * @note Even if multiple skinned meshes use the same skinning shader, the correct number
+   *   of separate instances need to be declared in the .dli to avoid clashing uniform
+   *   definitions and constraints.
+   */
+  void ConfigureSkinningShaders(const ResourceBundle& resources,
+    Actor root, std::vector<SkinningShaderConfigurationRequest>&& requests) const;
+
+  /*
+   * @brief Ensures there is no two meshes with blend shapes sharing the same shader.
+   */
+  void EnsureUniqueBlendShapeShaderInstances(ResourceBundle& resources) const;
+
+  /**
+   * @brief Performs the configuration of the given blend shapes.
+   *
+   * For each node with blend shapes it registers into the actor the weights properties for each morph target
+   * and some needed uniforms into the shader.
+   * 
+   * @param[in] root The root actor.
+   * @param[in] requests The requests to configure blend shapes.
+   * @param[in] resources The resources bundle. Meshes need to be accessed to configure the blend shapes.
+   * @param[in] onError The error callback.
+   */
+  bool ConfigureBlendshapeShaders(const ResourceBundle& resources,
+    Actor root, std::vector<BlendshapeShaderConfigurationRequest>&& requests,
+    StringCallback onError = DefaultErrorCallback) const;
+
+  SceneDefinition& operator=(SceneDefinition&& other);
+
+private: // METHODS
+  bool FindNode(const std::string& name, std::unique_ptr<NodeDefinition>** result);
+
+private: // DATA
+  std::vector<std::unique_ptr<NodeDefinition>> mNodes;  // size unknown up front (may discard nodes).
+  std::vector<Index> mRootNodeIds;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_SCENE_DEFINITION_H_
diff --git a/dali-scene-loader/public-api/shader-definition-factory.cpp b/dali-scene-loader/public-api/shader-definition-factory.cpp
new file mode 100644 (file)
index 0000000..f0a2ad8
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/internal/hash.h"
+#include "dali-scene-loader/public-api/shader-definition-factory.h"
+#include "dali-scene-loader/public-api/node-definition.h"
+#include "dali-scene-loader/public-api/blend-shape-details.h"
+#include "dali/devel-api/common/map-wrapper.h"
+#include <cstring>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+namespace
+{
+struct ResourceReceiver : IResourceReceiver
+{
+  const ResourceBundle& mResources;
+  const MeshDefinition* mMeshDef = nullptr;
+  const MaterialDefinition* mMaterialDef = nullptr;
+
+  ResourceReceiver(const ResourceBundle& resources)
+  : mResources(resources)
+  {}
+
+  void Register(ResourceType::Value type, Index id) override
+  {
+    switch (type)
+    {
+    case ResourceType::Mesh:
+      mMeshDef = &mResources.mMeshes[id].first;
+      break;
+
+    case ResourceType::Material:
+      mMaterialDef = &mResources.mMaterials[id].first;
+      break;
+
+    default:
+      break;
+    }
+  }
+};
+
+const std::string PBR_SHADER_NAME = "dli_pbr";
+
+void RetrieveBlendShapeComponents(const std::vector<MeshDefinition::BlendShape>& blendShapes, bool& hasPositions, bool& hasNormals, bool& hasTangents)
+{
+  for (const auto& blendShape : blendShapes)
+  {
+    hasPositions = hasPositions || blendShape.deltas.IsDefined();
+    hasNormals = hasNormals || blendShape.normals.IsDefined();
+    hasTangents = hasTangents || blendShape.tangents.IsDefined();
+  }
+}
+
+uint64_t HashNode(const NodeDefinition& nodeDef, const MaterialDefinition& materialDef, const MeshDefinition& meshDef)
+{
+  Hash hash;
+
+  // note: could be per vertex / fragment component - in WatchViewer, these have the same name.
+  hash.Add(PBR_SHADER_NAME);
+
+  const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
+  hash.Add(hasTransparency);
+
+  if (hasTransparency ||
+    materialDef.CheckTextures(MaterialDefinition::ALBEDO) ||
+    materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS) ||
+    materialDef.CheckTextures(MaterialDefinition::NORMAL))
+  {
+    hash.Add("3TEX");
+  }
+
+  if (materialDef.GetAlphaCutoff() > 0.f)
+  {
+    hash.Add("ALPH"/*A_TEST*/);
+  }
+
+  if (MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
+  {
+    hash.Add("SSS");
+  }
+
+  if (MaskMatch(materialDef.mFlags, MaterialDefinition::GLTF_CHANNELS))
+  {
+    hash.Add("GLTF"/*_CHANNELS*/);
+  }
+
+  if (meshDef.IsSkinned())
+  {
+    hash.Add("SKIN"/*NING*/);
+  }
+
+  if (MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
+  {
+    hash.Add("FLIP"/*_V*/);
+  }
+
+  if (meshDef.HasBlendShapes())
+  {
+    bool hasPositions = false;
+    bool hasNormals = false;
+    bool hasTangents = false;
+    RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
+    if (hasPositions)
+    {
+      hash.Add("MORPHPOS");
+    }
+
+    if (hasNormals)
+    {
+      hash.Add("MORPHNOR");
+    }
+
+    if (hasTangents)
+    {
+      hash.Add("MORPHTAN");
+    }
+
+    if (hasPositions || hasNormals || hasTangents)
+    {
+      hash.Add("MORPH");
+
+      if (BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
+      {
+        hash.Add("MORPHV2");
+      }
+    }
+  }
+
+  return hash;
+}
+}
+
+struct ShaderDefinitionFactory::Impl
+{
+  ResourceBundle* mResources;  // no ownership
+  std::map<uint64_t, Index> mShaderMap;
+};
+
+ShaderDefinitionFactory::ShaderDefinitionFactory()
+: mImpl{ new Impl() }
+{}
+
+ShaderDefinitionFactory::~ShaderDefinitionFactory() = default;
+
+void ShaderDefinitionFactory::SetResources(ResourceBundle& resources)
+{
+  mImpl->mResources = &resources;
+  mImpl->mShaderMap.clear();
+}
+
+Index ShaderDefinitionFactory::ProduceShader(const NodeDefinition& nodeDef)
+{
+  DALI_ASSERT_DEBUG(nodeDef.mRenderable);
+
+  auto& resources = *mImpl->mResources;
+  ResourceReceiver receiver{ resources };
+  nodeDef.mRenderable->RegisterResources(receiver);
+  if (!(receiver.mMeshDef && receiver.mMaterialDef))
+  {
+    return INVALID_INDEX;
+  }
+
+  auto& shaderMap = mImpl->mShaderMap;
+  uint64_t hash = HashNode(nodeDef, *receiver.mMaterialDef, *receiver.mMeshDef);
+  auto iFind = shaderMap.find(hash);
+  if (iFind != shaderMap.end())
+  {
+    return iFind->second;
+  }
+
+  ShaderDefinition shaderDef;
+  shaderDef.mVertexShaderPath = PBR_SHADER_NAME + ".vsh";
+  shaderDef.mFragmentShaderPath = PBR_SHADER_NAME + ".fsh";
+  shaderDef.mRendererState = RendererState::DEPTH_TEST | RendererState::DEPTH_WRITE | RendererState::CULL_BACK;
+
+  auto& materialDef = *receiver.mMaterialDef;
+  const bool hasTransparency = MaskMatch(materialDef.mFlags, MaterialDefinition::TRANSPARENCY);
+  if (hasTransparency)
+  {
+    // TODO: this requires more granularity
+    shaderDef.mRendererState = (shaderDef.mRendererState | RendererState::ALPHA_BLEND) & ~RendererState::DEPTH_WRITE;
+  }
+
+  if (hasTransparency ||
+    materialDef.CheckTextures(MaterialDefinition::ALBEDO) ||
+    materialDef.CheckTextures(MaterialDefinition::METALLIC | MaterialDefinition::ROUGHNESS) ||
+    materialDef.CheckTextures(MaterialDefinition::NORMAL))
+  {
+    shaderDef.mDefines.push_back("THREE_TEX");
+  }
+
+  if (materialDef.GetAlphaCutoff() > 0.f)
+  {
+    shaderDef.mDefines.push_back("ALPHA_TEST");
+  }
+
+  if (MaskMatch(materialDef.mFlags, MaterialDefinition::SUBSURFACE))
+  {
+    shaderDef.mDefines.push_back("SSS");
+  }
+
+  if (MaskMatch(receiver.mMaterialDef->mFlags, MaterialDefinition::GLTF_CHANNELS))
+  {
+    shaderDef.mDefines.push_back("GLTF_CHANNELS");
+  }
+
+  const auto& meshDef = *receiver.mMeshDef;
+  if (meshDef.IsSkinned())
+  {
+    shaderDef.mDefines.push_back("SKINNING");
+  }
+
+  if (MaskMatch(meshDef.mFlags, MeshDefinition::FLIP_UVS_VERTICAL))
+  {
+    shaderDef.mDefines.push_back("FLIP_V");
+  }
+
+  if (meshDef.HasBlendShapes())
+  {
+    bool hasPositions = false;
+    bool hasNormals = false;
+    bool hasTangents = false;
+    RetrieveBlendShapeComponents(meshDef.mBlendShapes, hasPositions, hasNormals, hasTangents);
+
+    if (hasPositions)
+    {
+      shaderDef.mDefines.push_back("MORPH_POSITION");
+    }
+
+    if (hasNormals)
+    {
+      shaderDef.mDefines.push_back("MORPH_NORMAL");
+    }
+
+    if (hasTangents)
+    {
+      shaderDef.mDefines.push_back("MORPH_TANGENT");
+    }
+
+    if (hasPositions || hasNormals || hasTangents)
+    {
+      shaderDef.mDefines.push_back("MORPH");
+
+      if (BlendShapes::Version::VERSION_2_0 == meshDef.mBlendShapeVersion)
+      {
+        shaderDef.mDefines.push_back("MORPH_VERSION_2_0");
+      }
+    }
+  }
+
+  shaderDef.mUniforms["uMaxLOD"] = 6.f;
+  shaderDef.mUniforms["uCubeMatrix"] = Matrix::IDENTITY;
+
+  Index result = resources.mShaders.size();
+  shaderMap[hash] = result;
+
+  resources.mShaders.emplace_back(std::move(shaderDef), Shader());
+
+  return result;
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/shader-definition-factory.h b/dali-scene-loader/public-api/shader-definition-factory.h
new file mode 100644 (file)
index 0000000..4f55bbe
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef DALI_SCENE_LOADER_SHADER_DEFINITION_FACTORY_H_
+#define DALI_SCENE_LOADER_SHADER_DEFINITION_FACTORY_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/index.h"
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDER
+#include <memory>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+struct NodeDefinition;
+class ResourceBundle;
+
+class DALI_SCENE_LOADER_API ShaderDefinitionFactory
+{
+public:
+  ShaderDefinitionFactory();
+  ~ShaderDefinitionFactory();
+
+  /*
+   * @brief Input for meshes and materials, output for shaders.
+   */
+  void SetResources(ResourceBundle& resources);
+
+  /*
+   * @brief Produces the index of a shader, which should be used to index into the shaders
+   *  vector of the ResourceBundle which was provided for the factory. This shader will be
+   *  created if one with the given settings hasn't been created by the factory yet (shaders
+   *  already existing in the ResourceBundle are ignored), otherwise the index of the previously
+   *  created shader will be returned.
+   */
+  Index ProduceShader(const NodeDefinition& nodeDef);
+
+private:
+  struct Impl;
+  const std::unique_ptr<Impl> mImpl;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_SHADER_DEFINITION_FACTORY_H_
diff --git a/dali-scene-loader/public-api/shader-definition.cpp b/dali-scene-loader/public-api/shader-definition.cpp
new file mode 100644 (file)
index 0000000..5fbec33
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/shader-definition.h"
+#include "dali-scene-loader/public-api/utils.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+namespace
+{
+
+const std::string SHADER_HINT_OUTPUT_IS_TRANSPARENT("OUTPUT_IS_TRANSPARENT"); ///< Might generate transparent alpha from opaque inputs.
+const std::string SHADER_HINT_MODIFIES_GEOMETRY("MODIFIES_GEOMETRY");     ///< Might change position of vertices, this option disables any culling optimizations.
+
+}
+
+ShaderDefinition::ShaderDefinition(const ShaderDefinition& other)
+: mRendererState(other.mRendererState),
+  mVertexShaderPath(other.mVertexShaderPath),
+  mFragmentShaderPath(other.mFragmentShaderPath),
+  mDefines(other.mDefines),
+  mHints(other.mHints),
+  mUniforms(other.mUniforms)
+{}
+
+void ShaderDefinition::ApplyDefine(std::string& shaderCode, const std::string& definevar)
+{
+  const std::string IF_1 = "#if 1";
+
+  std::size_t found = shaderCode.find(definevar);
+  while (found != std::string::npos)
+  {
+    // Greater then "@" character means is a letter,
+    // therefore is not has the definevar we looking for.
+    if ((found + definevar.length()) < shaderCode.length() && shaderCode.at(found + definevar.length()) > '@')
+    {
+      found = shaderCode.find(definevar, found + definevar.length());
+      continue;
+    }
+    if (found > 0u && shaderCode.at(found - 1) > '@')
+    {
+      found = shaderCode.find(definevar, found + definevar.length());
+      continue;
+    }
+
+    std::size_t defidx = shaderCode.rfind("#ifdef", found);
+    std::size_t newlineidx = shaderCode.rfind("\n", found);
+    if (newlineidx < defidx && defidx != std::string::npos)
+    {
+      shaderCode.replace(defidx, found - defidx + definevar.length(), IF_1);
+      found = defidx + IF_1.length();
+    }
+    else
+    {
+      found += definevar.length();
+    }
+    found = shaderCode.find(definevar, found);
+  }
+}
+
+ShaderDefinition::RawData
+  ShaderDefinition::LoadRaw(const std::string& shadersPath) const
+{
+  RawData raw;
+
+  bool fail = false;
+  raw.mVertexShaderSource = LoadTextFile((shadersPath + mVertexShaderPath).c_str(), &fail);
+  if (!fail)
+  {
+    raw.mFragmentShaderSource = LoadTextFile((shadersPath + mFragmentShaderPath).c_str(), &fail);
+    if (!fail)
+    {
+      for (auto definevar : mDefines)
+      {
+        ApplyDefine(raw.mVertexShaderSource, definevar);
+        ApplyDefine(raw.mFragmentShaderSource, definevar);
+      }
+    }
+    else
+    {
+      ExceptionFlinger(ASSERT_LOCATION) << "Failed to load shader source from '" <<
+        shadersPath + mFragmentShaderPath << "'.";
+    }
+  }
+  else
+  {
+    ExceptionFlinger(ASSERT_LOCATION) << "Failed to load shader source from '" <<
+      shadersPath + mVertexShaderPath << "'.";
+  }
+  return raw;
+}
+
+Shader ShaderDefinition::Load(RawData&& raw) const
+{
+  uint32_t hints = Shader::Hint::NONE;
+  for (const auto& hint : mHints)
+  {
+    if (hint == SHADER_HINT_OUTPUT_IS_TRANSPARENT)
+    {
+      hints |= Shader::Hint::OUTPUT_IS_TRANSPARENT;
+    }
+    else if (hint == SHADER_HINT_MODIFIES_GEOMETRY)
+    {
+      hints |= Shader::Hint::MODIFIES_GEOMETRY;
+    }
+  }
+
+  Shader shader = Shader::New(raw.mVertexShaderSource, raw.mFragmentShaderSource,
+    static_cast<Shader::Hint::Value>(hints));
+  for (Property::Map::SizeType i0 = 0, i1 = mUniforms.Count(); i0 != i1; ++i0)
+  {
+    auto pair = mUniforms.GetKeyValue(i0);
+    DALI_ASSERT_ALWAYS(pair.first.type == Property::Key::STRING);
+    shader.RegisterProperty(pair.first.stringKey, pair.second);
+  }
+
+  return shader;
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/shader-definition.h b/dali-scene-loader/public-api/shader-definition.h
new file mode 100644 (file)
index 0000000..29693fb
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef DALI_SCENE_LOADER_SHADER_DEFINITION_H
+#define DALI_SCENE_LOADER_SHADER_DEFINITION_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/renderer-state.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/common/vector-wrapper.h"
+#include "dali/public-api/rendering/shader.h"
+#include <memory>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/*
+ * @brief Defines a shader with paths to the files which define its
+ *  vertex and fragment components, and a mapping of uniform names (which are
+ *  used to refer to them in GLSL) to data.
+ */
+struct DALI_SCENE_LOADER_API ShaderDefinition
+{
+  using Vector = std::vector<std::pair<ShaderDefinition, Shader>>;
+
+  struct RawData
+  {
+    std::string mVertexShaderSource;
+    std::string mFragmentShaderSource;
+  };
+
+  /*
+   * @brief Apply the defines values to shader.
+   */
+  static void ApplyDefine(std::string& shaderCode, const std::string& definevar);
+
+  ShaderDefinition() = default;
+
+  ShaderDefinition(const ShaderDefinition& other);
+  ShaderDefinition& operator=(const ShaderDefinition& other);
+
+  ShaderDefinition(ShaderDefinition&&) = default;
+  ShaderDefinition& operator=(ShaderDefinition&&) = default;
+
+  /*
+   * @brief Attempts to load the source of the vertex and fragment shaders,
+   *  then performs pre-processing of defines.
+   * @note This may be called from any thread.
+   */
+  RawData LoadRaw(const std::string& shadersPath) const;
+
+  /*
+   * @brief Creates a DALi Shader from the sources in @a raw, traverses
+   *  uniforms to get them to register their data against their name,
+   *  then returns the Shader.
+   * @note This must be called from the event thread.
+   */
+  Shader Load(RawData&& raw) const;
+
+public: // DATA
+  RendererState::Type mRendererState = RendererState::NONE;
+
+  std::string mVertexShaderPath;
+  std::string mFragmentShaderPath;
+  std::vector<std::string> mDefines;
+  std::vector<std::string> mHints;
+
+  Property::Map mUniforms;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_SHADER_DEFINITION_H
diff --git a/dali-scene-loader/public-api/skeleton-definition.h b/dali-scene-loader/public-api/skeleton-definition.h
new file mode 100644 (file)
index 0000000..a06f19b
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef DALI_SCENE_LOADER_SKELETON_H
+#define DALI_SCENE_LOADER_SKELETON_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/index.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/common/vector-wrapper.h"
+#include "dali/public-api/math/matrix.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/*
+ * @brief A set of joints (stored as node indices), and an optional root node index.
+ * @note The list of joints must not be empty and must not contain INVALID_INDEX.
+ * @note Client code should not change the order of joints, as they are indexed by mesh vertex data.
+ * @note Client code should make no assumption about the relation of the joint IDs.
+ */
+struct DALI_SCENE_LOADER_API SkeletonDefinition
+{
+  struct Joint
+  {
+    Index mNodeIdx;
+    Matrix mInverseBindMatrix;
+  };
+
+  using Vector = std::vector<SkeletonDefinition>;
+
+  Index mRootNodeIdx = INVALID_INDEX;
+  std::vector<Joint> mJoints;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_SKELETON_H
diff --git a/dali-scene-loader/public-api/skinning-details.cpp b/dali-scene-loader/public-api/skinning-details.cpp
new file mode 100644 (file)
index 0000000..92ded02
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/public-api/skinning-details.h"
+#include "dali/public-api/rendering/shader.h"
+#include "dali/public-api/object/property.h"
+#include "dali/public-api/animation/constraints.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+const unsigned int Skinning::MAX_JOINTS = 64;
+
+const std::string Skinning::BONE_UNIFORM_NAME = "uBone";
+
+}
+}
diff --git a/dali-scene-loader/public-api/skinning-details.h b/dali-scene-loader/public-api/skinning-details.h
new file mode 100644 (file)
index 0000000..415d25a
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef DALI_SCENE_LOADER_SKINNING_DETAILS_H_
+#define DALI_SCENE_LOADER_SKINNING_DETAILS_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/rendering/shader.h"
+#include <string>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+struct DALI_SCENE_LOADER_API Skinning
+{
+  /*
+   * @brief Upper limit on the number of joints supported.
+   */
+  static const uint32_t MAX_JOINTS;
+
+  /*
+   * @brief Name of bone matrix uniform (array).
+   */
+  static const std::string BONE_UNIFORM_NAME;
+
+  Skinning() = delete;
+};
+
+}
+}
+
+#endif // DALI_SCENE_LOADER_SKINNING_DETAILS_H_
diff --git a/dali-scene-loader/public-api/string-callback.cpp b/dali-scene-loader/public-api/string-callback.cpp
new file mode 100644 (file)
index 0000000..d13fc15
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/public-api/string-callback.h"
+#include "dali/integration-api/debug.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+void DefaultErrorCallback(const std::string& message)
+{
+  DALI_LOG_ERROR("%s", message.c_str());
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/string-callback.h b/dali-scene-loader/public-api/string-callback.h
new file mode 100644 (file)
index 0000000..9569b2d
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef DALI_SCENE_LOADER_STRING_CALLBACK_H
+#define DALI_SCENE_LOADER_STRING_CALLBACK_H
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include <functional>
+#include <string>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/*
+ * @brief A callback to post strings to.
+ */
+using StringCallback = std::function<void(const std::string&)>;
+
+/*
+ * @brief Simply passes the formatted message to LOGE().
+ */
+DALI_SCENE_LOADER_API void DefaultErrorCallback(const std::string& message);
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_STRING_CALLBACK_H
diff --git a/dali-scene-loader/public-api/utils.cpp b/dali-scene-loader/public-api/utils.cpp
new file mode 100644 (file)
index 0000000..8b8132e
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL
+#include "dali-scene-loader/public-api/utils.h"
+
+// EXTERNAL
+#include "dali/public-api/common/vector-wrapper.h"
+#include "dali/public-api/animation/constraints.h"
+#include <iostream>
+#include <fstream>
+#include <cstring>
+#include <stdarg.h>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+namespace
+{
+thread_local char sExceptionFlingerMessageBuffer[ExceptionFlinger::MESSAGE_BUFFER_SIZE]{};
+}
+
+StreamBuffer::StreamBuffer(char* buffer, size_t size) noexcept(true)
+{
+  setp(buffer, buffer + size);
+}
+
+ExceptionFlinger::Impl::~Impl() noexcept(false)
+{
+  throw DaliException(mLocation, GetMessageBuffer());
+}
+
+ExceptionFlinger::ExceptionFlinger(const char* location) noexcept(true)
+: mImpl{ location },
+  mStreamBuffer(GetMessageBuffer(), MESSAGE_BUFFER_SIZE - 1),
+  mStream(&mStreamBuffer)
+{}
+
+ExceptionFlinger::~ExceptionFlinger() noexcept(false)
+{
+  operator<<('\0');
+}
+
+char* ExceptionFlinger::GetMessageBuffer() noexcept(true)
+{
+  return sExceptionFlingerMessageBuffer;
+}
+
+std::string FormatString(const char* format, ...)
+{
+  va_list vl;
+  va_start(vl, format);
+  va_list vl2;
+  va_copy(vl2, vl);
+
+  size_t sizeRequired = vsnprintf(nullptr, 0, format, vl);
+  va_end(vl);
+
+  ++sizeRequired;
+  std::string result(sizeRequired, '\0');
+  vsnprintf(&result[0], sizeRequired, format, vl2);
+  va_end(vl2);
+
+  return result;
+}
+
+std::string LoadTextFile(const char * path, bool* fail)
+{
+  std::ifstream inFile(path);
+  if (inFile)
+  {
+    std::istreambuf_iterator<char> eos;
+    std::istreambuf_iterator<char> i(inFile.rdbuf());
+    return std::string(i, eos);
+  }
+  else if (fail)
+  {
+    *fail = true;
+  }
+  return std::string();
+}
+
+Geometry MakeTexturedQuadGeometry(TexturedQuadOptions::Type options)
+{
+  Property::Map properties;
+  properties.Insert("aPosition", Property::VECTOR3);
+  properties.Insert("aTexCoord", Property::VECTOR2);
+
+  std::vector<uint8_t> bytes;
+  size_t stride = 0;
+  size_t uvOffset = 0;
+  struct
+  {
+    Vector3 aPosition;
+    Vector2 aTexCoord;
+  } vertices[] = {
+    { Vector3(-0.5f, 0.5f, 0.0f), Vector2(0.0f, .0f) },
+    { Vector3(0.5f, 0.5f, 0.0f), Vector2(1.0f, .0f) },
+    { Vector3(-0.5f, -0.5f, 0.0f), Vector2(0.0f, 1.0f) },
+    { Vector3(0.5f, -0.5f, 0.0f), Vector2(1.0f, 1.0f) }
+  };
+
+  bytes.resize(sizeof(vertices));
+  stride = sizeof(vertices[0]);
+  uvOffset = reinterpret_cast<const uint8_t*>(&vertices[0].aTexCoord) - reinterpret_cast<const uint8_t*>(&vertices[0]);
+
+  std::memcpy(bytes.data(), vertices, sizeof(vertices));
+
+  if (MaskMatch(options, TexturedQuadOptions::FLIP_VERTICAL))
+  {
+    Vector2* uv = reinterpret_cast<Vector2*>(reinterpret_cast<uint8_t*>(bytes.data()) + uvOffset);
+    for (int i = 0; i < 4; ++i)
+    {
+      uv->y = 1.0f - uv->y;
+      uv = reinterpret_cast<Vector2*>(reinterpret_cast<uint8_t*>(uv) + stride);
+    }
+  }
+
+  VertexBuffer vertexBuffer = VertexBuffer::New(properties);
+  vertexBuffer.SetData(bytes.data(), bytes.size() / stride);
+
+  Geometry geometry = Geometry::New();
+  geometry.AddVertexBuffer(vertexBuffer);
+  geometry.SetType(Geometry::TRIANGLE_STRIP);
+  return geometry;
+}
+
+void ToUnixFileSeparators(std::string& path)
+{
+  std::replace(path.begin(), path.end(), '\\', '/');
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/utils.h b/dali-scene-loader/public-api/utils.h
new file mode 100644 (file)
index 0000000..1aace35
--- /dev/null
@@ -0,0 +1,206 @@
+#ifndef DALI_SCENE_LOADER_UTILS_H_
+#define DALI_SCENE_LOADER_UTILS_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/actors/actor.h"
+#include "dali/public-api/rendering/renderer.h"
+#include "dali/public-api/common/dali-common.h"
+#include <sstream>
+#include <cctype>
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/*
+ * @brief Fixed size backing buffer to use with std::ostreams where control over
+ *  allocations (which this does not make), is required.
+ * @note All stream insertions that would overflow the buffer that StreamBuffer
+ *  was created with, will fail.
+ */
+class DALI_SCENE_LOADER_API StreamBuffer : public std::basic_streambuf<char>
+{
+public:
+  StreamBuffer(char* buffer, size_t size) noexcept(true);
+};
+
+/*
+ * @brief Wraps an ostream with a pre-allocated, fixed size backing buffer
+ *  which a message can be formatted into. Upon destruction, it throws a
+ *  DaliException with the message.
+ */
+class DALI_SCENE_LOADER_API ExceptionFlinger
+{
+public:
+  enum { MESSAGE_BUFFER_SIZE = 512 };
+
+  ExceptionFlinger(const char* location) noexcept(true);
+
+  [[noreturn]]
+  ~ExceptionFlinger() noexcept(false);
+
+  template <typename T>
+  ExceptionFlinger& operator<<(const T& rhs) noexcept(true)
+  {
+    mStream << rhs;
+    return *this;
+  }
+
+private:
+  struct Impl
+  {
+    const char* mLocation;
+
+    [[noreturn]]
+    ~Impl() noexcept(false);
+  };
+
+  static char* GetMessageBuffer() noexcept(true);
+
+  Impl mImpl;
+  StreamBuffer mStreamBuffer;
+  std::ostream mStream;
+};
+
+/*
+ * @brief Formats the given printf-style varargs into a std::string.
+ */
+DALI_SCENE_LOADER_API std::string FormatString(const char* format, ...);
+
+/*
+ * @return The @n th bit in a bitmask.
+ */
+DALI_SCENE_LOADER_API constexpr size_t NthBit(size_t n) { return 1 << n; }
+
+/*
+ * @return Whether all of @a mask 's bits are set on @a value.
+ */
+inline
+DALI_SCENE_LOADER_API bool MaskMatch(uint32_t value, uint32_t mask)
+{
+  return (value & mask) == mask;
+}
+
+/*
+ * @brief Convert a four-letter(, null-terminated) string literal into a uint32_t.
+ */
+inline
+DALI_SCENE_LOADER_API constexpr uint32_t FourCC(const char(&fourCC)[5])
+{
+  return (fourCC[3] << 24) | (fourCC[2] << 16) | (fourCC[1] << 8) | fourCC[0];
+}
+
+/*
+ * @brief Insensitive case compare function.
+ * @param[in] a, compare string
+ * @param[in] b, compare string
+ * @return true if strings are equal
+ */
+inline
+DALI_SCENE_LOADER_API bool CaseInsensitiveCharacterCompare( unsigned char a, unsigned char b )
+{
+  // Converts to lower case in the current locale.
+  return std::tolower( a ) == std::tolower( b );
+}
+
+/*
+ * @return true if the lower cased ASCII strings are equal.
+ * @param[in] a, compare string
+ * @param[in] b, compare string
+ */
+inline
+DALI_SCENE_LOADER_API bool CaseInsensitiveStringCompare( const std::string& a, const std::string& b )
+{
+  bool result = false;
+  if( a.length() == b.length() )
+  {
+    result = std::equal( a.begin(), a.end(), b.begin(), &CaseInsensitiveCharacterCompare );
+  }
+  return result;
+}
+
+/*
+ * @brief Attempts to load the contents of a text file; returns empty string on
+ *  failure. A pointer to a boolean may be passed in @a fail; this will be set
+ *  to true in case of failure (should only be checked if the returned string
+ *  was empty()).
+ */
+DALI_SCENE_LOADER_API std::string LoadTextFile(const char* path, bool* fail = nullptr);
+
+/*
+ * @brief Makes a number of calls to @a fn, passing to each one the given
+ *  @a actor then each of its children, in depth-first traversal.
+ * @note @a fn must not change the actor hierarchy during traversal.
+ * @note Use of a @a fn that is itself recursing in @a is also discouraged
+ *  for performance and stability reasons.
+ */
+template <typename Func>
+inline
+DALI_SCENE_LOADER_API void VisitActor(Actor a, Func fn)
+{
+  fn(a);
+
+  unsigned int numChildren = a.GetChildCount();
+  for(unsigned int i = 0; i < numChildren; ++i)
+  {
+    VisitActor(a.GetChildAt(i), fn);
+  }
+}
+
+/*
+ * @brief Convenience function to set the given actor @a 's anchor point
+ *  and parent origin to center.
+ */
+inline
+DALI_SCENE_LOADER_API void SetActorCentered(Actor a)
+{
+  a.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::CENTER);
+  a.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::CENTER);
+}
+
+namespace TexturedQuadOptions
+{
+using Type = uint32_t;
+
+enum DALI_SCENE_LOADER_API Values : Type
+{
+  NONE = 0x00,
+  FLIP_VERTICAL = 0x01,
+};
+}
+
+/*
+ * @brief Makes... geometry for a textured quad.
+ */
+DALI_SCENE_LOADER_API Geometry MakeTexturedQuadGeometry(TexturedQuadOptions::Type options = TexturedQuadOptions::NONE);
+
+/*
+ * @brief Fixes the path of a file. Replaces the '\\' separator by the '/' one.
+ * @param[in,out] path The path to be fixed.
+ */
+DALI_SCENE_LOADER_API void ToUnixFileSeparators( std::string& path );
+
+}
+}
+
+#endif /* DALI_SCENE_LOADER_UTILS_H_ */
diff --git a/dali-scene-loader/public-api/view-projection.cpp b/dali-scene-loader/public-api/view-projection.cpp
new file mode 100644 (file)
index 0000000..ed8cbe3
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#include "dali-scene-loader/public-api/view-projection.h"
+#include "dali-scene-loader/public-api/utils.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+void ViewProjection::Update()
+{
+  Matrix::Multiply(mViewProjection, mView, mProjection);
+
+  mInvProjection = mProjection;
+  if (!mInvProjection.Invert())
+  {
+    ExceptionFlinger(ASSERT_LOCATION) << "Failed to find inverse of projection matrix " << mProjection << ".";
+  }
+}
+
+}
+}
diff --git a/dali-scene-loader/public-api/view-projection.h b/dali-scene-loader/public-api/view-projection.h
new file mode 100644 (file)
index 0000000..70efc4f
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef DALI_SCENE_LOADER_VIEW_PROJECTION_H_
+#define DALI_SCENE_LOADER_VIEW_PROJECTION_H_
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// INTERNAL INCLUDES
+#include "dali-scene-loader/public-api/api.h"
+
+// EXTERNAL INCLUDES
+#include "dali/public-api/math/matrix.h"
+
+namespace Dali
+{
+namespace SceneLoader
+{
+
+/**
+ * @brief Contains view and projection matrices, also caching the view-projection
+ *      and inverse projection matrices.
+ */
+class ViewProjection
+{
+public:
+  Matrix& GetView() { return mView; }
+  Matrix& GetProjection() { return mProjection; }
+
+  /*
+   * @brief Updates the cached view projection and inverse projection matrices.
+   */
+  void Update();
+
+  const Matrix& GetView() const { return mView; }
+  const Matrix& GetProjection() const { return mProjection; }
+  const Matrix& GetInverseProjection() const { return mInvProjection; }
+  const Matrix& GetViewProjection() const { return mViewProjection; }
+
+private:
+  Matrix mView;
+  Matrix mProjection;
+  Matrix mInvProjection;
+  Matrix mViewProjection;
+};
+
+}
+}
+
+#endif //DALI_SCENE_LOADER_VIEW_PROJECTION_H_
diff --git a/dali-scene-loader/third-party/json.h b/dali-scene-loader/third-party/json.h
new file mode 100644 (file)
index 0000000..925c50b
--- /dev/null
@@ -0,0 +1,3134 @@
+/*
+   The latest version of this library is available on GitHub;
+   https://github.com/sheredom/json.h.
+*/
+
+/*
+   This is free and unencumbered software released into the public domain.
+
+   Anyone is free to copy, modify, publish, use, compile, sell, or
+   distribute this software, either in source code form or as a compiled
+   binary, for any purpose, commercial or non-commercial, and by any
+   means.
+
+   In jurisdictions that recognize copyright laws, the author or authors
+   of this software dedicate any and all copyright interest in the
+   software to the public domain. We make this dedication for the benefit
+   of the public at large and to the detriment of our heirs and
+   successors. We intend this dedication to be an overt act of
+   relinquishment in perpetuity of all present and future rights to this
+   software under copyright law.
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+   OTHER DEALINGS IN THE SOFTWARE.
+
+   For more information, please refer to <http://unlicense.org/>.
+*/
+
+#ifndef SHEREDOM_JSON_H_INCLUDED
+#define SHEREDOM_JSON_H_INCLUDED
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+
+/* disable 'bytes padding added after construct' warning */
+#pragma warning(disable : 4820)
+#endif
+
+#include <stddef.h>
+
+#if defined(__clang__) || defined(__GNUC__)
+#define json_weak __attribute__((weak))
+#elif defined(_MSC_VER)
+#define json_weak __inline
+#else
+#error Non clang, non gcc, non MSVC compiler found!
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  struct json_value_s;
+  struct json_parse_result_s;
+
+  enum json_parse_flags_e {
+    json_parse_flags_default = 0,
+
+    /* allow trailing commas in objects and arrays. For example, both [true,] and
+       {"a" : null,} would be allowed with this option on. */
+    json_parse_flags_allow_trailing_comma = 0x1,
+
+    /* allow unquoted keys for objects. For example, {a : null} would be allowed
+       with this option on. */
+    json_parse_flags_allow_unquoted_keys = 0x2,
+
+    /* allow a global unbracketed object. For example, a : null, b : true, c : {}
+       would be allowed with this option on. */
+    json_parse_flags_allow_global_object = 0x4,
+
+    /* allow objects to use '=' instead of ':' between key/value pairs. For
+       example, a = null, b : true would be allowed with this option on. */
+    json_parse_flags_allow_equals_in_object = 0x8,
+
+    /* allow that objects don't have to have comma separators between key/value
+       pairs. */
+    json_parse_flags_allow_no_commas = 0x10,
+
+    /* allow c-style comments (either variants) to be ignored in the input JSON
+       file. */
+    json_parse_flags_allow_c_style_comments = 0x20,
+
+    /* deprecated flag, unused. */
+    json_parse_flags_deprecated = 0x40,
+
+    /* record location information for each value. */
+    json_parse_flags_allow_location_information = 0x80,
+
+    /* allow strings to be 'single quoted'. */
+    json_parse_flags_allow_single_quoted_strings = 0x100,
+
+    /* allow numbers to be hexadecimal. */
+    json_parse_flags_allow_hexadecimal_numbers = 0x200,
+
+    /* allow numbers like +123 to be parsed. */
+    json_parse_flags_allow_leading_plus_sign = 0x400,
+
+    /* allow numbers like .0123 or 123. to be parsed. */
+    json_parse_flags_allow_leading_or_trailing_decimal_point = 0x800,
+
+    /* allow Infinity, -Infinity, NaN, -NaN. */
+    json_parse_flags_allow_inf_and_nan = 0x1000,
+
+    /* allow multi line string values. */
+    json_parse_flags_allow_multi_line_strings = 0x2000,
+
+    /* allow simplified JSON to be parsed. Simplified JSON is an enabling of a set
+       of other parsing options. */
+    json_parse_flags_allow_simplified_json =
+    (json_parse_flags_allow_trailing_comma |
+      json_parse_flags_allow_unquoted_keys |
+      json_parse_flags_allow_global_object |
+      json_parse_flags_allow_equals_in_object |
+      json_parse_flags_allow_no_commas),
+
+    /* allow JSON5 to be parsed. JSON5 is an enabling of a set of other parsing
+       options. */
+    json_parse_flags_allow_json5 =
+    (json_parse_flags_allow_trailing_comma |
+      json_parse_flags_allow_unquoted_keys |
+      json_parse_flags_allow_c_style_comments |
+      json_parse_flags_allow_single_quoted_strings |
+      json_parse_flags_allow_hexadecimal_numbers |
+      json_parse_flags_allow_leading_plus_sign |
+      json_parse_flags_allow_leading_or_trailing_decimal_point |
+      json_parse_flags_allow_inf_and_nan |
+      json_parse_flags_allow_multi_line_strings)
+  };
+
+  /* Parse a JSON text file, returning a pointer to the root of the JSON
+   * structure. json_parse performs 1 call to malloc for the entire encoding.
+   * Returns 0 if an error occurred (malformed JSON input, or malloc failed). */
+  json_weak struct json_value_s *json_parse(const void *src, size_t src_size);
+
+  /* Parse a JSON text file, returning a pointer to the root of the JSON
+   * structure. json_parse performs 1 call to malloc for the entire encoding.
+   * Returns 0 if an error occurred (malformed JSON input, or malloc failed). If
+   * an error occurred, the result struct (if not NULL) will explain the type of
+   * error, and the location in the input it occurred. */
+  json_weak struct json_value_s *
+    json_parse_ex(const void *src, size_t src_size, size_t flags_bitset,
+      void *(*alloc_func_ptr)(void *, size_t), void *user_data,
+      struct json_parse_result_s *result);
+
+  /* Write out a minified JSON utf-8 string. This string is an encoding of the
+   * minimal string characters required to still encode the same data.
+   * json_write_minified performs 1 call to malloc for the entire encoding. Return
+   * 0 if an error occurred (malformed JSON input, or malloc failed). The out_size
+   * parameter is optional as the utf-8 string is null terminated. */
+  json_weak void *json_write_minified(const struct json_value_s *value,
+    size_t *out_size);
+
+  /* Write out a pretty JSON utf-8 string. This string is encoded such that the
+   * resultant JSON is pretty in that it is easily human readable. The indent and
+   * newline parameters allow a user to specify what kind of indentation and
+   * newline they want (two spaces / three spaces / tabs? \r, \n, \r\n ?). Both
+   * indent and newline can be NULL, indent defaults to two spaces ("  "), and
+   * newline defaults to linux newlines ('\n' as the newline character).
+   * json_write_pretty performs 1 call to malloc for the entire encoding. Return 0
+   * if an error occurred (malformed JSON input, or malloc failed). The out_size
+   * parameter is optional as the utf-8 string is null terminated. */
+  json_weak void *json_write_pretty(const struct json_value_s *value,
+    const char *indent, const char *newline,
+    size_t *out_size);
+
+  /* Reinterpret a JSON value as a string. Returns null is the value was not a
+   * string. */
+  json_weak struct json_string_s *
+    json_value_as_string(struct json_value_s *const value);
+
+  /* Reinterpret a JSON value as a number. Returns null is the value was not a
+   * number. */
+  json_weak struct json_number_s *
+    json_value_as_number(struct json_value_s *const value);
+
+  /* Reinterpret a JSON value as an object. Returns null is the value was not an
+   * object. */
+  json_weak struct json_object_s *
+    json_value_as_object(struct json_value_s *const value);
+
+  /* Reinterpret a JSON value as an array. Returns null is the value was not an
+   * array. */
+  json_weak struct json_array_s *
+    json_value_as_array(struct json_value_s *const value);
+
+  /* Whether the value is true. */
+  json_weak int json_value_is_true(const struct json_value_s *const value);
+
+  /* Whether the value is false. */
+  json_weak int json_value_is_false(const struct json_value_s *const value);
+
+  /* Whether the value is null. */
+  json_weak int json_value_is_null(const struct json_value_s *const value);
+
+  /* The various types JSON values can be. Used to identify what a value is. */
+  enum json_type_e {
+    json_type_string,
+    json_type_number,
+    json_type_object,
+    json_type_array,
+    json_type_true,
+    json_type_false,
+    json_type_null
+  };
+
+  /* A JSON string value. */
+  struct json_string_s {
+    /* utf-8 string */
+    const char *string;
+    /* The size (in bytes) of the string */
+    size_t string_size;
+  };
+
+  /* A JSON string value (extended). */
+  struct json_string_ex_s {
+    /* The JSON string this extends. */
+    struct json_string_s string;
+
+    /* The character offset for the value in the JSON input. */
+    size_t offset;
+
+    /* The line number for the value in the JSON input. */
+    size_t line_no;
+
+    /* The row number for the value in the JSON input, in bytes. */
+    size_t row_no;
+  };
+
+  /* A JSON number value. */
+  struct json_number_s {
+    /* ASCII string containing representation of the number. */
+    const char *number;
+    /* the size (in bytes) of the number. */
+    size_t number_size;
+  };
+
+  /* an element of a JSON object. */
+  struct json_object_element_s {
+    /* the name of this element. */
+    struct json_string_s *name;
+    /* the value of this element. */
+    struct json_value_s *value;
+    /* the next object element (can be NULL if the last element in the object). */
+    struct json_object_element_s *next;
+  };
+
+  /* a JSON object value. */
+  struct json_object_s {
+    /* a linked list of the elements in the object. */
+    struct json_object_element_s *start;
+    /* the number of elements in the object. */
+    size_t length;
+  };
+
+  /* an element of a JSON array. */
+  struct json_array_element_s {
+    /* the value of this element. */
+    struct json_value_s *value;
+    /* the next array element (can be NULL if the last element in the array). */
+    struct json_array_element_s *next;
+  };
+
+  /* a JSON array value. */
+  struct json_array_s {
+    /* a linked list of the elements in the array. */
+    struct json_array_element_s *start;
+    /* the number of elements in the array. */
+    size_t length;
+  };
+
+  /* a JSON value. */
+  struct json_value_s {
+    /* a pointer to either a json_string_s, json_number_s, json_object_s, or. */
+    /* json_array_s. Should be cast to the appropriate struct type based on what.
+     */
+     /* the type of this value is. */
+    void *payload;
+    /* must be one of json_type_e. If type is json_type_true, json_type_false, or.
+     */
+     /* json_type_null, payload will be NULL. */
+    size_t type;
+  };
+
+  /* a JSON value (extended). */
+  struct json_value_ex_s {
+    /* the JSON value this extends. */
+    struct json_value_s value;
+
+    /* the character offset for the value in the JSON input. */
+    size_t offset;
+
+    /* the line number for the value in the JSON input. */
+    size_t line_no;
+
+    /* the row number for the value in the JSON input, in bytes. */
+    size_t row_no;
+  };
+
+  /* a parsing error code. */
+  enum json_parse_error_e {
+    /* no error occurred (huzzah!). */
+    json_parse_error_none = 0,
+
+    /* expected either a comma or a closing '}' or ']' to close an object or. */
+    /* array! */
+    json_parse_error_expected_comma_or_closing_bracket,
+
+    /* colon separating name/value pair was missing! */
+    json_parse_error_expected_colon,
+
+    /* expected string to begin with '"'! */
+    json_parse_error_expected_opening_quote,
+
+    /* invalid escaped sequence in string! */
+    json_parse_error_invalid_string_escape_sequence,
+
+    /* invalid number format! */
+    json_parse_error_invalid_number_format,
+
+    /* invalid value! */
+    json_parse_error_invalid_value,
+
+    /* reached end of buffer before object/array was complete! */
+    json_parse_error_premature_end_of_buffer,
+
+    /* string was malformed! */
+    json_parse_error_invalid_string,
+
+    /* a call to malloc, or a user provider allocator, failed. */
+    json_parse_error_allocator_failed,
+
+    /* the JSON input had unexpected trailing characters that weren't part of the.
+     */
+     /* JSON value. */
+     json_parse_error_unexpected_trailing_characters,
+
+     /* catch-all error for everything else that exploded (real bad chi!). */
+     json_parse_error_unknown
+  };
+
+  /* error report from json_parse_ex(). */
+  struct json_parse_result_s {
+    /* the error code (one of json_parse_error_e). */
+    size_t error;
+
+    /* the character offset for the error in the JSON input. */
+    size_t error_offset;
+
+    /* the line number for the error in the JSON input. */
+    size_t error_line_no;
+
+    /* the row number for the error, in bytes. */
+    size_t error_row_no;
+  };
+
+#ifdef __cplusplus
+} /* extern "C". */
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#include <stdlib.h>
+
+#if defined(_MSC_VER)
+#define json_strtoumax _strtoui64
+#define json_uintmax_t unsigned __int64
+#else
+#include <inttypes.h>
+#define json_strtoumax strtoumax
+#define json_uintmax_t uintmax_t
+#endif
+
+#if defined(__cplusplus) && (__cplusplus >= 201103L)
+#define json_null nullptr
+#else
+#define json_null 0
+#endif
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+
+/* we do one big allocation via malloc, then cast aligned slices of this for. */
+/* our structures - we don't have a way to tell the compiler we know what we. */
+/* are doing, so disable the warning instead! */
+#pragma clang diagnostic ignored "-Wcast-align"
+
+/* We use C style casts everywhere. */
+#pragma clang diagnostic ignored "-Wold-style-cast"
+
+/* We need long long for strtoull. */
+#pragma clang diagnostic ignored "-Wc++11-long-long"
+
+/* Who cares if nullptr doesn't work with C++98, we don't use it there! */
+#pragma clang diagnostic ignored "-Wc++98-compat"
+#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#elif defined(_MSC_VER)
+#pragma warning(push)
+
+/* disable 'function selected for inline expansion' warning. */
+#pragma warning(disable : 4711)
+
+/* disable '#pragma warning: there is no warning number' warning. */
+#pragma warning(disable : 4619)
+
+/* disable 'warning number not a valid compiler warning' warning. */
+#pragma warning(disable : 4616)
+
+/* disable 'Compiler will insert Spectre mitigation for memory load if
+ * /Qspectre. */
+ /* switch specified' warning. */
+#pragma warning(disable : 5045)
+#endif
+
+struct json_parse_state_s {
+  const char *src;
+  size_t size;
+  size_t offset;
+  size_t flags_bitset;
+  char *data;
+  char *dom;
+  size_t dom_size;
+  size_t data_size;
+  size_t line_no;     /* line counter for error reporting. */
+  size_t line_offset; /* (offset-line_offset) is the character number (in
+                         bytes). */
+  size_t error;
+};
+
+json_weak int json_hexadecimal_digit(const char c);
+int json_hexadecimal_digit(const char c) {
+  if ('0' <= c && c <= '9') {
+    return c - '0';
+  }
+  if ('a' <= c && c <= 'f') {
+    return c - 'a' + 10;
+  }
+  if ('A' <= c && c <= 'F') {
+    return c - 'A' + 10;
+  }
+  return -1;
+}
+
+json_weak int json_hexadecimal_value(const char * c, const unsigned long size, unsigned long * result);
+int json_hexadecimal_value(const char * c, const unsigned long size, unsigned long * result) {
+  const char * p;
+  int digit;
+
+  if (size > sizeof(unsigned long) * 2) {
+    return 0;
+  }
+
+  *result = 0;
+  for (p = c; (unsigned long)(p - c) < size; ++p) {
+    *result <<= 4;
+    digit = json_hexadecimal_digit(*p);
+    if (digit < 0 || digit > 15) {
+      return 0;
+    }
+    *result |= (unsigned char)digit;
+  }
+  return 1;
+}
+
+json_weak int json_skip_whitespace(struct json_parse_state_s *state);
+int json_skip_whitespace(struct json_parse_state_s *state) {
+  size_t offset = state->offset;
+  const size_t size = state->size;
+  const char *const src = state->src;
+
+  /* the only valid whitespace according to ECMA-404 is ' ', '\n', '\r' and
+   * '\t'. */
+  switch (src[offset]) {
+  default:
+    return 0;
+  case ' ':
+  case '\r':
+  case '\t':
+  case '\n':
+    break;
+  }
+
+  do {
+    switch (src[offset]) {
+    default:
+      /* Update offset. */
+      state->offset = offset;
+      return 1;
+    case ' ':
+    case '\r':
+    case '\t':
+      break;
+    case '\n':
+      state->line_no++;
+      state->line_offset = offset;
+      break;
+    }
+
+    offset++;
+  } while (offset < size);
+
+  /* Update offset. */
+  state->offset = offset;
+  return 1;
+}
+
+json_weak int json_skip_c_style_comments(struct json_parse_state_s *state);
+int json_skip_c_style_comments(struct json_parse_state_s *state) {
+  /* do we have a comment?. */
+  if ('/' == state->src[state->offset]) {
+    /* skip '/'. */
+    state->offset++;
+
+    if ('/' == state->src[state->offset]) {
+      /* we had a comment of the form //. */
+
+      /* skip second '/'. */
+      state->offset++;
+
+      while (state->offset < state->size) {
+        switch (state->src[state->offset]) {
+        default:
+          /* skip the character in the comment. */
+          state->offset++;
+          break;
+        case '\n':
+          /* if we have a newline, our comment has ended! Skip the newline. */
+          state->offset++;
+
+          /* we entered a newline, so move our line info forward. */
+          state->line_no++;
+          state->line_offset = state->offset;
+          return 1;
+        }
+      }
+
+      /* we reached the end of the JSON file! */
+      return 1;
+    }
+    else if ('*' == state->src[state->offset]) {
+      /* we had a comment in the C-style long form. */
+
+      /* skip '*'. */
+      state->offset++;
+
+      while (state->offset + 1 < state->size) {
+        if (('*' == state->src[state->offset]) &&
+          ('/' == state->src[state->offset + 1])) {
+          /* we reached the end of our comment! */
+          state->offset += 2;
+          return 1;
+        }
+        else if ('\n' == state->src[state->offset]) {
+          /* we entered a newline, so move our line info forward. */
+          state->line_no++;
+          state->line_offset = state->offset;
+        }
+
+        /* skip character within comment. */
+        state->offset++;
+      }
+
+      /* Comment wasn't ended correctly which is a failure. */
+      return 1;
+    }
+  }
+
+  /* we didn't have any comment, which is ok too! */
+  return 0;
+}
+
+json_weak int json_skip_all_skippables(struct json_parse_state_s *state);
+int json_skip_all_skippables(struct json_parse_state_s *state) {
+  /* skip all whitespace and other skippables until there are none left. note
+   * that the previous version suffered from read past errors should. the
+   * stream end on json_skip_c_style_comments eg. '{"a" ' with comments flag.
+   */
+
+  int did_consume = 0;
+  const size_t size = state->size;
+
+  if (json_parse_flags_allow_c_style_comments & state->flags_bitset) {
+    do {
+      if (state->offset == size) {
+        state->error = json_parse_error_premature_end_of_buffer;
+        return 1;
+      }
+
+      did_consume = json_skip_whitespace(state);
+
+      /* This should really be checked on access, not in front of every call.
+       */
+      if (state->offset == size) {
+        state->error = json_parse_error_premature_end_of_buffer;
+        return 1;
+      }
+
+      did_consume |= json_skip_c_style_comments(state);
+    } while (0 != did_consume);
+  }
+  else {
+    do {
+      if (state->offset == size) {
+        state->error = json_parse_error_premature_end_of_buffer;
+        return 1;
+      }
+
+      did_consume = json_skip_whitespace(state);
+    } while (0 != did_consume);
+  }
+
+  if (state->offset == size) {
+    state->error = json_parse_error_premature_end_of_buffer;
+    return 1;
+  }
+
+  return 0;
+}
+
+json_weak int json_get_value_size(struct json_parse_state_s *state,
+  int is_global_object);
+
+json_weak int json_get_string_size(struct json_parse_state_s *state,
+  size_t is_key);
+int json_get_string_size(struct json_parse_state_s *state,
+  size_t is_key) {
+  size_t offset = state->offset;
+  const size_t size = state->size;
+  size_t data_size = 0;
+  const char *const src = state->src;
+  const int is_single_quote = '\'' == src[offset];
+  const char quote_to_use = is_single_quote ? '\'' : '"';
+  const size_t flags_bitset = state->flags_bitset;
+  unsigned long codepoint;
+  unsigned long high_surrogate = 0;
+
+  if ((json_parse_flags_allow_location_information & flags_bitset) != 0 &&
+    is_key != 0) {
+    state->dom_size += sizeof(struct json_string_ex_s);
+  }
+  else {
+    state->dom_size += sizeof(struct json_string_s);
+  }
+
+  if ('"' != src[offset]) {
+    /* if we are allowed single quoted strings check for that too. */
+    if (!((json_parse_flags_allow_single_quoted_strings & flags_bitset) &&
+      is_single_quote)) {
+      state->error = json_parse_error_expected_opening_quote;
+      state->offset = offset;
+      return 1;
+    }
+  }
+
+  /* skip leading '"' or '\''. */
+  offset++;
+
+  while ((offset < size) && (quote_to_use != src[offset])) {
+    /* add space for the character. */
+    data_size++;
+
+    if ('\\' == src[offset]) {
+      /* skip reverse solidus character. */
+      offset++;
+
+      if (offset == size) {
+        state->error = json_parse_error_premature_end_of_buffer;
+        state->offset = offset;
+        return 1;
+      }
+
+      switch (src[offset]) {
+      default:
+        state->error = json_parse_error_invalid_string_escape_sequence;
+        state->offset = offset;
+        return 1;
+      case '"':
+      case '\\':
+      case '/':
+      case 'b':
+      case 'f':
+      case 'n':
+      case 'r':
+      case 't':
+        /* all valid characters! */
+        offset++;
+        break;
+      case 'u':
+        if (!(offset + 5 < size)) {
+          /* invalid escaped unicode sequence! */
+          state->error = json_parse_error_invalid_string_escape_sequence;
+          state->offset = offset;
+          return 1;
+        }
+
+        codepoint = 0;
+        if (!json_hexadecimal_value(&src[offset + 1], 4, &codepoint)) {
+          /* escaped unicode sequences must contain 4 hexadecimal digits! */
+          state->error = json_parse_error_invalid_string_escape_sequence;
+          state->offset = offset;
+          return 1;
+        }
+
+        /* Valid sequence!
+         * see: https://en.wikipedia.org/wiki/UTF-8#Invalid_code_points.
+         *      1       7       U + 0000        U + 007F        0xxxxxxx.
+         *      2       11      U + 0080        U + 07FF        110xxxxx
+         * 10xxxxxx.
+         *      3       16      U + 0800        U + FFFF        1110xxxx
+         * 10xxxxxx        10xxxxxx.
+         *      4       21      U + 10000       U + 10FFFF      11110xxx
+         * 10xxxxxx        10xxxxxx        10xxxxxx.
+         * Note: the high and low surrogate halves used by UTF-16 (U+D800
+         * through U+DFFF) and code points not encodable by UTF-16 (those after
+         * U+10FFFF) are not legal Unicode values, and their UTF-8 encoding must
+         * be treated as an invalid byte sequence. */
+
+        if (high_surrogate != 0) {
+          /* we previously read the high half of the \uxxxx\uxxxx pair, so now
+           * we expect the low half. */
+          if (codepoint >= 0xdc00 &&
+            codepoint <= 0xdfff) { /* low surrogate range. */
+            data_size += 3;
+            high_surrogate = 0;
+          }
+          else {
+            state->error = json_parse_error_invalid_string_escape_sequence;
+            state->offset = offset;
+            return 1;
+          }
+        }
+        else if (codepoint <= 0x7f) {
+          data_size += 0;
+        }
+        else if (codepoint <= 0x7ff) {
+          data_size += 1;
+        }
+        else if (codepoint >= 0xd800 &&
+          codepoint <= 0xdbff) { /* high surrogate range. */
+ /* The codepoint is the first half of a "utf-16 surrogate pair". so we
+  * need the other half for it to be valid: \uHHHH\uLLLL. */
+          if (offset + 11 > size || '\\' != src[offset + 5] ||
+            'u' != src[offset + 6]) {
+            state->error = json_parse_error_invalid_string_escape_sequence;
+            state->offset = offset;
+            return 1;
+          }
+          high_surrogate = codepoint;
+        }
+        else if (codepoint >= 0xd800 &&
+          codepoint <= 0xdfff) { /* low surrogate range. */
+ /* we did not read the other half before. */
+          state->error = json_parse_error_invalid_string_escape_sequence;
+          state->offset = offset;
+          return 1;
+        }
+        else {
+          data_size += 2;
+        }
+        /* escaped codepoints after 0xffff are supported in json through utf-16
+         * surrogate pairs: \uD83D\uDD25 for U+1F525. */
+
+        offset += 5;
+        break;
+      }
+    }
+    else if (('\r' == src[offset]) || ('\n' == src[offset])) {
+      if (!(json_parse_flags_allow_multi_line_strings & flags_bitset)) {
+        /* invalid escaped unicode sequence! */
+        state->error = json_parse_error_invalid_string_escape_sequence;
+        state->offset = offset;
+        return 1;
+      }
+
+      offset++;
+    }
+    else {
+      /* skip character (valid part of sequence). */
+      offset++;
+    }
+  }
+
+  /* skip trailing '"' or '\''. */
+  offset++;
+
+  /* add enough space to store the string. */
+  state->data_size += data_size;
+
+  /* one more byte for null terminator ending the string! */
+  state->data_size++;
+
+  /* update offset. */
+  state->offset = offset;
+
+  return 0;
+}
+
+json_weak int is_valid_unquoted_key_char(const char c);
+int is_valid_unquoted_key_char(const char c) {
+  return (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') ||
+    ('A' <= c && c <= 'Z') || ('_' == c));
+}
+
+json_weak int json_get_key_size(struct json_parse_state_s *state);
+int json_get_key_size(struct json_parse_state_s *state) {
+  const size_t flags_bitset = state->flags_bitset;
+
+  if (json_parse_flags_allow_unquoted_keys & flags_bitset) {
+    size_t offset = state->offset;
+    const size_t size = state->size;
+    const char *const src = state->src;
+    size_t data_size = state->data_size;
+
+    /* if we are allowing unquoted keys, first grok for a quote... */
+    if ('"' == src[offset]) {
+      /* ... if we got a comma, just parse the key as a string as normal. */
+      return json_get_string_size(state, 1);
+    }
+    else if ((json_parse_flags_allow_single_quoted_strings & flags_bitset) &&
+      ('\'' == src[offset])) {
+      /* ... if we got a comma, just parse the key as a string as normal. */
+      return json_get_string_size(state, 1);
+    }
+    else {
+      while ((offset < size) && is_valid_unquoted_key_char(src[offset])) {
+        offset++;
+        data_size++;
+      }
+
+      /* one more byte for null terminator ending the string! */
+      data_size++;
+
+      if (json_parse_flags_allow_location_information & flags_bitset) {
+        state->dom_size += sizeof(struct json_string_ex_s);
+      }
+      else {
+        state->dom_size += sizeof(struct json_string_s);
+      }
+
+      /* update offset. */
+      state->offset = offset;
+
+      /* update data_size. */
+      state->data_size = data_size;
+
+      return 0;
+    }
+  }
+  else {
+    /* we are only allowed to have quoted keys, so just parse a string! */
+    return json_get_string_size(state, 1);
+  }
+}
+
+json_weak int json_get_object_size(struct json_parse_state_s *state,
+  int is_global_object);
+int json_get_object_size(struct json_parse_state_s *state,
+  int is_global_object) {
+  const size_t flags_bitset = state->flags_bitset;
+  const char *const src = state->src;
+  const size_t size = state->size;
+  size_t elements = 0;
+  int allow_comma = 0;
+
+  if (is_global_object) {
+    /* if we found an opening '{' of an object, we actually have a normal JSON
+     * object at the root of the DOM... */
+    if (!json_skip_all_skippables(state) && '{' == state->src[state->offset]) {
+      /* . and we don't actually have a global object after all! */
+      is_global_object = 0;
+    }
+  }
+
+  if (!is_global_object) {
+    if ('{' != src[state->offset]) {
+      state->error = json_parse_error_unknown;
+      return 1;
+    }
+
+    /* skip leading '{'. */
+    state->offset++;
+  }
+
+  state->dom_size += sizeof(struct json_object_s);
+
+  while (state->offset < size) {
+    if (!is_global_object) {
+      if (json_skip_all_skippables(state)) {
+        state->error = json_parse_error_premature_end_of_buffer;
+        return 1;
+      }
+      if ('}' == src[state->offset]) {
+        /* skip trailing '}'. */
+        state->offset++;
+
+        /* finished the object! */
+        break;
+      }
+    }
+    else {
+      /* we don't require brackets, so that means the object ends when the input
+       * stream ends! */
+      if (json_skip_all_skippables(state)) {
+        break;
+      }
+    }
+
+    /* if we parsed at least once element previously, grok for a comma. */
+    if (allow_comma) {
+      if (',' == src[state->offset]) {
+        /* skip comma. */
+        state->offset++;
+        allow_comma = 0;
+      }
+      else if (json_parse_flags_allow_no_commas & flags_bitset) {
+        /* we don't require a comma, and we didn't find one, which is ok! */
+        allow_comma = 0;
+      }
+      else {
+        /* otherwise we are required to have a comma, and we found none. */
+        state->error = json_parse_error_expected_comma_or_closing_bracket;
+        return 1;
+      }
+
+      if (json_parse_flags_allow_trailing_comma & flags_bitset) {
+        continue;
+      }
+      else {
+        if (json_skip_all_skippables(state)) {
+          state->error = json_parse_error_premature_end_of_buffer;
+          return 1;
+        }
+      }
+    }
+
+    if (json_get_key_size(state)) {
+      /* key parsing failed! */
+      state->error = json_parse_error_invalid_string;
+      return 1;
+    }
+
+    if (json_skip_all_skippables(state)) {
+      state->error = json_parse_error_premature_end_of_buffer;
+      return 1;
+    }
+
+    if (json_parse_flags_allow_equals_in_object & flags_bitset) {
+      const char current = src[state->offset];
+      if ((':' != current) && ('=' != current)) {
+        state->error = json_parse_error_expected_colon;
+        return 1;
+      }
+    }
+    else {
+      if (':' != src[state->offset]) {
+        state->error = json_parse_error_expected_colon;
+        return 1;
+      }
+    }
+
+    /* skip colon. */
+    state->offset++;
+
+    if (json_skip_all_skippables(state)) {
+      state->error = json_parse_error_premature_end_of_buffer;
+      return 1;
+    }
+
+    if (json_get_value_size(state, /* is_global_object = */ 0)) {
+      /* value parsing failed! */
+      return 1;
+    }
+
+    /* successfully parsed a name/value pair! */
+    elements++;
+    allow_comma = 1;
+  }
+
+  state->dom_size += sizeof(struct json_object_element_s) * elements;
+
+  return 0;
+}
+
+json_weak int json_get_array_size(struct json_parse_state_s *state);
+int json_get_array_size(struct json_parse_state_s *state) {
+  const size_t flags_bitset = state->flags_bitset;
+  size_t elements = 0;
+  int allow_comma = 0;
+  const char *const src = state->src;
+  const size_t size = state->size;
+
+  if ('[' != src[state->offset]) {
+    /* expected array to begin with leading '['. */
+    state->error = json_parse_error_unknown;
+    return 1;
+  }
+
+  /* skip leading '['. */
+  state->offset++;
+
+  state->dom_size += sizeof(struct json_array_s);
+
+  while (state->offset < size) {
+    if (json_skip_all_skippables(state)) {
+      state->error = json_parse_error_premature_end_of_buffer;
+      return 1;
+    }
+
+    if (']' == src[state->offset]) {
+      /* skip trailing ']'. */
+      state->offset++;
+
+      state->dom_size += sizeof(struct json_array_element_s) * elements;
+
+      /* finished the object! */
+      return 0;
+    }
+
+    /* if we parsed at least once element previously, grok for a comma. */
+    if (allow_comma) {
+      if (',' == src[state->offset]) {
+        /* skip comma. */
+        state->offset++;
+        allow_comma = 0;
+      }
+      else if (!(json_parse_flags_allow_no_commas & flags_bitset)) {
+        state->error = json_parse_error_expected_comma_or_closing_bracket;
+        return 1;
+      }
+
+      if (json_parse_flags_allow_trailing_comma & flags_bitset) {
+        allow_comma = 0;
+        continue;
+      }
+      else {
+        if (json_skip_all_skippables(state)) {
+          state->error = json_parse_error_premature_end_of_buffer;
+          return 1;
+        }
+      }
+    }
+
+    if (json_get_value_size(state, /* is_global_object = */ 0)) {
+      /* value parsing failed! */
+      return 1;
+    }
+
+    /* successfully parsed an array element! */
+    elements++;
+    allow_comma = 1;
+  }
+
+  /* we consumed the entire input before finding the closing ']' of the array!
+   */
+  state->error = json_parse_error_premature_end_of_buffer;
+  return 1;
+}
+
+json_weak int json_get_number_size(struct json_parse_state_s *state);
+int json_get_number_size(struct json_parse_state_s *state) {
+  const size_t flags_bitset = state->flags_bitset;
+  size_t offset = state->offset;
+  const size_t size = state->size;
+  int had_leading_digits = 0;
+  const char *const src = state->src;
+
+  state->dom_size += sizeof(struct json_number_s);
+
+  if ((json_parse_flags_allow_hexadecimal_numbers & flags_bitset) &&
+    (offset + 1 < size) && ('0' == src[offset]) &&
+    (('x' == src[offset + 1]) || ('X' == src[offset + 1]))) {
+    /* skip the leading 0x that identifies a hexadecimal number. */
+    offset += 2;
+
+    /* consume hexadecimal digits. */
+    while ((offset < size) && (('0' <= src[offset] && src[offset] <= '9') ||
+      ('a' <= src[offset] && src[offset] <= 'f') ||
+      ('A' <= src[offset] && src[offset] <= 'F'))) {
+      offset++;
+    }
+  }
+  else {
+    int found_sign = 0;
+    int inf_or_nan = 0;
+
+    if ((offset < size) &&
+      (('-' == src[offset]) ||
+      ((json_parse_flags_allow_leading_plus_sign & flags_bitset) &&
+        ('+' == src[offset])))) {
+      /* skip valid leading '-' or '+'. */
+      offset++;
+
+      found_sign = 1;
+    }
+
+    if (json_parse_flags_allow_inf_and_nan & flags_bitset) {
+      const char inf[9] = "Infinity";
+      const size_t inf_strlen = sizeof(inf) - 1;
+      const char nan[4] = "NaN";
+      const size_t nan_strlen = sizeof(nan) - 1;
+
+      if (offset + inf_strlen < size) {
+        int found = 1;
+        size_t i;
+        for (i = 0; i < inf_strlen; i++) {
+          if (inf[i] != src[offset + i]) {
+            found = 0;
+            break;
+          }
+        }
+
+        if (found) {
+          /* We found our special 'Infinity' keyword! */
+          offset += inf_strlen;
+
+          inf_or_nan = 1;
+        }
+      }
+
+      if (offset + nan_strlen < size) {
+        int found = 1;
+        size_t i;
+        for (i = 0; i < nan_strlen; i++) {
+          if (nan[i] != src[offset + i]) {
+            found = 0;
+            break;
+          }
+        }
+
+        if (found) {
+          /* We found our special 'NaN' keyword! */
+          offset += nan_strlen;
+
+          inf_or_nan = 1;
+        }
+      }
+    }
+
+    if (found_sign && !inf_or_nan && (offset < size) &&
+      !('0' <= src[offset] && src[offset] <= '9')) {
+      /* check if we are allowing leading '.'. */
+      if (!(json_parse_flags_allow_leading_or_trailing_decimal_point &
+        flags_bitset) ||
+        ('.' != src[offset])) {
+        /* a leading '-' must be immediately followed by any digit! */
+        state->error = json_parse_error_invalid_number_format;
+        state->offset = offset;
+        return 1;
+      }
+    }
+
+    if ((offset < size) && ('0' == src[offset])) {
+      /* skip valid '0'. */
+      offset++;
+
+      /* we need to record whether we had any leading digits for checks later.
+       */
+      had_leading_digits = 1;
+
+      if ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) {
+        /* a leading '0' must not be immediately followed by any digit! */
+        state->error = json_parse_error_invalid_number_format;
+        state->offset = offset;
+        return 1;
+      }
+    }
+
+    /* the main digits of our number next. */
+    while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) {
+      offset++;
+
+      /* we need to record whether we had any leading digits for checks later.
+       */
+      had_leading_digits = 1;
+    }
+
+    if ((offset < size) && ('.' == src[offset])) {
+      offset++;
+
+      if (!('0' <= src[offset] && src[offset] <= '9')) {
+        if (!(json_parse_flags_allow_leading_or_trailing_decimal_point &
+          flags_bitset) ||
+          !had_leading_digits) {
+          /* a decimal point must be followed by at least one digit. */
+          state->error = json_parse_error_invalid_number_format;
+          state->offset = offset;
+          return 1;
+        }
+      }
+
+      /* a decimal point can be followed by more digits of course! */
+      while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) {
+        offset++;
+      }
+    }
+
+    if ((offset < size) && ('e' == src[offset] || 'E' == src[offset])) {
+      /* our number has an exponent! Wkip 'e' or 'E'. */
+      offset++;
+
+      if ((offset < size) && ('-' == src[offset] || '+' == src[offset])) {
+        /* skip optional '-' or '+'. */
+        offset++;
+      }
+
+      /* consume exponent digits. */
+      while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) {
+        offset++;
+      }
+    }
+  }
+
+  if (offset < size) {
+    switch (src[offset]) {
+    case ' ':
+    case '\t':
+    case '\r':
+    case '\n':
+    case '}':
+    case ',':
+    case ']':
+      /* all of the above are ok. */
+      break;
+    case '=':
+      if (json_parse_flags_allow_equals_in_object & flags_bitset) {
+        break;
+      }
+
+      state->error = json_parse_error_invalid_number_format;
+      state->offset = offset;
+      return 1;
+    default:
+      state->error = json_parse_error_invalid_number_format;
+      state->offset = offset;
+      return 1;
+    }
+  }
+
+  state->data_size += offset - state->offset;
+
+  /* one more byte for null terminator ending the number string! */
+  state->data_size++;
+
+  /* update offset. */
+  state->offset = offset;
+
+  return 0;
+}
+
+json_weak int json_get_value_size(struct json_parse_state_s *state,
+  int is_global_object);
+int json_get_value_size(struct json_parse_state_s *state,
+  int is_global_object) {
+  const size_t flags_bitset = state->flags_bitset;
+  const char *const src = state->src;
+  size_t offset;
+  const size_t size = state->size;
+
+  if (json_parse_flags_allow_location_information & flags_bitset) {
+    state->dom_size += sizeof(struct json_value_ex_s);
+  }
+  else {
+    state->dom_size += sizeof(struct json_value_s);
+  }
+
+  if (is_global_object) {
+    return json_get_object_size(state, /* is_global_object = */ 1);
+  }
+  else {
+    if (json_skip_all_skippables(state)) {
+      state->error = json_parse_error_premature_end_of_buffer;
+      return 1;
+    }
+
+    /* can cache offset now. */
+    offset = state->offset;
+
+    switch (src[offset]) {
+    case '"':
+      return json_get_string_size(state, 0);
+    case '\'':
+      if (json_parse_flags_allow_single_quoted_strings & flags_bitset) {
+        return json_get_string_size(state, 0);
+      }
+      else {
+        /* invalid value! */
+        state->error = json_parse_error_invalid_value;
+        return 1;
+      }
+    case '{':
+      return json_get_object_size(state, /* is_global_object = */ 0);
+    case '[':
+      return json_get_array_size(state);
+    case '-':
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+      return json_get_number_size(state);
+    case '+':
+      if (json_parse_flags_allow_leading_plus_sign & flags_bitset) {
+        return json_get_number_size(state);
+      }
+      else {
+        /* invalid value! */
+        state->error = json_parse_error_invalid_number_format;
+        return 1;
+      }
+    case '.':
+      if (json_parse_flags_allow_leading_or_trailing_decimal_point &
+        flags_bitset) {
+        return json_get_number_size(state);
+      }
+      else {
+        /* invalid value! */
+        state->error = json_parse_error_invalid_number_format;
+        return 1;
+      }
+    default:
+      if ((offset + 4) <= size && 't' == src[offset + 0] &&
+        'r' == src[offset + 1] && 'u' == src[offset + 2] &&
+        'e' == src[offset + 3]) {
+        state->offset += 4;
+        return 0;
+      }
+      else if ((offset + 5) <= size && 'f' == src[offset + 0] &&
+        'a' == src[offset + 1] && 'l' == src[offset + 2] &&
+        's' == src[offset + 3] && 'e' == src[offset + 4]) {
+        state->offset += 5;
+        return 0;
+      }
+      else if ((offset + 4) <= size && 'n' == state->src[offset + 0] &&
+        'u' == state->src[offset + 1] &&
+        'l' == state->src[offset + 2] &&
+        'l' == state->src[offset + 3]) {
+        state->offset += 4;
+        return 0;
+      }
+      else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) &&
+        (offset + 3) <= size && 'N' == src[offset + 0] &&
+        'a' == src[offset + 1] && 'N' == src[offset + 2]) {
+        return json_get_number_size(state);
+      }
+      else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) &&
+        (offset + 8) <= size && 'I' == src[offset + 0] &&
+        'n' == src[offset + 1] && 'f' == src[offset + 2] &&
+        'i' == src[offset + 3] && 'n' == src[offset + 4] &&
+        'i' == src[offset + 5] && 't' == src[offset + 6] &&
+        'y' == src[offset + 7]) {
+        return json_get_number_size(state);
+      }
+
+      /* invalid value! */
+      state->error = json_parse_error_invalid_value;
+      return 1;
+    }
+  }
+}
+
+json_weak void json_parse_value(struct json_parse_state_s *state,
+  int is_global_object, struct json_value_s *value);
+
+json_weak void json_parse_string(struct json_parse_state_s *state,
+  struct json_string_s *string);
+void json_parse_string(struct json_parse_state_s *state,
+  struct json_string_s *string) {
+  size_t offset = state->offset;
+  size_t bytes_written = 0;
+  const char *const src = state->src;
+  const char quote_to_use = '\'' == src[offset] ? '\'' : '"';
+  char *data = state->data;
+  unsigned long high_surrogate = 0;
+  unsigned long codepoint;
+
+  string->string = data;
+
+  /* skip leading '"' or '\''. */
+  offset++;
+
+  while (quote_to_use != src[offset]) {
+    if ('\\' == src[offset]) {
+      /* skip the reverse solidus. */
+      offset++;
+
+      switch (src[offset++]) {
+      default:
+        return; /* we cannot ever reach here. */
+      case 'u': {
+        codepoint = 0;
+        if (!json_hexadecimal_value(&src[offset], 4, &codepoint)) {
+          return; /* this shouldn't happen as the value was already validated.
+                   */
+        }
+
+        offset += 4;
+
+        if (codepoint <= 0x7fu) {
+          data[bytes_written++] = (char)codepoint; /* 0xxxxxxx. */
+        }
+        else if (codepoint <= 0x7ffu) {
+          data[bytes_written++] =
+            (char)(0xc0u | (codepoint >> 6)); /* 110xxxxx. */
+          data[bytes_written++] =
+            (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */
+        }
+        else if (codepoint >= 0xd800 &&
+          codepoint <= 0xdbff) { /* high surrogate. */
+          high_surrogate = codepoint;
+          continue; /* we need the low half to form a complete codepoint. */
+        }
+        else if (codepoint >= 0xdc00 &&
+          codepoint <= 0xdfff) { /* low surrogate. */
+ /* combine with the previously read half to obtain the complete
+  * codepoint. */
+          const unsigned long surrogate_offset = 0x10000u - (0xD800u << 10) - 0xDC00u;
+          codepoint = (high_surrogate << 10) + codepoint + surrogate_offset;
+          high_surrogate = 0;
+          data[bytes_written++] =
+            (char)(0xF0u | (codepoint >> 18)); /* 11110xxx. */
+          data[bytes_written++] =
+            (char)(0x80u | ((codepoint >> 12) & 0x3fu)); /* 10xxxxxx. */
+          data[bytes_written++] =
+            (char)(0x80u | ((codepoint >> 6) & 0x3fu)); /* 10xxxxxx. */
+          data[bytes_written++] =
+            (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */
+        }
+        else {
+          /* we assume the value was validated and thus is within the valid
+           * range. */
+          data[bytes_written++] =
+            (char)(0xe0u | (codepoint >> 12)); /* 1110xxxx. */
+          data[bytes_written++] =
+            (char)(0x80u | ((codepoint >> 6) & 0x3fu)); /* 10xxxxxx. */
+          data[bytes_written++] =
+            (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */
+        }
+      } break;
+      case '"':
+        data[bytes_written++] = '"';
+        break;
+      case '\\':
+        data[bytes_written++] = '\\';
+        break;
+      case '/':
+        data[bytes_written++] = '/';
+        break;
+      case 'b':
+        data[bytes_written++] = '\b';
+        break;
+      case 'f':
+        data[bytes_written++] = '\f';
+        break;
+      case 'n':
+        data[bytes_written++] = '\n';
+        break;
+      case 'r':
+        data[bytes_written++] = '\r';
+        break;
+      case 't':
+        data[bytes_written++] = '\t';
+        break;
+      case '\r':
+        data[bytes_written++] = '\r';
+
+        /* check if we have a "\r\n" sequence. */
+        if ('\n' == src[offset]) {
+          data[bytes_written++] = '\n';
+          offset++;
+        }
+
+        break;
+      case '\n':
+        data[bytes_written++] = '\n';
+        break;
+      }
+    }
+    else {
+      /* copy the character. */
+      data[bytes_written++] = src[offset++];
+    }
+  }
+
+  /* skip trailing '"' or '\''. */
+  offset++;
+
+  /* record the size of the string. */
+  string->string_size = bytes_written;
+
+  /* add null terminator to string. */
+  data[bytes_written++] = '\0';
+
+  /* move data along. */
+  state->data += bytes_written;
+
+  /* update offset. */
+  state->offset = offset;
+}
+
+json_weak void json_parse_key(struct json_parse_state_s *state,
+  struct json_string_s *string);
+void json_parse_key(struct json_parse_state_s *state,
+  struct json_string_s *string) {
+  if (json_parse_flags_allow_unquoted_keys & state->flags_bitset) {
+    const char *const src = state->src;
+    char *const data = state->data;
+    size_t offset = state->offset;
+
+    /* if we are allowing unquoted keys, check for quoted anyway... */
+    if (('"' == src[offset]) || ('\'' == src[offset])) {
+      /* ... if we got a quote, just parse the key as a string as normal. */
+      json_parse_string(state, string);
+    }
+    else {
+      size_t size = 0;
+
+      string->string = state->data;
+
+      while (is_valid_unquoted_key_char(src[offset])) {
+        data[size++] = src[offset++];
+      }
+
+      /* add null terminator to string. */
+      data[size] = '\0';
+
+      /* record the size of the string. */
+      string->string_size = size++;
+
+      /* move data along. */
+      state->data += size;
+
+      /* update offset. */
+      state->offset = offset;
+    }
+  }
+  else {
+    /* we are only allowed to have quoted keys, so just parse a string! */
+    json_parse_string(state, string);
+  }
+}
+
+json_weak void json_parse_object(struct json_parse_state_s *state,
+  int is_global_object,
+  struct json_object_s *object);
+void json_parse_object(struct json_parse_state_s *state,
+  int is_global_object,
+  struct json_object_s *object) {
+  const size_t flags_bitset = state->flags_bitset;
+  const size_t size = state->size;
+  const char *const src = state->src;
+  size_t elements = 0;
+  int allow_comma = 0;
+  struct json_object_element_s *previous = json_null;
+
+  if (is_global_object) {
+    /* if we skipped some whitespace, and then found an opening '{' of an. */
+    /* object, we actually have a normal JSON object at the root of the DOM...
+     */
+    if ('{' == src[state->offset]) {
+      /* . and we don't actually have a global object after all! */
+      is_global_object = 0;
+    }
+  }
+
+  if (!is_global_object) {
+    /* skip leading '{'. */
+    state->offset++;
+  }
+
+  (void)json_skip_all_skippables(state);
+
+  /* reset elements. */
+  elements = 0;
+
+  while (state->offset < size) {
+    struct json_object_element_s *element = json_null;
+    struct json_string_s *string = json_null;
+    struct json_value_s *value = json_null;
+
+    if (!is_global_object) {
+      (void)json_skip_all_skippables(state);
+
+      if ('}' == src[state->offset]) {
+        /* skip trailing '}'. */
+        state->offset++;
+
+        /* finished the object! */
+        break;
+      }
+    }
+    else {
+      if (json_skip_all_skippables(state)) {
+        /* global object ends when the file ends! */
+        break;
+      }
+    }
+
+    /* if we parsed at least one element previously, grok for a comma. */
+    if (allow_comma) {
+      if (',' == src[state->offset]) {
+        /* skip comma. */
+        state->offset++;
+        allow_comma = 0;
+        continue;
+      }
+    }
+
+    element = (struct json_object_element_s *)state->dom;
+
+    state->dom += sizeof(struct json_object_element_s);
+
+    if (json_null == previous) {
+      /* this is our first element, so record it in our object. */
+      object->start = element;
+    }
+    else {
+      previous->next = element;
+    }
+
+    previous = element;
+
+    if (json_parse_flags_allow_location_information & flags_bitset) {
+      struct json_string_ex_s *string_ex =
+        (struct json_string_ex_s *)state->dom;
+      state->dom += sizeof(struct json_string_ex_s);
+
+      string_ex->offset = state->offset;
+      string_ex->line_no = state->line_no;
+      string_ex->row_no = state->offset - state->line_offset;
+
+      string = &(string_ex->string);
+    }
+    else {
+      string = (struct json_string_s *)state->dom;
+      state->dom += sizeof(struct json_string_s);
+    }
+
+    element->name = string;
+
+    (void)json_parse_key(state, string);
+
+    (void)json_skip_all_skippables(state);
+
+    /* skip colon or equals. */
+    state->offset++;
+
+    (void)json_skip_all_skippables(state);
+
+    if (json_parse_flags_allow_location_information & flags_bitset) {
+      struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state->dom;
+      state->dom += sizeof(struct json_value_ex_s);
+
+      value_ex->offset = state->offset;
+      value_ex->line_no = state->line_no;
+      value_ex->row_no = state->offset - state->line_offset;
+
+      value = &(value_ex->value);
+    }
+    else {
+      value = (struct json_value_s *)state->dom;
+      state->dom += sizeof(struct json_value_s);
+    }
+
+    element->value = value;
+
+    json_parse_value(state, /* is_global_object = */ 0, value);
+
+    /* successfully parsed a name/value pair! */
+    elements++;
+    allow_comma = 1;
+  }
+
+  /* if we had at least one element, end the linked list. */
+  if (previous) {
+    previous->next = json_null;
+  }
+
+  if (0 == elements) {
+    object->start = json_null;
+  }
+
+  object->length = elements;
+}
+
+json_weak void json_parse_array(struct json_parse_state_s *state,
+  struct json_array_s *array);
+void json_parse_array(struct json_parse_state_s *state,
+  struct json_array_s *array) {
+  const char *const src = state->src;
+  const size_t size = state->size;
+  size_t elements = 0;
+  int allow_comma = 0;
+  struct json_array_element_s *previous = json_null;
+
+  /* skip leading '['. */
+  state->offset++;
+
+  (void)json_skip_all_skippables(state);
+
+  /* reset elements. */
+  elements = 0;
+
+  do {
+    struct json_array_element_s *element = json_null;
+    struct json_value_s *value = json_null;
+
+    (void)json_skip_all_skippables(state);
+
+    if (']' == src[state->offset]) {
+      /* skip trailing ']'. */
+      state->offset++;
+
+      /* finished the array! */
+      break;
+    }
+
+    /* if we parsed at least one element previously, grok for a comma. */
+    if (allow_comma) {
+      if (',' == src[state->offset]) {
+        /* skip comma. */
+        state->offset++;
+        allow_comma = 0;
+        continue;
+      }
+    }
+
+    element = (struct json_array_element_s *)state->dom;
+
+    state->dom += sizeof(struct json_array_element_s);
+
+    if (json_null == previous) {
+      /* this is our first element, so record it in our array. */
+      array->start = element;
+    }
+    else {
+      previous->next = element;
+    }
+
+    previous = element;
+
+    if (json_parse_flags_allow_location_information & state->flags_bitset) {
+      struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state->dom;
+      state->dom += sizeof(struct json_value_ex_s);
+
+      value_ex->offset = state->offset;
+      value_ex->line_no = state->line_no;
+      value_ex->row_no = state->offset - state->line_offset;
+
+      value = &(value_ex->value);
+    }
+    else {
+      value = (struct json_value_s *)state->dom;
+      state->dom += sizeof(struct json_value_s);
+    }
+
+    element->value = value;
+
+    json_parse_value(state, /* is_global_object = */ 0, value);
+
+    /* successfully parsed an array element! */
+    elements++;
+    allow_comma = 1;
+  } while (state->offset < size);
+
+  /* end the linked list. */
+  if (previous) {
+    previous->next = json_null;
+  }
+
+  if (0 == elements) {
+    array->start = json_null;
+  }
+
+  array->length = elements;
+}
+
+json_weak void json_parse_number(struct json_parse_state_s *state,
+  struct json_number_s *number);
+void json_parse_number(struct json_parse_state_s *state,
+  struct json_number_s *number) {
+  const size_t flags_bitset = state->flags_bitset;
+  size_t offset = state->offset;
+  const size_t size = state->size;
+  size_t bytes_written = 0;
+  const char *const src = state->src;
+  char *data = state->data;
+
+  number->number = data;
+
+  if (json_parse_flags_allow_hexadecimal_numbers & flags_bitset) {
+    if (('0' == src[offset]) &&
+      (('x' == src[offset + 1]) || ('X' == src[offset + 1]))) {
+      /* consume hexadecimal digits. */
+      while ((offset < size) &&
+        (('0' <= src[offset] && src[offset] <= '9') ||
+        ('a' <= src[offset] && src[offset] <= 'f') ||
+          ('A' <= src[offset] && src[offset] <= 'F') ||
+          ('x' == src[offset]) || ('X' == src[offset]))) {
+        data[bytes_written++] = src[offset++];
+      }
+    }
+  }
+
+  while (offset < size) {
+    int end = 0;
+
+    switch (src[offset]) {
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+    case '.':
+    case 'e':
+    case 'E':
+    case '+':
+    case '-':
+      data[bytes_written++] = src[offset++];
+      break;
+    default:
+      end = 1;
+      break;
+    }
+
+    if (0 != end) {
+      break;
+    }
+  }
+
+  if (json_parse_flags_allow_inf_and_nan & flags_bitset) {
+    const size_t inf_strlen = 8; /* = strlen("Infinity");. */
+    const size_t nan_strlen = 3; /* = strlen("NaN");. */
+
+    if (offset + inf_strlen < size) {
+      if ('I' == src[offset]) {
+        size_t i;
+        /* We found our special 'Infinity' keyword! */
+        for (i = 0; i < inf_strlen; i++) {
+          data[bytes_written++] = src[offset++];
+        }
+      }
+    }
+
+    if (offset + nan_strlen < size) {
+      if ('N' == src[offset]) {
+        size_t i;
+        /* We found our special 'NaN' keyword! */
+        for (i = 0; i < nan_strlen; i++) {
+          data[bytes_written++] = src[offset++];
+        }
+      }
+    }
+  }
+
+  /* record the size of the number. */
+  number->number_size = bytes_written;
+  /* add null terminator to number string. */
+  data[bytes_written++] = '\0';
+  /* move data along. */
+  state->data += bytes_written;
+  /* update offset. */
+  state->offset = offset;
+}
+
+json_weak void json_parse_value(struct json_parse_state_s *state,
+  int is_global_object, struct json_value_s *value);
+void json_parse_value(struct json_parse_state_s *state,
+  int is_global_object, struct json_value_s *value) {
+  const size_t flags_bitset = state->flags_bitset;
+  const char *const src = state->src;
+  const size_t size = state->size;
+  size_t offset;
+
+  (void)json_skip_all_skippables(state);
+
+  /* cache offset now. */
+  offset = state->offset;
+
+  if (is_global_object) {
+    value->type = json_type_object;
+    value->payload = state->dom;
+    state->dom += sizeof(struct json_object_s);
+    json_parse_object(state, /* is_global_object = */ 1,
+      (struct json_object_s *)value->payload);
+  }
+  else {
+    switch (src[offset]) {
+    case '"':
+    case '\'':
+      value->type = json_type_string;
+      value->payload = state->dom;
+      state->dom += sizeof(struct json_string_s);
+      json_parse_string(state, (struct json_string_s *)value->payload);
+      break;
+    case '{':
+      value->type = json_type_object;
+      value->payload = state->dom;
+      state->dom += sizeof(struct json_object_s);
+      json_parse_object(state, /* is_global_object = */ 0,
+        (struct json_object_s *)value->payload);
+      break;
+    case '[':
+      value->type = json_type_array;
+      value->payload = state->dom;
+      state->dom += sizeof(struct json_array_s);
+      json_parse_array(state, (struct json_array_s *)value->payload);
+      break;
+    case '-':
+    case '+':
+    case '0':
+    case '1':
+    case '2':
+    case '3':
+    case '4':
+    case '5':
+    case '6':
+    case '7':
+    case '8':
+    case '9':
+    case '.':
+      value->type = json_type_number;
+      value->payload = state->dom;
+      state->dom += sizeof(struct json_number_s);
+      json_parse_number(state, (struct json_number_s *)value->payload);
+      break;
+    default:
+      if ((offset + 4) <= size && 't' == src[offset + 0] &&
+        'r' == src[offset + 1] && 'u' == src[offset + 2] &&
+        'e' == src[offset + 3]) {
+        value->type = json_type_true;
+        value->payload = json_null;
+        state->offset += 4;
+      }
+      else if ((offset + 5) <= size && 'f' == src[offset + 0] &&
+        'a' == src[offset + 1] && 'l' == src[offset + 2] &&
+        's' == src[offset + 3] && 'e' == src[offset + 4]) {
+        value->type = json_type_false;
+        value->payload = json_null;
+        state->offset += 5;
+      }
+      else if ((offset + 4) <= size && 'n' == src[offset + 0] &&
+        'u' == src[offset + 1] && 'l' == src[offset + 2] &&
+        'l' == src[offset + 3]) {
+        value->type = json_type_null;
+        value->payload = json_null;
+        state->offset += 4;
+      }
+      else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) &&
+        (offset + 3) <= size && 'N' == src[offset + 0] &&
+        'a' == src[offset + 1] && 'N' == src[offset + 2]) {
+        value->type = json_type_number;
+        value->payload = state->dom;
+        state->dom += sizeof(struct json_number_s);
+        json_parse_number(state, (struct json_number_s *)value->payload);
+      }
+      else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) &&
+        (offset + 8) <= size && 'I' == src[offset + 0] &&
+        'n' == src[offset + 1] && 'f' == src[offset + 2] &&
+        'i' == src[offset + 3] && 'n' == src[offset + 4] &&
+        'i' == src[offset + 5] && 't' == src[offset + 6] &&
+        'y' == src[offset + 7]) {
+        value->type = json_type_number;
+        value->payload = state->dom;
+        state->dom += sizeof(struct json_number_s);
+        json_parse_number(state, (struct json_number_s *)value->payload);
+      }
+      break;
+    }
+  }
+}
+
+struct json_value_s *
+  json_parse_ex(const void *src, size_t src_size, size_t flags_bitset,
+    void *(*alloc_func_ptr)(void *user_data, size_t size),
+    void *user_data, struct json_parse_result_s *result) {
+  struct json_parse_state_s state;
+  void *allocation;
+  struct json_value_s *value;
+  size_t total_size;
+  int input_error;
+
+  if (result) {
+    result->error = json_parse_error_none;
+    result->error_offset = 0;
+    result->error_line_no = 0;
+    result->error_row_no = 0;
+  }
+
+  if (json_null == src) {
+    /* invalid src pointer was null! */
+    return json_null;
+  }
+
+  state.src = (const char *)src;
+  state.size = src_size;
+  state.offset = 0;
+  state.line_no = 1;
+  state.line_offset = 0;
+  state.error = json_parse_error_none;
+  state.dom_size = 0;
+  state.data_size = 0;
+  state.flags_bitset = flags_bitset;
+
+  input_error = json_get_value_size(
+    &state, (int)(json_parse_flags_allow_global_object & state.flags_bitset));
+
+  if (0 == input_error) {
+    json_skip_all_skippables(&state);
+
+    if (state.offset != state.size) {
+      /* our parsing didn't have an error, but there are characters remaining in
+       * the input that weren't part of the JSON! */
+
+      state.error = json_parse_error_unexpected_trailing_characters;
+      input_error = 1;
+    }
+  }
+
+  if (input_error) {
+    /* parsing value's size failed (most likely an invalid JSON DOM!). */
+    if (result) {
+      result->error = state.error;
+      result->error_offset = state.offset;
+      result->error_line_no = state.line_no;
+      result->error_row_no = state.offset - state.line_offset;
+    }
+    return json_null;
+  }
+
+  /* our total allocation is the combination of the dom and data sizes (we. */
+  /* first encode the structure of the JSON, and then the data referenced by. */
+  /* the JSON values). */
+  total_size = state.dom_size + state.data_size;
+
+  if (json_null == alloc_func_ptr) {
+    allocation = malloc(total_size);
+  }
+  else {
+    allocation = alloc_func_ptr(user_data, total_size);
+  }
+
+  if (json_null == allocation) {
+    /* malloc failed! */
+    if (result) {
+      result->error = json_parse_error_allocator_failed;
+      result->error_offset = 0;
+      result->error_line_no = 0;
+      result->error_row_no = 0;
+    }
+
+    return json_null;
+  }
+
+  /* reset offset so we can reuse it. */
+  state.offset = 0;
+
+  /* reset the line information so we can reuse it. */
+  state.line_no = 1;
+  state.line_offset = 0;
+
+  state.dom = (char *)allocation;
+  state.data = state.dom + state.dom_size;
+
+  if (json_parse_flags_allow_location_information & state.flags_bitset) {
+    struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state.dom;
+    state.dom += sizeof(struct json_value_ex_s);
+
+    value_ex->offset = state.offset;
+    value_ex->line_no = state.line_no;
+    value_ex->row_no = state.offset - state.line_offset;
+
+    value = &(value_ex->value);
+  }
+  else {
+    value = (struct json_value_s *)state.dom;
+    state.dom += sizeof(struct json_value_s);
+  }
+
+  json_parse_value(
+    &state, (int)(json_parse_flags_allow_global_object & state.flags_bitset),
+    value);
+
+  return (struct json_value_s *)allocation;
+}
+
+struct json_value_s *json_parse(const void *src, size_t src_size) {
+  return json_parse_ex(src, src_size, json_parse_flags_default, json_null,
+    json_null, json_null);
+}
+
+struct json_string_s *json_value_as_string(struct json_value_s *const value) {
+  if (value->type != json_type_string) {
+    return json_null;
+  }
+
+  return (struct json_string_s *)value->payload;
+}
+
+struct json_number_s *json_value_as_number(struct json_value_s *const value) {
+  if (value->type != json_type_number) {
+    return json_null;
+  }
+
+  return (struct json_number_s *)value->payload;
+}
+
+struct json_object_s *json_value_as_object(struct json_value_s *const value) {
+  if (value->type != json_type_object) {
+    return json_null;
+  }
+
+  return (struct json_object_s *)value->payload;
+}
+
+struct json_array_s *json_value_as_array(struct json_value_s *const value) {
+  if (value->type != json_type_array) {
+    return json_null;
+  }
+
+  return (struct json_array_s *)value->payload;
+}
+
+int json_value_is_true(const struct json_value_s *const value) {
+  return value->type == json_type_true;
+}
+
+int json_value_is_false(const struct json_value_s *const value) {
+  return value->type == json_type_false;
+}
+
+int json_value_is_null(const struct json_value_s *const value) {
+  return value->type == json_type_null;
+}
+
+json_weak int json_write_minified_get_value_size(const struct json_value_s *value,
+  size_t *size);
+
+json_weak int json_write_get_number_size(const struct json_number_s *number,
+  size_t *size);
+int json_write_get_number_size(const struct json_number_s *number,
+  size_t *size) {
+  json_uintmax_t parsed_number;
+  size_t i;
+
+  if (number->number_size >= 2) {
+    switch (number->number[1]) {
+    default:
+      break;
+    case 'x':
+    case 'X':
+      /* the number is a json_parse_flags_allow_hexadecimal_numbers hexadecimal
+       * so we have to do extra work to convert it to a non-hexadecimal for JSON
+       * output. */
+      parsed_number = json_strtoumax(number->number, json_null, 0);
+
+      i = 0;
+
+      while (0 != parsed_number) {
+        parsed_number /= 10;
+        i++;
+      }
+
+      *size += i;
+      return 0;
+    }
+  }
+
+  /* check to see if the number has leading/trailing decimal point. */
+  i = 0;
+
+  /* skip any leading '+' or '-'. */
+  if ((i < number->number_size) &&
+    (('+' == number->number[i]) || ('-' == number->number[i]))) {
+    i++;
+  }
+
+  /* check if we have infinity. */
+  if ((i < number->number_size) && ('I' == number->number[i])) {
+    const char *inf = "Infinity";
+    size_t k;
+
+    for (k = i; k < number->number_size; k++) {
+      const char c = *inf++;
+
+      /* Check if we found the Infinity string! */
+      if ('\0' == c) {
+        break;
+      }
+      else if (c != number->number[k]) {
+        break;
+      }
+    }
+
+    if ('\0' == *inf) {
+      /* Inf becomes 1.7976931348623158e308 because JSON can't support it. */
+      *size += 22;
+
+      /* if we had a leading '-' we need to record it in the JSON output. */
+      if ('-' == number->number[0]) {
+        *size += 1;
+      }
+    }
+
+    return 0;
+  }
+
+  /* check if we have nan. */
+  if ((i < number->number_size) && ('N' == number->number[i])) {
+    const char *nan = "NaN";
+    size_t k;
+
+    for (k = i; k < number->number_size; k++) {
+      const char c = *nan++;
+
+      /* Check if we found the NaN string! */
+      if ('\0' == c) {
+        break;
+      }
+      else if (c != number->number[k]) {
+        break;
+      }
+    }
+
+    if ('\0' == *nan) {
+      /* NaN becomes 1 because JSON can't support it. */
+      *size += 1;
+
+      return 0;
+    }
+  }
+
+  /* if we had a leading decimal point. */
+  if ((i < number->number_size) && ('.' == number->number[i])) {
+    /* 1 + because we had a leading decimal point. */
+    *size += 1;
+    goto cleanup;
+  }
+
+  for (; i < number->number_size; i++) {
+    const char c = number->number[i];
+    if (!('0' <= c && c <= '9')) {
+      break;
+    }
+  }
+
+  /* if we had a trailing decimal point. */
+  if ((i + 1 == number->number_size) && ('.' == number->number[i])) {
+    /* 1 + because we had a trailing decimal point. */
+    *size += 1;
+    goto cleanup;
+  }
+
+cleanup:
+  *size += number->number_size; /* the actual string of the number. */
+
+  /* if we had a leading '+' we don't record it in the JSON output. */
+  if ('+' == number->number[0]) {
+    *size -= 1;
+  }
+
+  return 0;
+}
+
+json_weak int json_write_get_string_size(const struct json_string_s *string,
+  size_t *size);
+int json_write_get_string_size(const struct json_string_s *string,
+  size_t *size) {
+  size_t i;
+  for (i = 0; i < string->string_size; i++) {
+    switch (string->string[i]) {
+    case '"':
+    case '\\':
+    case '\b':
+    case '\f':
+    case '\n':
+    case '\r':
+    case '\t':
+      *size += 2;
+      break;
+    default:
+      *size += 1;
+      break;
+    }
+  }
+
+  *size += 2; /* need to encode the surrounding '"' characters. */
+
+  return 0;
+}
+
+json_weak int json_write_minified_get_array_size(const struct json_array_s *array,
+  size_t *size);
+int json_write_minified_get_array_size(const struct json_array_s *array,
+  size_t *size) {
+  struct json_array_element_s *element;
+
+  *size += 2; /* '[' and ']'. */
+
+  if (1 < array->length) {
+    *size += array->length - 1; /* ','s seperate each element. */
+  }
+
+  for (element = array->start; json_null != element; element = element->next) {
+    if (json_write_minified_get_value_size(element->value, size)) {
+      /* value was malformed! */
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+json_weak int
+json_write_minified_get_object_size(const struct json_object_s *object,
+  size_t *size);
+int
+json_write_minified_get_object_size(const struct json_object_s *object,
+  size_t *size) {
+  struct json_object_element_s *element;
+
+  *size += 2; /* '{' and '}'. */
+
+  *size += object->length; /* ':'s seperate each name/value pair. */
+
+  if (1 < object->length) {
+    *size += object->length - 1; /* ','s seperate each element. */
+  }
+
+  for (element = object->start; json_null != element; element = element->next) {
+    if (json_write_get_string_size(element->name, size)) {
+      /* string was malformed! */
+      return 1;
+    }
+
+    if (json_write_minified_get_value_size(element->value, size)) {
+      /* value was malformed! */
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+json_weak int json_write_minified_get_value_size(const struct json_value_s *value,
+  size_t *size);
+int json_write_minified_get_value_size(const struct json_value_s *value,
+  size_t *size) {
+  switch (value->type) {
+  default:
+    /* unknown value type found! */
+    return 1;
+  case json_type_number:
+    return json_write_get_number_size((struct json_number_s *)value->payload,
+      size);
+  case json_type_string:
+    return json_write_get_string_size((struct json_string_s *)value->payload,
+      size);
+  case json_type_array:
+    return json_write_minified_get_array_size(
+      (struct json_array_s *)value->payload, size);
+  case json_type_object:
+    return json_write_minified_get_object_size(
+      (struct json_object_s *)value->payload, size);
+  case json_type_true:
+    *size += 4; /* the string "true". */
+    return 0;
+  case json_type_false:
+    *size += 5; /* the string "false". */
+    return 0;
+  case json_type_null:
+    *size += 4; /* the string "null". */
+    return 0;
+  }
+}
+
+json_weak char *json_write_minified_value(const struct json_value_s *value,
+  char *data);
+
+json_weak char *json_write_number(const struct json_number_s *number, char *data);
+char *json_write_number(const struct json_number_s *number, char *data) {
+  json_uintmax_t parsed_number, backup;
+  size_t i;
+
+  if (number->number_size >= 2) {
+    switch (number->number[1]) {
+    default:
+      break;
+    case 'x':
+    case 'X':
+      /* The number is a json_parse_flags_allow_hexadecimal_numbers hexadecimal
+       * so we have to do extra work to convert it to a non-hexadecimal for JSON
+       * output. */
+      parsed_number = json_strtoumax(number->number, json_null, 0);
+
+      /* We need a copy of parsed number twice, so take a backup of it. */
+      backup = parsed_number;
+
+      i = 0;
+
+      while (0 != parsed_number) {
+        parsed_number /= 10;
+        i++;
+      }
+
+      /* Restore parsed_number to its original value stored in the backup. */
+      parsed_number = backup;
+
+      /* Now use backup to take a copy of i, or the length of the string. */
+      backup = i;
+
+      do {
+        *(data + i - 1) = '0' + (char)(parsed_number % 10);
+        parsed_number /= 10;
+        i--;
+      } while (0 != parsed_number);
+
+      data += backup;
+
+      return data;
+    }
+  }
+
+  /* check to see if the number has leading/trailing decimal point. */
+  i = 0;
+
+  /* skip any leading '-'. */
+  if ((i < number->number_size) &&
+    (('+' == number->number[i]) || ('-' == number->number[i]))) {
+    i++;
+  }
+
+  /* check if we have infinity. */
+  if ((i < number->number_size) && ('I' == number->number[i])) {
+    const char *inf = "Infinity";
+    size_t k;
+
+    for (k = i; k < number->number_size; k++) {
+      const char c = *inf++;
+
+      /* Check if we found the Infinity string! */
+      if ('\0' == c) {
+        break;
+      }
+      else if (c != number->number[k]) {
+        break;
+      }
+    }
+
+    if ('\0' == *inf++) {
+      const char *dbl_max;
+
+      /* if we had a leading '-' we need to record it in the JSON output. */
+      if ('-' == number->number[0]) {
+        *data++ = '-';
+      }
+
+      /* Inf becomes 1.7976931348623158e308 because JSON can't support it. */
+      for (dbl_max = "1.7976931348623158e308"; '\0' != *dbl_max; dbl_max++) {
+        *data++ = *dbl_max;
+      }
+
+      return data;
+    }
+  }
+
+  /* check if we have nan. */
+  if ((i < number->number_size) && ('N' == number->number[i])) {
+    const char *nan = "NaN";
+    size_t k;
+
+    for (k = i; k < number->number_size; k++) {
+      const char c = *nan++;
+
+      /* Check if we found the NaN string! */
+      if ('\0' == c) {
+        break;
+      }
+      else if (c != number->number[k]) {
+        break;
+      }
+    }
+
+    if ('\0' == *nan++) {
+      /* NaN becomes 0 because JSON can't support it. */
+      *data++ = '0';
+      return data;
+    }
+  }
+
+  /* if we had a leading decimal point. */
+  if ((i < number->number_size) && ('.' == number->number[i])) {
+    i = 0;
+
+    /* skip any leading '+'. */
+    if ('+' == number->number[i]) {
+      i++;
+    }
+
+    /* output the leading '-' if we had one. */
+    if ('-' == number->number[i]) {
+      *data++ = '-';
+      i++;
+    }
+
+    /* insert a '0' to fix the leading decimal point for JSON output. */
+    *data++ = '0';
+
+    /* and output the rest of the number as normal. */
+    for (; i < number->number_size; i++) {
+      *data++ = number->number[i];
+    }
+
+    return data;
+  }
+
+  for (; i < number->number_size; i++) {
+    const char c = number->number[i];
+    if (!('0' <= c && c <= '9')) {
+      break;
+    }
+  }
+
+  /* if we had a trailing decimal point. */
+  if ((i + 1 == number->number_size) && ('.' == number->number[i])) {
+    i = 0;
+
+    /* skip any leading '+'. */
+    if ('+' == number->number[i]) {
+      i++;
+    }
+
+    /* output the leading '-' if we had one. */
+    if ('-' == number->number[i]) {
+      *data++ = '-';
+      i++;
+    }
+
+    /* and output the rest of the number as normal. */
+    for (; i < number->number_size; i++) {
+      *data++ = number->number[i];
+    }
+
+    /* insert a '0' to fix the trailing decimal point for JSON output. */
+    *data++ = '0';
+
+    return data;
+  }
+
+  i = 0;
+
+  /* skip any leading '+'. */
+  if ('+' == number->number[i]) {
+    i++;
+  }
+
+  for (; i < number->number_size; i++) {
+    *data++ = number->number[i];
+  }
+
+  return data;
+}
+
+json_weak char *json_write_string(const struct json_string_s *string, char *data);
+char *json_write_string(const struct json_string_s *string, char *data) {
+  size_t i;
+
+  *data++ = '"'; /* open the string. */
+
+  for (i = 0; i < string->string_size; i++) {
+    switch (string->string[i]) {
+    case '"':
+      *data++ = '\\'; /* escape the control character. */
+      *data++ = '"';
+      break;
+    case '\\':
+      *data++ = '\\'; /* escape the control character. */
+      *data++ = '\\';
+      break;
+    case '\b':
+      *data++ = '\\'; /* escape the control character. */
+      *data++ = 'b';
+      break;
+    case '\f':
+      *data++ = '\\'; /* escape the control character. */
+      *data++ = 'f';
+      break;
+    case '\n':
+      *data++ = '\\'; /* escape the control character. */
+      *data++ = 'n';
+      break;
+    case '\r':
+      *data++ = '\\'; /* escape the control character. */
+      *data++ = 'r';
+      break;
+    case '\t':
+      *data++ = '\\'; /* escape the control character. */
+      *data++ = 't';
+      break;
+    default:
+      *data++ = string->string[i];
+      break;
+    }
+  }
+
+  *data++ = '"'; /* close the string. */
+
+  return data;
+}
+
+json_weak char *json_write_minified_array(const struct json_array_s *array,
+  char *data);
+char *json_write_minified_array(const struct json_array_s *array,
+  char *data) {
+  struct json_array_element_s *element = json_null;
+
+  *data++ = '['; /* open the array. */
+
+  for (element = array->start; json_null != element; element = element->next) {
+    if (element != array->start) {
+      *data++ = ','; /* ','s seperate each element. */
+    }
+
+    data = json_write_minified_value(element->value, data);
+
+    if (json_null == data) {
+      /* value was malformed! */
+      return json_null;
+    }
+  }
+
+  *data++ = ']'; /* close the array. */
+
+  return data;
+}
+
+json_weak char *json_write_minified_object(const struct json_object_s *object,
+  char *data);
+char *json_write_minified_object(const struct json_object_s *object,
+  char *data) {
+  struct json_object_element_s *element = json_null;
+
+  *data++ = '{'; /* open the object. */
+
+  for (element = object->start; json_null != element;
+    element = element->next) {
+    if (element != object->start) {
+      *data++ = ','; /* ','s seperate each element. */
+    }
+
+    data = json_write_string(element->name, data);
+
+    if (json_null == data) {
+      /* string was malformed! */
+      return json_null;
+    }
+
+    *data++ = ':'; /* ':'s seperate each name/value pair. */
+
+    data = json_write_minified_value(element->value, data);
+
+    if (json_null == data) {
+      /* value was malformed! */
+      return json_null;
+    }
+  }
+
+  *data++ = '}'; /* close the object. */
+
+  return data;
+}
+
+json_weak char *json_write_minified_value(const struct json_value_s *value,
+  char *data);
+char *json_write_minified_value(const struct json_value_s *value,
+  char *data) {
+  switch (value->type) {
+  default:
+    /* unknown value type found! */
+    return json_null;
+  case json_type_number:
+    return json_write_number((struct json_number_s *)value->payload, data);
+  case json_type_string:
+    return json_write_string((struct json_string_s *)value->payload, data);
+  case json_type_array:
+    return json_write_minified_array((struct json_array_s *)value->payload,
+      data);
+  case json_type_object:
+    return json_write_minified_object((struct json_object_s *)value->payload,
+      data);
+  case json_type_true:
+    data[0] = 't';
+    data[1] = 'r';
+    data[2] = 'u';
+    data[3] = 'e';
+    return data + 4;
+  case json_type_false:
+    data[0] = 'f';
+    data[1] = 'a';
+    data[2] = 'l';
+    data[3] = 's';
+    data[4] = 'e';
+    return data + 5;
+  case json_type_null:
+    data[0] = 'n';
+    data[1] = 'u';
+    data[2] = 'l';
+    data[3] = 'l';
+    return data + 4;
+  }
+}
+
+void *json_write_minified(const struct json_value_s *value, size_t *out_size) {
+  size_t size = 0;
+  char *data = json_null;
+  char *data_end = json_null;
+
+  if (json_null == value) {
+    return json_null;
+  }
+
+  if (json_write_minified_get_value_size(value, &size)) {
+    /* value was malformed! */
+    return json_null;
+  }
+
+  size += 1; /* for the '\0' null terminating character. */
+
+  data = (char *)malloc(size);
+
+  if (json_null == data) {
+    /* malloc failed! */
+    return json_null;
+  }
+
+  data_end = json_write_minified_value(value, data);
+
+  if (json_null == data_end) {
+    /* bad chi occurred! */
+    free(data);
+    return json_null;
+  }
+
+  /* null terminated the string. */
+  *data_end = '\0';
+
+  if (json_null != out_size) {
+    *out_size = size;
+  }
+
+  return data;
+}
+
+json_weak int json_write_pretty_get_value_size(const struct json_value_s *value,
+  size_t depth, size_t indent_size,
+  size_t newline_size, size_t *size);
+
+json_weak int json_write_pretty_get_array_size(const struct json_array_s *array,
+  size_t depth, size_t indent_size,
+  size_t newline_size, size_t *size);
+int json_write_pretty_get_array_size(const struct json_array_s *array,
+  size_t depth, size_t indent_size,
+  size_t newline_size, size_t *size) {
+  struct json_array_element_s *element;
+
+  *size += 1; /* '['. */
+
+  if (0 < array->length) {
+    /* if we have any elements we need to add a newline after our '['. */
+    *size += newline_size;
+
+    *size += array->length - 1; /* ','s seperate each element. */
+
+    for (element = array->start; json_null != element;
+      element = element->next) {
+      /* each element gets an indent. */
+      *size += (depth + 1) * indent_size;
+
+      if (json_write_pretty_get_value_size(element->value, depth + 1,
+        indent_size, newline_size, size)) {
+        /* value was malformed! */
+        return 1;
+      }
+
+      /* each element gets a newline too. */
+      *size += newline_size;
+    }
+
+    /* since we wrote out some elements, need to add a newline and indentation.
+     */
+     /* to the trailing ']'. */
+    *size += depth * indent_size;
+  }
+
+  *size += 1; /* ']'. */
+
+  return 0;
+}
+
+json_weak int json_write_pretty_get_object_size(const struct json_object_s *object,
+  size_t depth, size_t indent_size,
+  size_t newline_size,
+  size_t *size);
+int json_write_pretty_get_object_size(const struct json_object_s *object,
+  size_t depth, size_t indent_size,
+  size_t newline_size,
+  size_t *size) {
+  struct json_object_element_s *element;
+
+  *size += 1; /* '{'. */
+
+  if (0 < object->length) {
+    *size += newline_size; /* need a newline next. */
+
+    *size += object->length - 1; /* ','s seperate each element. */
+
+    for (element = object->start; json_null != element;
+      element = element->next) {
+      /* each element gets an indent and newline. */
+      *size += (depth + 1) * indent_size;
+      *size += newline_size;
+
+      if (json_write_get_string_size(element->name, size)) {
+        /* string was malformed! */
+        return 1;
+      }
+
+      *size += 3; /* seperate each name/value pair with " : ". */
+
+      if (json_write_pretty_get_value_size(element->value, depth + 1,
+        indent_size, newline_size, size)) {
+        /* value was malformed! */
+        return 1;
+      }
+    }
+
+    *size += depth * indent_size;
+  }
+
+  *size += 1; /* '}'. */
+
+  return 0;
+}
+
+json_weak int json_write_pretty_get_value_size(const struct json_value_s *value,
+  size_t depth, size_t indent_size,
+  size_t newline_size, size_t *size);
+int json_write_pretty_get_value_size(const struct json_value_s *value,
+  size_t depth, size_t indent_size,
+  size_t newline_size, size_t *size) {
+  switch (value->type) {
+  default:
+    /* unknown value type found! */
+    return 1;
+  case json_type_number:
+    return json_write_get_number_size((struct json_number_s *)value->payload,
+      size);
+  case json_type_string:
+    return json_write_get_string_size((struct json_string_s *)value->payload,
+      size);
+  case json_type_array:
+    return json_write_pretty_get_array_size(
+      (struct json_array_s *)value->payload, depth, indent_size, newline_size,
+      size);
+  case json_type_object:
+    return json_write_pretty_get_object_size(
+      (struct json_object_s *)value->payload, depth, indent_size,
+      newline_size, size);
+  case json_type_true:
+    *size += 4; /* the string "true". */
+    return 0;
+  case json_type_false:
+    *size += 5; /* the string "false". */
+    return 0;
+  case json_type_null:
+    *size += 4; /* the string "null". */
+    return 0;
+  }
+}
+
+json_weak char *json_write_pretty_value(const struct json_value_s *value,
+  size_t depth, const char *indent,
+  const char *newline, char *data);
+
+json_weak char *json_write_pretty_array(const struct json_array_s *array,
+  size_t depth, const char *indent,
+  const char *newline, char *data);
+char *json_write_pretty_array(const struct json_array_s *array,
+  size_t depth, const char *indent,
+  const char *newline, char *data) {
+  size_t k, m;
+  struct json_array_element_s *element;
+
+  *data++ = '['; /* open the array. */
+
+  if (0 < array->length) {
+    for (k = 0; '\0' != newline[k]; k++) {
+      *data++ = newline[k];
+    }
+
+    for (element = array->start; json_null != element;
+      element = element->next) {
+      if (element != array->start) {
+        *data++ = ','; /* ','s seperate each element. */
+
+        for (k = 0; '\0' != newline[k]; k++) {
+          *data++ = newline[k];
+        }
+      }
+
+      for (k = 0; k < depth + 1; k++) {
+        for (m = 0; '\0' != indent[m]; m++) {
+          *data++ = indent[m];
+        }
+      }
+
+      data = json_write_pretty_value(element->value, depth + 1, indent, newline,
+        data);
+
+      if (json_null == data) {
+        /* value was malformed! */
+        return json_null;
+      }
+    }
+
+    for (k = 0; '\0' != newline[k]; k++) {
+      *data++ = newline[k];
+    }
+
+    for (k = 0; k < depth; k++) {
+      for (m = 0; '\0' != indent[m]; m++) {
+        *data++ = indent[m];
+      }
+    }
+  }
+
+  *data++ = ']'; /* close the array. */
+
+  return data;
+}
+
+json_weak char *json_write_pretty_object(const struct json_object_s *object,
+  size_t depth, const char *indent,
+  const char *newline, char *data);
+char *json_write_pretty_object(const struct json_object_s *object,
+  size_t depth, const char *indent,
+  const char *newline, char *data) {
+  size_t k, m;
+  struct json_object_element_s *element;
+
+  *data++ = '{'; /* open the object. */
+
+  if (0 < object->length) {
+    for (k = 0; '\0' != newline[k]; k++) {
+      *data++ = newline[k];
+    }
+
+    for (element = object->start; json_null != element;
+      element = element->next) {
+      if (element != object->start) {
+        *data++ = ','; /* ','s seperate each element. */
+
+        for (k = 0; '\0' != newline[k]; k++) {
+          *data++ = newline[k];
+        }
+      }
+
+      for (k = 0; k < depth + 1; k++) {
+        for (m = 0; '\0' != indent[m]; m++) {
+          *data++ = indent[m];
+        }
+      }
+
+      data = json_write_string(element->name, data);
+
+      if (json_null == data) {
+        /* string was malformed! */
+        return json_null;
+      }
+
+      /* " : "s seperate each name/value pair. */
+      *data++ = ' ';
+      *data++ = ':';
+      *data++ = ' ';
+
+      data = json_write_pretty_value(element->value, depth + 1, indent, newline,
+        data);
+
+      if (json_null == data) {
+        /* value was malformed! */
+        return json_null;
+      }
+    }
+
+    for (k = 0; '\0' != newline[k]; k++) {
+      *data++ = newline[k];
+    }
+
+    for (k = 0; k < depth; k++) {
+      for (m = 0; '\0' != indent[m]; m++) {
+        *data++ = indent[m];
+      }
+    }
+  }
+
+  *data++ = '}'; /* close the object. */
+
+  return data;
+}
+
+json_weak char *json_write_pretty_value(const struct json_value_s *value,
+  size_t depth, const char *indent,
+  const char *newline, char *data);
+char *json_write_pretty_value(const struct json_value_s *value,
+  size_t depth, const char *indent,
+  const char *newline, char *data) {
+  switch (value->type) {
+  default:
+    /* unknown value type found! */
+    return json_null;
+  case json_type_number:
+    return json_write_number((struct json_number_s *)value->payload, data);
+  case json_type_string:
+    return json_write_string((struct json_string_s *)value->payload, data);
+  case json_type_array:
+    return json_write_pretty_array((struct json_array_s *)value->payload, depth,
+      indent, newline, data);
+  case json_type_object:
+    return json_write_pretty_object((struct json_object_s *)value->payload,
+      depth, indent, newline, data);
+  case json_type_true:
+    data[0] = 't';
+    data[1] = 'r';
+    data[2] = 'u';
+    data[3] = 'e';
+    return data + 4;
+  case json_type_false:
+    data[0] = 'f';
+    data[1] = 'a';
+    data[2] = 'l';
+    data[3] = 's';
+    data[4] = 'e';
+    return data + 5;
+  case json_type_null:
+    data[0] = 'n';
+    data[1] = 'u';
+    data[2] = 'l';
+    data[3] = 'l';
+    return data + 4;
+  }
+}
+
+void *json_write_pretty(const struct json_value_s *value, const char *indent,
+  const char *newline, size_t *out_size) {
+  size_t size = 0;
+  size_t indent_size = 0;
+  size_t newline_size = 0;
+  char *data = json_null;
+  char *data_end = json_null;
+
+  if (json_null == value) {
+    return json_null;
+  }
+
+  if (json_null == indent) {
+    indent = "  "; /* default to two spaces. */
+  }
+
+  if (json_null == newline) {
+    newline = "\n"; /* default to linux newlines. */
+  }
+
+  while ('\0' != indent[indent_size]) {
+    ++indent_size; /* skip non-null terminating characters. */
+  }
+
+  while ('\0' != newline[newline_size]) {
+    ++newline_size; /* skip non-null terminating characters. */
+  }
+
+  if (json_write_pretty_get_value_size(value, 0, indent_size, newline_size,
+    &size)) {
+    /* value was malformed! */
+    return json_null;
+  }
+
+  size += 1; /* for the '\0' null terminating character. */
+
+  data = (char *)malloc(size);
+
+  if (json_null == data) {
+    /* malloc failed! */
+    return json_null;
+  }
+
+  data_end = json_write_pretty_value(value, 0, indent, newline, data);
+
+  if (json_null == data_end) {
+    /* bad chi occurred! */
+    free(data);
+    return json_null;
+  }
+
+  /* null terminated the string. */
+  *data_end = '\0';
+
+  if (json_null != out_size) {
+    *out_size = size;
+  }
+
+  return data;
+}
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#elif defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+#endif /* SHEREDOM_JSON_H_INCLUDED. */
index ef0071d..0d04753 100644 (file)
@@ -103,7 +103,7 @@ size_t TreeNode::Size() const
   return c;
 }
 
-size_t TreeNode::Count(const std::string& childName) const
+size_t TreeNode::Count(std::string_view childName) const
 {
   const TreeNode* c = GetChild(childName);
   if(c)
@@ -116,12 +116,12 @@ size_t TreeNode::Count(const std::string& childName) const
   }
 }
 
-const TreeNode* TreeNode::GetChild(const std::string& childName) const
+const TreeNode* TreeNode::GetChild(std::string_view childName) const
 {
   const TreeNode* p = mFirstChild;
   while(p)
   {
-    if(p->mName && (std::string(p->mName) == childName))
+    if(p->mName && (std::string_view(p->mName) == childName))
     {
       return p;
     }
@@ -130,7 +130,7 @@ const TreeNode* TreeNode::GetChild(const std::string& childName) const
   return NULL;
 }
 
-const TreeNode* TreeNode::GetChildIgnoreCase(const std::string& childName) const
+const TreeNode* TreeNode::GetChildIgnoreCase(std::string_view childName) const
 {
   const TreeNode* p = mFirstChild;
   while(p)
@@ -144,9 +144,9 @@ const TreeNode* TreeNode::GetChildIgnoreCase(const std::string& childName) const
   return NULL;
 }
 
-const TreeNode* TreeNode::Find(const std::string& childName) const
+const TreeNode* TreeNode::Find(std::string_view childName) const
 {
-  if(mName && std::string(mName) == childName)
+  if(mName && std::string_view(mName) == childName)
   {
     return this;
   }
index e7e511e..6246b16 100644 (file)
@@ -130,7 +130,7 @@ public:
    * @param childName The name of the child to find
    * @return the number of children in the found child
    */
-  size_t Count(const std::string& childName) const;
+  size_t Count(std::string_view childName) const;
 
   /*
    * Get the nodes name
@@ -184,21 +184,21 @@ public:
    * @param name The name of the child.
    * @return The child if found, else NULL
    */
-  const TreeNode* GetChild(const std::string& name) const;
+  const TreeNode* GetChild(std::string_view name) const;
 
   /*
    * Gets a child of the node (using case insensitive matching)
    * @param name The name of the child in lower case
    * @return The child if found, else NULL
    */
-  const TreeNode* GetChildIgnoreCase(const std::string& name) const;
+  const TreeNode* GetChildIgnoreCase(std::string_view name) const;
 
   /*
    * Recursively search for a child of the node
    * @param name The name of the child
    * @return The child if found, else NULL
    */
-  const TreeNode* Find(const std::string& name) const;
+  const TreeNode* Find(std::string_view name) const;
 
 private:
   friend class Internal::TreeNodeManipulator;
index e03515b..8be5a94 100644 (file)
@@ -39,21 +39,21 @@ enum
 {
   /**
    * @brief The thickness of the arc.
-   * @details Name "thickness", type Property::FLOAT.
+   * @details Name "thickness", type Property::FLOAT, animatable.
    * @note Mandatory.
    */
   THICKNESS = VISUAL_PROPERTY_START_INDEX,
 
   /**
    * @brief The start angle where the arc begins in degrees.
-   * @details Name "startAngle", type Property::FLOAT.
+   * @details Name "startAngle", type Property::FLOAT, animatable.
    * @note Optional. If not specified, the default is 0.
    */
   START_ANGLE,
 
   /**
    * @brief The sweep angle of the arc in degrees.
-   * @details Name "sweepAngle", type Property::FLOAT.
+   * @details Name "sweepAngle", type Property::FLOAT, animatable.
    * The arc starts at a specified start angle and sweeps clockwise, drawing slices of pie.
    * @note Optional. If not specified, the default is 360.
    */
index a10a3f8..413b556 100644 (file)
@@ -104,14 +104,14 @@ enum Type
   PLAY_STATE = ORIENTATION_CORRECTION + 5,
 
   /**
-   * @brief The current frame number the AnimatedVectorImageVisual will use.
+   * @brief The current frame number the AnimatedImageVisual and AnimatedVectorImageVisual will use.
    * @details Name "currentFrameNumber", Type Property::INTEGER, between [0, the maximum frame number] or between the play range if specified
    * @note This property is read-only.
    */
   CURRENT_FRAME_NUMBER = ORIENTATION_CORRECTION + 6,
 
   /**
-   * @brief The total frame number the AnimatedVectorImageVisual will use.
+   * @brief The total frame number the AnimatedImageVisual and AnimatedVectorImageVisual will use.
    * @details Name "totalFrameNumber", Type Property::INTEGER.
    * @note This property is read-only.
    */
index 9567306..06ba033 100644 (file)
@@ -549,8 +549,7 @@ void TreeNodeManipulator::DoWrite(const TreeNode *value, std::ostream& output, i
   } // switch
 } // DoWrite
 
-
-const TreeNode* FindIt(const std::string& childName, const TreeNode* node)
+const TreeNode* FindIt(std::string_view childName, const TreeNode* node)
 {
   DALI_ASSERT_DEBUG(node);
 
index eaea13e..cc9e98b 100644 (file)
@@ -241,7 +241,7 @@ void DepthFirst( TreeNode* node, Operation& operation)
  * @param tree The tree to search
  * @return the TreeNode if found, else NULL
  */
-const TreeNode* FindIt(const std::string& childName, const TreeNode* tree);
+const TreeNode* FindIt(std::string_view childName, const TreeNode* tree);
 
 /*
  * Copy string to a buffer
index 2c9dca5..b10842b 100755 (executable)
@@ -47,6 +47,7 @@
 #include <dali-toolkit/internal/visuals/visual-string-constants.h>
 #include <dali-toolkit/public-api/focus-manager/keyboard-focus-manager.h>
 #include <dali-toolkit/public-api/controls/image-view/image-view.h>
+#include <dali-toolkit/devel-api/asset-manager/asset-manager.h>
 
 namespace
 {
@@ -104,25 +105,30 @@ void Remove( DictionaryKeys& keys, const std::string& name )
   }
 }
 
-Toolkit::Visual::Type GetVisualTypeFromMap( const Property::Map& map )
+/**
+ *  Finds visual in given array, returning true if found along with the iterator for that visual as a out parameter
+ */
+bool FindVisual( Property::Index targetIndex, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter )
 {
-  Property::Value* typeValue = map.Find( Toolkit::Visual::Property::TYPE, VISUAL_TYPE  );
-  Toolkit::Visual::Type type = Toolkit::Visual::IMAGE;
-  if( typeValue )
+  for ( iter = visuals.Begin(); iter != visuals.End(); iter++ )
   {
-    Scripting::GetEnumerationProperty( *typeValue, VISUAL_TYPE_TABLE, VISUAL_TYPE_TABLE_COUNT, type );
+    if ( (*iter)->index ==  targetIndex )
+    {
+      return true;
+    }
   }
-  return type;
+  return false;
 }
 
 /**
  *  Finds visual in given array, returning true if found along with the iterator for that visual as a out parameter
  */
-bool FindVisual( Property::Index targetIndex, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter )
+bool FindVisual( std::string visualName, const RegisteredVisualContainer& visuals, RegisteredVisualContainer::Iterator& iter )
 {
   for ( iter = visuals.Begin(); iter != visuals.End(); iter++ )
   {
-    if ( (*iter)->index ==  targetIndex )
+    Toolkit::Visual::Base visual = (*iter)->visual;
+    if( visual && visual.GetName() == visualName )
     {
       return true;
     }
@@ -1558,59 +1564,47 @@ void Control::Impl::RecreateChangedVisuals( Dictionary<Property::Map>& stateVisu
     const std::string& visualName = (*iter).key;
     const Property::Map& toMap = (*iter).entry;
 
-    // is it a candidate for re-creation?
-    bool recreate = false;
-
-    Toolkit::Visual::Base visual = GetVisualByName( mVisuals, visualName );
-    if( visual )
+    Actor self = mControlImpl.Self();
+    RegisteredVisualContainer::Iterator registeredVisualsiter;
+    // Check if visual (visualName) is already registered, this is the current visual.
+    if(FindVisual(visualName, mVisuals, registeredVisualsiter))
     {
-      Property::Map fromMap;
-      visual.CreatePropertyMap( fromMap );
-
-      Toolkit::Visual::Type fromType = GetVisualTypeFromMap( fromMap );
-      Toolkit::Visual::Type toType = GetVisualTypeFromMap( toMap );
-
-      if( fromType != toType )
+      Toolkit::Visual::Base& visual = (*registeredVisualsiter)->visual;
+      if(visual)
       {
-        recreate = true;
-      }
-      else
-      {
-        if( fromType == Toolkit::Visual::IMAGE || fromType == Toolkit::Visual::N_PATCH
-            || fromType == Toolkit::Visual::SVG || fromType == Toolkit::Visual::ANIMATED_IMAGE )
-        {
-          Property::Value* fromUrl = fromMap.Find( Toolkit::ImageVisual::Property::URL, IMAGE_URL_NAME );
-          Property::Value* toUrl = toMap.Find( Toolkit::ImageVisual::Property::URL, IMAGE_URL_NAME );
+        // No longer required to know if the replaced visual's resources are ready
+        StopObservingVisual(visual);
 
-          if( fromUrl && toUrl )
+        // If control staged then visuals will be swapped once ready
+        if(self.GetProperty<bool>(Actor::Property::CONNECTED_TO_SCENE))
+        {
+          // Check if visual is currently in the process of being replaced ( is in removal container )
+          RegisteredVisualContainer::Iterator visualQueuedForRemoval;
+          if(FindVisual(visualName, mRemoveVisuals, visualQueuedForRemoval))
           {
-            std::string fromUrlString;
-            std::string toUrlString;
-            fromUrl->Get(fromUrlString);
-            toUrl->Get(toUrlString);
-
-            if( fromUrlString != toUrlString )
-            {
-              recreate = true;
-            }
+            // Visual with same visual name is already in removal container so current visual pending
+            // Only the the last requested visual will be displayed so remove current visual which is staged but not ready.
+            Toolkit::GetImplementation(visual).SetOffScene(self);
+            (*registeredVisualsiter)->visual.Reset();
+            mVisuals.Erase(registeredVisualsiter);
+          }
+          else
+          {
+            // current visual not already in removal container so add now.
+            DALI_LOG_INFO(gLogFilter, Debug::Verbose, "RegisterVisual Move current registered visual to removal Queue: %s \n", visualName.c_str());
+            MoveVisual(registeredVisualsiter, mVisuals, mRemoveVisuals);
           }
         }
+        else
+        {
+          // Control not staged or visual disabled so can just erase from registered visuals and new visual will be added later.
+          (*registeredVisualsiter)->visual.Reset();
+          mVisuals.Erase(registeredVisualsiter);
+        }
       }
 
-      const Property::Map* instancedMap = instancedProperties.FindConst( visualName );
-      if( recreate || instancedMap )
-      {
-        RemoveVisual( mVisuals, visualName );
-        Style::ApplyVisual( handle, visualName, toMap, instancedMap );
-      }
-      else
-      {
-        // @todo check to see if we can apply toMap without recreating the visual
-        // e.g. by setting only animatable properties
-        // For now, recreate all visuals, but merge in instance data.
-        RemoveVisual( mVisuals, visualName );
-        Style::ApplyVisual( handle, visualName, toMap, instancedMap );
-      }
+      const Property::Map* instancedMap = instancedProperties.FindConst(visualName);
+      Style::ApplyVisual(handle, visualName, toMap, instancedMap);
     }
   }
 }
@@ -2139,14 +2133,13 @@ bool Control::Impl::AccessibleImpl::GrabFocus()
   return Toolkit::KeyboardFocusManager::Get().SetCurrentFocusActor( self );
 }
 
-const char* const FOCUS_BORDER_IMAGE_PATH = DALI_IMAGE_DIR "keyboard_focus.9.png";
-
 static Dali::Actor CreateHighlightIndicatorActor()
 {
+  std::string focusBorderImagePath(AssetManager::GetDaliImagePath());
+  focusBorderImagePath += "/keyboard_focus.9.png";
   // Create the default if it hasn't been set and one that's shared by all the
-  // keyboard focusable actors const char* const FOCUS_BORDER_IMAGE_PATH =
-  // DALI_IMAGE_DIR "keyboard_focus.9.png";
-  auto actor = Toolkit::ImageView::New( FOCUS_BORDER_IMAGE_PATH );
+  // keyboard focusable actors
+  auto actor = Toolkit::ImageView::New( focusBorderImagePath );
   actor.SetResizePolicy( ResizePolicy::FILL_TO_PARENT, Dimension::ALL_DIMENSIONS );
   DevelControl::AppendAccessibilityAttribute( actor, "highlight", "" );
   actor.SetProperty( Toolkit::DevelControl::Property::ACCESSIBILITY_ANIMATED, true);
index 23799c3..7e64b6e 100644 (file)
@@ -1514,6 +1514,9 @@ void TextEditor::OnKeyInputFocusGained()
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextEditor::OnKeyInputFocusGained %p\n", mController.Get() );
   if ( mInputMethodContext  && IsEditable() )
   {
+    // All input panel properties, such as layout, return key type, and input hint, should be set before input panel activates (or shows).
+    mInputMethodContext.NotifyTextInputMultiLine( true );
+
     mInputMethodContext.StatusChangedSignal().Connect( this, &TextEditor::KeyboardStatusChanged );
 
     mInputMethodContext.EventReceivedSignal().Connect( this, &TextEditor::OnInputMethodContextEvent );
index fc09fd8..e87ba2f 100644 (file)
@@ -1519,7 +1519,9 @@ void TextField::OnKeyInputFocusGained()
   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "TextField::OnKeyInputFocusGained %p\n", mController.Get() );
   if ( mInputMethodContext && IsEditable() )
   {
+    // All input panel properties, such as layout, return key type, and input hint, should be set before input panel activates (or shows).
     mInputMethodContext.ApplyOptions( mInputMethodOptions );
+    mInputMethodContext.NotifyTextInputMultiLine( false );
 
     mInputMethodContext.StatusChangedSignal().Connect( this, &TextField::KeyboardStatusChanged );
 
index 29ec224..8cc93bc 100644 (file)
@@ -34,6 +34,7 @@ SET( toolkit_src_files
    ${toolkit_src_dir}/visuals/image-atlas-manager.cpp
    ${toolkit_src_dir}/visuals/image/image-visual.cpp
    ${toolkit_src_dir}/visuals/mesh/mesh-visual.cpp
+   ${toolkit_src_dir}/visuals/npatch-data.cpp
    ${toolkit_src_dir}/visuals/npatch-loader.cpp
    ${toolkit_src_dir}/visuals/npatch/npatch-visual.cpp
    ${toolkit_src_dir}/visuals/primitive/primitive-visual.cpp
index 745c45c..09cad55 100755 (executable)
@@ -249,6 +249,8 @@ void AnimatedImageVisual::DoCreatePropertyMap( Property::Map& map ) const
   map.Insert( Toolkit::ImageVisual::Property::CACHE_SIZE, static_cast<int>(mCacheSize) );
   map.Insert( Toolkit::ImageVisual::Property::FRAME_DELAY, static_cast<int>(mFrameDelay) );
   map.Insert( Toolkit::DevelImageVisual::Property::LOOP_COUNT, static_cast<int>(mLoopCount) );
+  map.Insert( Toolkit::DevelImageVisual::Property::CURRENT_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>(mImageCache->GetCurrentFrameIndex()) : -1 );
+  map.Insert( Toolkit::DevelImageVisual::Property::TOTAL_FRAME_NUMBER, (mImageCache) ? static_cast<int32_t>(mImageCache->GetTotalFrameCount()) : -1 );
 
   map.Insert( Toolkit::DevelImageVisual::Property::STOP_BEHAVIOR, mStopBehavior );
 }
@@ -510,6 +512,7 @@ void AnimatedImageVisual::DoSetOffScene( Actor& actor )
   actor.RemoveRenderer( mImpl->mRenderer );
   mImpl->mRenderer.Reset();
   mPlacementActor.Reset();
+  mStartFirstFrame = false;
 }
 
 void AnimatedImageVisual::OnSetTransform()
@@ -608,12 +611,13 @@ void AnimatedImageVisual::StartFirstFrame( TextureSet& textureSet )
   if(mImpl->mRenderer)
   {
     mImpl->mRenderer.SetTextures( textureSet );
-  }
-  Actor actor = mPlacementActor.GetHandle();
-  if( actor )
-  {
-    actor.AddRenderer( mImpl->mRenderer );
-    mPlacementActor.Reset();
+
+    Actor actor = mPlacementActor.GetHandle();
+    if( actor )
+    {
+      actor.AddRenderer( mImpl->mRenderer );
+      mPlacementActor.Reset();
+    }
   }
 
   if( mFrameCount > 1 )
@@ -687,69 +691,71 @@ void AnimatedImageVisual::FrameReady( TextureSet textureSet )
 
 bool AnimatedImageVisual::DisplayNextFrame()
 {
-  bool nextFrame = false;
-  uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
+  bool continueTimer = false;
 
-  if( mIsJumpTo )
+  if(mImageCache)
   {
-    mIsJumpTo = false;
-    frameIndex = mFrameIndexForJumpTo;
-  }
-  else if( mActionStatus == DevelAnimatedImageVisual::Action::PAUSE )
-  {
-    return false;
-  }
-  else if( mActionStatus == DevelAnimatedImageVisual::Action::STOP )
-  {
-    frameIndex = 0;
-    if( mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME )
-    {
-      frameIndex = 0;
-    }
-    else if( mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME )
+    bool nextFrame = false;
+    uint32_t frameIndex = mImageCache->GetCurrentFrameIndex();
+
+    if( mIsJumpTo )
     {
-      frameIndex = mFrameCount - 1;
+      mIsJumpTo = false;
+      frameIndex = mFrameIndexForJumpTo;
     }
-    else
+    else if( mActionStatus == DevelAnimatedImageVisual::Action::PAUSE )
     {
-      return false; // Do not draw already rendered scene twice.
+      return false;
     }
-  }
-  else
-  {
-    if( mFrameCount > 1 )
+    else if( mActionStatus == DevelAnimatedImageVisual::Action::STOP )
     {
-      nextFrame = true;
-      frameIndex++;
-      if( frameIndex >= mFrameCount )
+      frameIndex = 0;
+      if( mStopBehavior == DevelImageVisual::StopBehavior::FIRST_FRAME )
       {
-        frameIndex %= mFrameCount;
-        ++mCurrentLoopIndex;
+        frameIndex = 0;
       }
-
-      if(mLoopCount >= 0 && mCurrentLoopIndex >= mLoopCount)
+      else if( mStopBehavior == DevelImageVisual::StopBehavior::LAST_FRAME )
       {
-        // This will stop timer
-        mActionStatus = DevelAnimatedImageVisual::Action::STOP;
-        return DisplayNextFrame();
+        frameIndex = mFrameCount - 1;
+      }
+      else
+      {
+        return false; // Do not draw already rendered scene twice.
       }
     }
-
-    unsigned int delay = mImageCache->GetFrameInterval( frameIndex );
-    if( delay > 0u )
+    else
     {
-      if( mFrameDelayTimer.GetInterval() != delay )
+      if( mFrameCount > 1 )
+      {
+        nextFrame = true;
+        frameIndex++;
+        if( frameIndex >= mFrameCount )
+        {
+          frameIndex %= mFrameCount;
+          ++mCurrentLoopIndex;
+        }
+
+        if(mLoopCount >= 0 && mCurrentLoopIndex >= mLoopCount)
+        {
+          // This will stop timer
+          mActionStatus = DevelAnimatedImageVisual::Action::STOP;
+          return DisplayNextFrame();
+        }
+      }
+
+      unsigned int delay = mImageCache->GetFrameInterval( frameIndex );
+      if( delay > 0u )
       {
-        mFrameDelayTimer.SetInterval( delay );
+        if( mFrameDelayTimer.GetInterval() != delay )
+        {
+          mFrameDelayTimer.SetInterval( delay );
+        }
       }
     }
-  }
 
-  DALI_LOG_INFO( gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex);
+    DALI_LOG_INFO( gAnimImgLogFilter,Debug::Concise,"AnimatedImageVisual::DisplayNextFrame(this:%p) CurrentFrameIndex:%d\n", this, frameIndex);
 
-  TextureSet textureSet;
-  if( mImageCache )
-  {
+    TextureSet textureSet;
     if(nextFrame)
     {
       textureSet = mImageCache->NextFrame();
@@ -762,11 +768,16 @@ bool AnimatedImageVisual::DisplayNextFrame()
     if( textureSet )
     {
       SetImageSize( textureSet );
-      mImpl->mRenderer.SetTextures( textureSet );
+      if( mImpl->mRenderer )
+      {
+        mImpl->mRenderer.SetTextures( textureSet );
+      }
     }
+
+    continueTimer = ( mActionStatus == DevelAnimatedImageVisual::Action::PLAY ) ? true : false;
   }
 
-  return ( mActionStatus == DevelAnimatedImageVisual::Action::PLAY ) ? true : false;
+  return continueTimer;
 }
 
 
index a90b31e..1eea6e6 100644 (file)
@@ -110,6 +110,11 @@ int32_t FixedImageCache::GetCurrentFrameIndex() const
   return static_cast<int32_t>(mFront);
 }
 
+int32_t FixedImageCache::GetTotalFrameCount() const
+{
+  return mImageUrls.size();
+}
+
 bool FixedImageCache::IsFrontReady() const
 {
   return ( mReadyFlags.size() > 0 && mReadyFlags[mFront] == true );
index bd232b4..3eaac8a 100644 (file)
@@ -77,6 +77,11 @@ public:
    */
   int32_t GetCurrentFrameIndex() const override;
 
+  /**
+   * Get total frame count of the animated image file.
+   */
+  int32_t GetTotalFrameCount() const override;
+
 private:
   /**
    * @return true if the front frame is ready
index b16b94e..adb6e58 100644 (file)
@@ -101,6 +101,11 @@ public:
    */
   virtual int32_t GetCurrentFrameIndex() const = 0;
 
+  /**
+   * Get total frame count of the animated image file.
+   */
+  virtual int32_t GetTotalFrameCount() const = 0;
+
 private:
 
   /**
index 2733d7a..565252f 100644 (file)
@@ -192,6 +192,11 @@ int32_t RollingAnimatedImageCache::GetCurrentFrameIndex() const
   return mQueue.Front().mFrameNumber;
 }
 
+int32_t RollingAnimatedImageCache::GetTotalFrameCount() const
+{
+  return mFrameCount;
+}
+
 bool RollingAnimatedImageCache::IsFrontReady() const
 {
   return ( !mQueue.IsEmpty() && mQueue.Front().mReady );
index db35741..595adc0 100644 (file)
@@ -95,6 +95,11 @@ public:
    */
   int32_t GetCurrentFrameIndex() const override;
 
+  /**
+   * Get total frame count of the animated image file.
+   */
+  int32_t GetTotalFrameCount() const override;
+
 private:
   /**
    * @return true if the front frame is ready
index e838060..dee6149 100644 (file)
@@ -165,6 +165,11 @@ int32_t RollingImageCache::GetCurrentFrameIndex() const
   return mQueue.Front().mUrlIndex;
 }
 
+int32_t RollingImageCache::GetTotalFrameCount() const
+{
+  return mImageUrls.size();
+}
+
 bool RollingImageCache::IsFrontReady() const
 {
   return ( !mQueue.IsEmpty() && mQueue.Front().mReady );
index c20586f..f33a7b3 100644 (file)
@@ -88,6 +88,11 @@ public:
    */
   int32_t GetCurrentFrameIndex() const override;
 
+  /**
+   * Get total frame count of the animated image file.
+   */
+  int32_t GetTotalFrameCount() const override;
+
 private:
   /**
    * @return true if the front frame is ready
index 71e2383..d694235 100644 (file)
@@ -244,13 +244,46 @@ void ArcVisual::DoSetOnScene( Actor& actor )
   ResourceReady( Toolkit::Visual::ResourceStatus::READY );
 }
 
+void ArcVisual::DoSetOffScene(Actor& actor)
+{
+  if(mImpl->mRenderer)
+  {
+    // Update values from Renderer
+    mThickness  = mImpl->mRenderer.GetProperty<float>(mThicknessIndex);
+    mStartAngle = mImpl->mRenderer.GetProperty<float>(mStartAngleIndex);
+    mSweepAngle = mImpl->mRenderer.GetProperty<float>(mSweepAngleIndex);
+  }
+
+  actor.RemoveRenderer(mImpl->mRenderer);
+  mImpl->mRenderer.Reset();
+
+  mThicknessIndex  = Property::INVALID_INDEX;
+  mStartAngleIndex = Property::INVALID_INDEX;
+  mSweepAngleIndex = Property::INVALID_INDEX;
+}
+
 void ArcVisual::DoCreatePropertyMap( Property::Map& map ) const
 {
+  float thickness, startAngle, sweepAngle;
+  if(mImpl->mRenderer)
+  {
+    // Update values from Renderer
+    thickness  = mImpl->mRenderer.GetProperty<float>(mThicknessIndex);
+    startAngle = mImpl->mRenderer.GetProperty<float>(mStartAngleIndex);
+    sweepAngle = mImpl->mRenderer.GetProperty<float>(mSweepAngleIndex);
+  }
+  else
+  {
+    thickness  = mThickness;
+    startAngle = mStartAngle;
+    sweepAngle = mSweepAngle;
+  }
+
   map.Clear();
   map.Insert( Toolkit::Visual::Property::TYPE, Toolkit::DevelVisual::ARC );
-  map.Insert( Toolkit::DevelArcVisual::Property::THICKNESS, mThickness );
-  map.Insert( Toolkit::DevelArcVisual::Property::START_ANGLE, mStartAngle );
-  map.Insert( Toolkit::DevelArcVisual::Property::SWEEP_ANGLE, mSweepAngle );
+  map.Insert(Toolkit::DevelArcVisual::Property::THICKNESS, thickness);
+  map.Insert(Toolkit::DevelArcVisual::Property::START_ANGLE, startAngle);
+  map.Insert(Toolkit::DevelArcVisual::Property::SWEEP_ANGLE, sweepAngle);
   map.Insert( Toolkit::DevelArcVisual::Property::CAP, mCapType );
 }
 
@@ -313,9 +346,9 @@ void ArcVisual::InitializeRenderer()
 
   mImpl->mRenderer = Renderer::New( geometry, shader );
 
-  mThicknessIndex = mImpl->mRenderer.RegisterProperty( THICKNESS_NAME, mThickness );
-  mStartAngleIndex = mImpl->mRenderer.RegisterProperty( START_ANGLE_NAME, mStartAngle );
-  mSweepAngleIndex = mImpl->mRenderer.RegisterProperty( SWEEP_ANGLE_NAME, mSweepAngle );
+  mThicknessIndex  = mImpl->mRenderer.RegisterProperty(DevelArcVisual::Property::THICKNESS, THICKNESS_NAME, mThickness);
+  mStartAngleIndex = mImpl->mRenderer.RegisterProperty(DevelArcVisual::Property::START_ANGLE, START_ANGLE_NAME, mStartAngle);
+  mSweepAngleIndex = mImpl->mRenderer.RegisterProperty(DevelArcVisual::Property::SWEEP_ANGLE, SWEEP_ANGLE_NAME, mSweepAngle);
 
   mRadiusIndex = mImpl->mRenderer.RegisterProperty( RADIUS_NAME, mRadius );
 
index 90ac861..15f57d3 100644 (file)
@@ -99,6 +99,11 @@ protected:
   void DoSetOnScene( Actor& actor ) override;
 
   /**
+   * @copydoc Visual::Base::DoSetOffScene
+   */
+  void DoSetOffScene(Actor& actor) override;
+
+  /**
    * @copydoc Visual::Base::OnSetTransform
    */
   void OnSetTransform() override;
index 22a54e6..0cd4c1b 100644 (file)
@@ -916,35 +916,38 @@ Shader ImageVisual::GetShader()
 {
   Shader shader;
 
-  std::string vertexShader;
-  bool        usesWholeTexture = true;
+  std::string_view vertexShaderView;
+  bool usesWholeTexture = true;
   if(mImpl->mCustomShader && !mImpl->mCustomShader->mVertexShader.empty())
   {
-    vertexShader     = mImpl->mCustomShader->mVertexShader;
+    vertexShaderView = mImpl->mCustomShader->mVertexShader;
     usesWholeTexture = false; // Impossible to tell.
   }
   else
   {
-    vertexShader = mImageVisualShaderFactory.GetVertexShaderSource().data();
+    vertexShaderView = mImageVisualShaderFactory.GetVertexShaderSource();
   }
 
-  std::string fragmentShader;
+  std::string_view fragmentShaderView;
   if(mImpl->mCustomShader && !mImpl->mCustomShader->mFragmentShader.empty())
   {
-    fragmentShader = mImpl->mCustomShader->mFragmentShader;
+    fragmentShaderView = mImpl->mCustomShader->mFragmentShader;
   }
   else
   {
-    fragmentShader = mImageVisualShaderFactory.GetFragmentShaderSource().data();
+    fragmentShaderView = mImageVisualShaderFactory.GetFragmentShaderSource();
   }
 
   // If the texture is native, we may need to change prefix and sampler in
   // the fragment shader
   bool modifiedFragmentShader = false;
+  std::string fragmentShaderString;
   if(mTextures && DevelTexture::IsNative(mTextures.GetTexture(0)))
   {
-    Texture nativeTexture  = mTextures.GetTexture(0);
-    modifiedFragmentShader = DevelTexture::ApplyNativeFragmentShader(nativeTexture, fragmentShader);
+    Texture nativeTexture = mTextures.GetTexture(0);
+    fragmentShaderString   = std::string(fragmentShaderView);
+    modifiedFragmentShader = DevelTexture::ApplyNativeFragmentShader(nativeTexture, fragmentShaderString);
+    fragmentShaderView     = fragmentShaderString;
   }
 
   const bool useStandardShader = !mImpl->mCustomShader && !modifiedFragmentShader;
@@ -955,26 +958,26 @@ Shader ImageVisual::GetShader()
       mFactoryCache,
       mImpl->mFlags & Impl::IS_ATLASING_APPLIED,
       mWrapModeU <= WrapMode::CLAMP_TO_EDGE && mWrapModeV <= WrapMode::CLAMP_TO_EDGE,
-      IsRoundedCornerRequired());
+      IsRoundedCornerRequired() );
   }
   else if(mImpl->mCustomShader)
   {
-    shader = Shader::New(vertexShader, fragmentShader, mImpl->mCustomShader->mHints);
+    shader = Shader::New(vertexShaderView, fragmentShaderView, mImpl->mCustomShader->mHints);
   }
   else
   {
-    shader = Shader::New(vertexShader, fragmentShader);
+    shader = Shader::New(vertexShaderView, fragmentShaderView);
   }
 
   if(usesWholeTexture)
   {
-    shader.RegisterProperty(PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT);
+    shader.RegisterProperty( PIXEL_AREA_UNIFORM_NAME, FULL_TEXTURE_RECT );
   }
 
   // Set pixel align off as default.
   // ToDo: Pixel align causes issues such as rattling image animation.
   // We should trun it off until issues are resolved
-  shader.RegisterProperty(PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_OFF);
+  shader.RegisterProperty( PIXEL_ALIGNED_UNIFORM_NAME, PIXEL_ALIGN_OFF );
 
   return shader;
 }
diff --git a/dali-toolkit/internal/visuals/npatch-data.cpp b/dali-toolkit/internal/visuals/npatch-data.cpp
new file mode 100644 (file)
index 0000000..e959003
--- /dev/null
@@ -0,0 +1,252 @@
+ /*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali-toolkit/internal/visuals/npatch-data.h>
+
+// INTERNAL HEADERS
+#include <dali-toolkit/internal/visuals/rendering-addon.h>
+
+// EXTERNAL HEADERS
+#include <dali/integration-api/debug.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+NPatchData::NPatchData()
+: mId(INVALID_NPATCH_DATA_ID),
+  mUrl(),
+  mTextureSet(),
+  mHash(0),
+  mCroppedWidth(0),
+  mCroppedHeight(0),
+  mBorder(0, 0, 0, 0),
+  mLoadingState(LoadingState::LOADING),
+  mPreMultiplyOnLoad(false),
+  mRenderingMap{nullptr}
+{
+}
+
+NPatchData::~NPatchData()
+{
+  // If there is an opacity map, it has to be destroyed using addon call
+  if( mRenderingMap )
+  {
+    RenderingAddOn::Get().DestroyNPatch( mRenderingMap );
+  }
+}
+
+void NPatchData::SetId(const NPatchDataId id)
+{
+  mId = id;
+}
+
+NPatchData::NPatchDataId NPatchData::GetId() const
+{
+  return mId;
+}
+
+void NPatchData::AddObserver(TextureUploadObserver* textureObserver)
+{
+  mObserverList.PushBack( textureObserver );
+}
+
+void NPatchData::RemoveObserver(TextureUploadObserver* textureObserver)
+{
+  for(uint32_t index = 0; index < mObserverList.Count(); ++index )
+  {
+    if(textureObserver == mObserverList[index])
+    {
+      mObserverList.Erase( mObserverList.begin() + index );
+      break;
+    }
+  }
+}
+
+uint32_t NPatchData::GetObserverCount() const
+{
+  return mObserverList.Count();
+}
+
+void NPatchData::SetUrl(const std::string url)
+{
+  mUrl = url;
+}
+
+std::string NPatchData::GetUrl() const
+{
+  return mUrl;
+}
+
+void NPatchData::SetTextures(const TextureSet textureSet)
+{
+  mTextureSet = textureSet;
+}
+
+TextureSet NPatchData::GetTextures() const
+{
+  return mTextureSet;
+}
+
+void NPatchData::SetStretchPixelsX(const NPatchUtility::StretchRanges stretchPixelsX)
+{
+  mStretchPixelsX = stretchPixelsX;
+}
+
+void NPatchData::SetStretchPixelsY(const NPatchUtility::StretchRanges stretchPixelsY)
+{
+  mStretchPixelsY = stretchPixelsY;
+}
+
+NPatchUtility::StretchRanges NPatchData::GetStretchPixelsX() const
+{
+  return mStretchPixelsX;
+}
+
+NPatchUtility::StretchRanges NPatchData::GetStretchPixelsY() const
+{
+  return mStretchPixelsY;
+}
+
+void NPatchData::SetHash(std::size_t hash)
+{
+  mHash = hash;
+}
+
+std::size_t NPatchData::GetHash() const
+{
+  return mHash;
+}
+
+void NPatchData::SetCroppedWidth(uint32_t croppedWidth)
+{
+  mCroppedWidth = croppedWidth;
+}
+
+void NPatchData::SetCroppedHeight(uint32_t croppedHeight)
+{
+  mCroppedHeight = croppedHeight;
+}
+
+uint32_t NPatchData::GetCroppedWidth() const
+{
+  return mCroppedWidth;
+}
+
+uint32_t NPatchData::GetCroppedHeight() const
+{
+  return mCroppedHeight;
+}
+
+void NPatchData::SetBorder(const Rect<int> border)
+{
+  mBorder = border;
+}
+
+Rect<int> NPatchData::GetBorder() const
+{
+  return mBorder;
+}
+
+void NPatchData::SetPreMultiplyOnLoad(bool preMultiplyOnLoad)
+{
+  mPreMultiplyOnLoad = preMultiplyOnLoad;
+}
+
+bool NPatchData::IsPreMultiplied() const
+{
+  return mPreMultiplyOnLoad;
+}
+
+void NPatchData::SetLoadingState(const LoadingState loadingState)
+{
+  mLoadingState = loadingState;
+}
+
+NPatchData::LoadingState NPatchData::GetLoadingState() const
+{
+  return mLoadingState;
+}
+
+void* NPatchData::GetRenderingMap() const
+{
+  return mRenderingMap;
+}
+
+void NPatchData::SetLoadedNPatchData( Devel::PixelBuffer& pixelBuffer, bool preMultiplied )
+{
+  if( mBorder == Rect< int >( 0, 0, 0, 0 ) )
+  {
+    NPatchUtility::ParseBorders( pixelBuffer, mStretchPixelsX, mStretchPixelsY );
+
+    // Crop the image
+    pixelBuffer.Crop( 1, 1, pixelBuffer.GetWidth() - 2, pixelBuffer.GetHeight() - 2 );
+  }
+  else
+  {
+    mStretchPixelsX.PushBack( Uint16Pair( mBorder.left, ( (pixelBuffer.GetWidth() >= static_cast< unsigned int >( mBorder.right )) ? pixelBuffer.GetWidth() - mBorder.right : 0 ) ) );
+    mStretchPixelsY.PushBack( Uint16Pair( mBorder.top, ( (pixelBuffer.GetHeight() >= static_cast< unsigned int >( mBorder.bottom )) ? pixelBuffer.GetHeight() - mBorder.bottom : 0 ) ) );
+  }
+
+  mCroppedWidth = pixelBuffer.GetWidth();
+  mCroppedHeight = pixelBuffer.GetHeight();
+
+  // Create opacity map
+  mRenderingMap = RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().BuildNPatch(pixelBuffer, this) : nullptr;
+
+  PixelData pixels = Devel::PixelBuffer::Convert( pixelBuffer ); // takes ownership of buffer
+
+  Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() );
+  texture.Upload( pixels );
+
+  mTextureSet = TextureSet::New();
+  mTextureSet.SetTexture( 0u, texture );
+
+  mPreMultiplyOnLoad = preMultiplied;
+
+  mLoadingState = LoadingState::LOAD_COMPLETE;
+}
+
+void NPatchData::LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied )
+{
+  if(loadSuccess)
+  {
+    SetLoadedNPatchData( pixelBuffer, preMultiplied );
+  }
+  else
+  {
+    mLoadingState = LoadingState::LOAD_FAILED;
+  }
+
+  for(uint32_t index = 0; index < mObserverList.Count(); ++index)
+  {
+    TextureUploadObserver* observer = mObserverList[index];
+    observer->UploadComplete(loadSuccess, TextureManager::INVALID_TEXTURE_ID, mTextureSet, false, Vector4(), preMultiplied);
+  }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
diff --git a/dali-toolkit/internal/visuals/npatch-data.h b/dali-toolkit/internal/visuals/npatch-data.h
new file mode 100644 (file)
index 0000000..60e6e6b
--- /dev/null
@@ -0,0 +1,303 @@
+#ifndef DALI_TOOLKIT_NPATCH_DATA_H
+#define DALI_TOOLKIT_NPATCH_DATA_H
+
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <string>
+#include <dali/public-api/rendering/texture-set.h>
+#include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/texture-manager-impl.h>
+#include <dali-toolkit/devel-api/utility/npatch-utilities.h>
+
+namespace Dali
+{
+
+namespace Toolkit
+{
+
+namespace Internal
+{
+
+class NPatchData : public Dali::Toolkit::TextureUploadObserver
+{
+public:
+  typedef int32_t NPatchDataId;                 ///< The NPatchDataId type. This is used as a handle to refer to a particular Npatch Data.
+  static const int INVALID_NPATCH_DATA_ID = -1; ///< Used to represent a null TextureId or error
+
+  /**
+   * @brief Loading State of the NPatch image.
+   */
+  enum class LoadingState
+  {
+    LOADING = 0,   ///< NPatch is on loading.
+    LOAD_COMPLETE, ///< NPatch loading is completed successfully.
+    LOAD_FAILED    ///< NPatch loading is failed.
+  };
+
+public:
+
+  /**
+   * Constructor
+   */
+  NPatchData();
+
+  /**
+   * Destructor, non-virtual as not a base class
+   */
+  ~NPatchData();
+
+public:
+
+  /**
+   * @brief Set cache data id.
+   *
+   * @param [in] id cache data id
+   */
+  void SetId(const NPatchDataId id);
+
+  /**
+   * @brief Retrieve cache data id
+   *
+   * @return cache data id.
+   */
+  NPatchDataId GetId() const;
+
+  /**
+   * @brief Add TextureUploadObserver that uses the image of this cache data.
+   *
+   * @param [in] textureObserver the TextureUploadObserver that uses the image of this cache data.
+   */
+  void AddObserver(TextureUploadObserver* textureObserver);
+
+  /**
+   * @brief Remove TextureUploadObserver.
+   *
+   * @param [in] textureObserver the TextureUploadObserver that will be removed in this cache data.
+   */
+  void RemoveObserver(TextureUploadObserver* textureObserver);
+
+  /**
+   * @brief Retrieve the number of observer.
+   *
+   * @return Return the number of observer.
+   */
+  uint32_t GetObserverCount() const;
+
+  /**
+   * @brief Set NPatch image url.
+   *
+   * @param [in] url NPatch image url
+   */
+  void SetUrl(const std::string url);
+
+  /**
+   * @brief Retrieve the image url.
+   *
+   * @return Return the image url.
+   */
+  std::string GetUrl() const;
+
+  /**
+   * @brief Set texture set on the cache data
+   *
+   * @param [in] textureSet loaded texture set
+   */
+  void SetTextures(const TextureSet textureSet);
+
+  /**
+   * @brief Retrieve loaded texture set.
+   *
+   * @return Return loaded texture set.
+   */
+  TextureSet GetTextures() const;
+
+  /**
+   * @brief Set X directional stretchPixels
+   *
+   * @param [in] stretchPixelsX stretchPixels for X direction
+   */
+  void SetStretchPixelsX(const NPatchUtility::StretchRanges stretchPixelsX);
+
+  /**
+   * @brief Set Y directional stretchPixels
+   *
+   * @param [in] stretchPixelsY stretchPixels for Y direction
+   */
+  void SetStretchPixelsY(const NPatchUtility::StretchRanges stretchPixelsY);
+
+  /**
+   * @brief Retrieve stretchPixels for X direction.
+   *
+   * @return Return stretchPixels for X direction.
+   */
+  NPatchUtility::StretchRanges GetStretchPixelsX() const;
+
+  /**
+   * @brief Retrieve stretchPixels for Y direction.
+   *
+   * @return Return stretchPixels for Y direction.
+   */
+  NPatchUtility::StretchRanges GetStretchPixelsY() const;
+
+  /**
+   * @brief Set cache data hash.
+   *
+   * @param [in] hash cache hash
+   */
+  void SetHash(std::size_t hash);
+
+  /**
+   * @brief Retrieve hash value of the cache.
+   *
+   * @return Return hash value of the cache.
+   */
+  std::size_t GetHash() const;
+
+  /**
+   * @brief Set croppedWidth of NPatch
+   *
+   * @param [in] croppedWidth croppedWidth of NPatch
+   */
+  void SetCroppedWidth(uint32_t croppedWidth);
+
+  /**
+   * @brief Set croppedHeight of NPatch
+   *
+   * @param [in] croppedHeight croppedHeight of NPatch
+   */
+  void SetCroppedHeight(uint32_t croppedHeight);
+
+  /**
+   * @brief Retrieve croppedWidth of NPatch.
+   *
+   * @return Return croppedWidth of NPatch.
+   */
+  uint32_t GetCroppedWidth() const;
+
+  /**
+   * @brief Retrieve croppedHeight of NPatch.
+   *
+   * @return Return croppedHeight of NPatch.
+   */
+  uint32_t GetCroppedHeight() const;
+
+  /**
+   * @brief Set border of NPatch.
+   *
+   * @param [in] border border of NPatch
+   */
+  void SetBorder(const Rect<int> border);
+
+  /**
+   * @brief Retrieve border of NPatch.
+   *
+   * @return Return border of NPatch.
+   */
+  Rect<int> GetBorder() const;
+
+  /**
+   * @brief Set whether the loaded image is premultiplied or not
+   *
+   * @param [in] preMultiplyOnLoad whether the loaded image is premultiplied or not
+   */
+  void SetPreMultiplyOnLoad(bool preMultiplyOnLoad);
+
+  /**
+   * @brief Retrieve whether the loaded image is premultiplied or not.
+   *
+   * @return Return true if the image is premultiplied alpha.
+   */
+  bool IsPreMultiplied() const;
+
+  /**
+   * @brief Set current loading state.
+   *
+   * @param [in] loadingState current loading state
+   */
+  void SetLoadingState(const LoadingState loadingState);
+
+  /**
+   * @brief Retrieve current loading state.
+   *
+   * @return Return current loading state.
+   */
+  LoadingState GetLoadingState() const;
+
+
+  /**
+   * @brief Retrieve NPatch rendering data.
+   *
+   * @return Return NPatch rendering data.
+   */
+  void* GetRenderingMap() const;
+
+  /**
+   * @brief Set loaded pixel buffer for the cache data.
+   *
+   * @param [in] pixelBuffer loaded pixel buffer.
+   * @param [in] preMultiplied whether the loaded image is premultiplied or not
+   */
+  void SetLoadedNPatchData( Devel::PixelBuffer& pixelBuffer, bool preMultiplied );
+
+private:
+
+  /**
+   * @copydoc TextureUploadObserver::UploadCompleted
+   *
+   * To avoid rendering garbage pixels, renderer should be added to actor after the resources are ready.
+   * This callback is the place to add the renderer as it would be called once the loading is finished.
+   */
+  void UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied ) override {}
+
+  /**
+   * @copydoc TextureUploadObserver::LoadComplete
+   *
+   * To avoid rendering garbage pixels, renderer should be added to actor after the resources are ready.
+   * This callback is the place to add the renderer as it would be called once the loading is finished.
+   */
+  void LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied ) override;
+
+
+private:
+
+  using ObserverListType = Dali::Vector<TextureUploadObserver*>;
+
+  NPatchDataId                 mId;
+  ObserverListType             mObserverList;      ///< Container used to store all observer clients of this Texture
+  std::string                  mUrl;               ///< Url of the N-Patch
+  TextureSet                   mTextureSet;        ///< Texture containing the cropped image
+  NPatchUtility::StretchRanges mStretchPixelsX;    ///< X stretch pixels
+  NPatchUtility::StretchRanges mStretchPixelsY;    ///< Y stretch pixels
+  std::size_t                  mHash;              ///< Hash code for the Url
+  uint32_t                     mCroppedWidth;      ///< Width of the cropped middle part of N-patch
+  uint32_t                     mCroppedHeight;     ///< Height of the cropped middle part of N-patch
+  Rect<int>                    mBorder;            ///< The size of the border
+  LoadingState                 mLoadingState;      ///< True if the data loading is completed
+  bool                         mPreMultiplyOnLoad; ///< Whether to multiply alpha into color channels on load
+  void*                        mRenderingMap;      ///< NPatch rendering data
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_NPATCH_DATA_H
index 2550001..f0b154a 100644 (file)
@@ -34,179 +34,159 @@ namespace Toolkit
 namespace Internal
 {
 
-namespace NPatchBuffer
+namespace
 {
 
-void SetLoadedNPatchData( NPatchLoader::Data* data, Devel::PixelBuffer& pixelBuffer )
-{
-  if( data->border == Rect< int >( 0, 0, 0, 0 ) )
-  {
-    NPatchUtility::ParseBorders( pixelBuffer, data->stretchPixelsX, data->stretchPixelsY );
-
-    // Crop the image
-    pixelBuffer.Crop( 1, 1, pixelBuffer.GetWidth() - 2, pixelBuffer.GetHeight() - 2 );
-  }
-  else
-  {
-    data->stretchPixelsX.PushBack( Uint16Pair( data->border.left, ( (pixelBuffer.GetWidth() >= static_cast< unsigned int >( data->border.right )) ? pixelBuffer.GetWidth() - data->border.right : 0 ) ) );
-    data->stretchPixelsY.PushBack( Uint16Pair( data->border.top, ( (pixelBuffer.GetHeight() >= static_cast< unsigned int >( data->border.bottom )) ? pixelBuffer.GetHeight() - data->border.bottom : 0 ) ) );
-  }
-
-  data->croppedWidth = pixelBuffer.GetWidth();
-  data->croppedHeight = pixelBuffer.GetHeight();
-
-  // Create opacity map
-  data->renderingMap = RenderingAddOn::Get().IsValid() ? RenderingAddOn::Get().BuildNPatch(pixelBuffer, data ) : nullptr;
-
-  PixelData pixels = Devel::PixelBuffer::Convert( pixelBuffer ); // takes ownership of buffer
-
-  Texture texture = Texture::New( TextureType::TEXTURE_2D, pixels.GetPixelFormat(), pixels.GetWidth(), pixels.GetHeight() );
-  texture.Upload( pixels );
+constexpr auto INVALID_CACHE_INDEX = int32_t{-1}; ///< Invalid Cache index
+constexpr auto UNINITIALIZED_ID = int32_t{0}; ///< uninitialised id, use to initialize ids
 
-  data->textureSet = TextureSet::New();
-  data->textureSet.SetTexture( 0u, texture );
+} // Anonymous namespace
 
-  data->loadCompleted = true;
-}
-
-} // namespace NPatchBuffer
-
-NPatchLoader::Data::~Data()
+NPatchLoader::NPatchLoader()
+: mCurrentNPatchDataId(0)
 {
-  // If there is an opacity map, it has to be destroyed using addon call
-  if( renderingMap )
-  {
-    RenderingAddOn::Get().DestroyNPatch( renderingMap );
-  }
 }
 
-NPatchLoader::NPatchLoader()
+NPatchLoader::~NPatchLoader()
 {
 }
 
-NPatchLoader::~NPatchLoader()
+NPatchData::NPatchDataId NPatchLoader::GenerateUniqueNPatchDataId()
 {
+  return mCurrentNPatchDataId++;
 }
 
 std::size_t NPatchLoader::Load( TextureManager& textureManager, TextureUploadObserver* textureObserver, const std::string& url, const Rect< int >& border, bool& preMultiplyOnLoad, bool synchronousLoading )
 {
   std::size_t hash = CalculateHash( url );
-  OwnerContainer< Data* >::SizeType index = UNINITIALIZED_ID;
-  const OwnerContainer< Data* >::SizeType count = mCache.Count();
-  int cachedIndex = -1;
-  Data* data;
+  OwnerContainer< NPatchData* >::SizeType index = UNINITIALIZED_ID;
+  const OwnerContainer< NPatchData* >::SizeType count = mCache.Count();
 
   for( ; index < count; ++index )
   {
-    if( mCache[ index ]->hash == hash )
+    if( mCache[ index ]->GetHash() == hash )
     {
       // hash match, check url as well in case of hash collision
-      if( mCache[ index ]->url == url )
+      if(mCache[ index ]->GetUrl() == url)
       {
         // Use cached data
-        if( mCache[ index ]->border == border )
+        if( mCache[ index ]->GetBorder() == border )
         {
-          if( mCache[ index ]->loadCompleted )
+          if( mCache[ index ]->GetLoadingState() == NPatchData::LoadingState::LOADING )
           {
-            return index + 1u; // valid indices are from 1 onwards
+            mCache[ index ]->AddObserver( textureObserver );
           }
-          mCache[ index ]->observerList.PushBack( textureObserver );
-          data = mCache[ index ];
-          return index + 1u; // valid indices are from 1 onwards
+          return mCache[ index ]->GetId(); // valid indices are from 1 onwards
         }
         else
         {
-          if( mCache[ index ]->loadCompleted )
+          if( mCache[ index ]->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
           {
             // Same url but border is different - use the existing texture
-            Data* data = new Data();
-            data->hash = hash;
-            data->url = url;
-            data->croppedWidth = mCache[ index ]->croppedWidth;
-            data->croppedHeight = mCache[ index ]->croppedHeight;
+            NPatchData* newData = new NPatchData();
+            newData->SetId(GenerateUniqueNPatchDataId());
+            newData->SetHash(hash);
+            newData->SetUrl(url);
+            newData->SetCroppedWidth(mCache[ index ]->GetCroppedWidth());
+            newData->SetCroppedHeight(mCache[ index ]->GetCroppedHeight());
 
-            data->textureSet = mCache[ index ]->textureSet;
+            newData->SetTextures(mCache[ index ]->GetTextures());
 
             NPatchUtility::StretchRanges stretchRangesX;
-            stretchRangesX.PushBack( Uint16Pair( border.left, ( (data->croppedWidth >= static_cast< unsigned int >( border.right )) ? data->croppedWidth - border.right : 0 ) ) );
+            stretchRangesX.PushBack( Uint16Pair( border.left, ( (newData->GetCroppedWidth() >= static_cast< unsigned int >( border.right )) ? newData->GetCroppedHeight() - border.right : 0 ) ) );
 
             NPatchUtility::StretchRanges stretchRangesY;
-            stretchRangesY.PushBack( Uint16Pair( border.top, ( (data->croppedHeight >= static_cast< unsigned int >( border.bottom )) ? data->croppedHeight - border.bottom : 0 ) ) );
+            stretchRangesY.PushBack( Uint16Pair( border.top, ( (newData->GetCroppedWidth() >= static_cast< unsigned int >( border.bottom )) ? newData->GetCroppedHeight() - border.bottom : 0 ) ) );
+
+            newData->SetStretchPixelsX(stretchRangesX);
+            newData->SetStretchPixelsY(stretchRangesY);
+            newData->SetBorder(border);
 
-            data->stretchPixelsX = stretchRangesX;
-            data->stretchPixelsY = stretchRangesY;
-            data->border = border;
+            newData->SetPreMultiplyOnLoad(mCache[ index ]->IsPreMultiplied());
 
-            data->loadCompleted = mCache[ index ]->loadCompleted;
+            newData->SetLoadingState(NPatchData::LoadingState::LOAD_COMPLETE);
+            newData->AddObserver( textureObserver );
 
-            mCache.PushBack( data );
+            mCache.PushBack( newData );
 
-            return mCache.Count(); // valid ids start from 1u
+            return newData->GetId(); // valid ids start from 1u
           }
         }
       }
     }
   }
 
-  if( cachedIndex == -1 )
-  {
-    data = new Data();
-    data->loadCompleted = false;
-    data->hash = hash;
-    data->url = url;
-    data->border = border;
-
-    mCache.PushBack( data );
-
-    cachedIndex = mCache.Count();
-  }
+  // If this is new image loading, make new cache data
+  NPatchData* data;
+  data = new NPatchData();
+  data->SetId(GenerateUniqueNPatchDataId());
+  data->SetHash(hash);
+  data->SetUrl(url);
+  data->SetBorder(border);
+  data->SetPreMultiplyOnLoad(preMultiplyOnLoad);
+  data->AddObserver(textureObserver);
+  mCache.PushBack(data);
 
   auto preMultiplyOnLoading = preMultiplyOnLoad ? TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD
                                                 : TextureManager::MultiplyOnLoad::LOAD_WITHOUT_MULTIPLY;
+
   Devel::PixelBuffer pixelBuffer = textureManager.LoadPixelBuffer( url, Dali::ImageDimensions(), FittingMode::DEFAULT,
                                                                    SamplingMode::BOX_THEN_LINEAR, synchronousLoading,
-                                                                   textureObserver, true, preMultiplyOnLoading );
+                                                                   data, true, preMultiplyOnLoading );
 
   if( pixelBuffer )
   {
-    NPatchBuffer::SetLoadedNPatchData( data, pixelBuffer );
     preMultiplyOnLoad = ( preMultiplyOnLoading == TextureManager::MultiplyOnLoad::MULTIPLY_ON_LOAD ) ? true : false;
+    data->SetLoadedNPatchData( pixelBuffer, preMultiplyOnLoad );
   }
 
-  return cachedIndex;
+  return data->GetId();
 }
 
-void NPatchLoader::SetNPatchData( bool loadSuccess, std::size_t id, Devel::PixelBuffer& pixelBuffer, const Internal::VisualUrl& url, bool preMultiplied )
+int32_t NPatchLoader::GetCacheIndexFromId( const NPatchData::NPatchDataId id )
 {
-  Data* data;
-  data = mCache[ id - 1u ];
+  const unsigned int size = mCache.Count();
 
-  // To prevent recursion.
-  // data->loadCompleted will be set true in the NPatchBuffer::SetLoadedNPatchData when the first observer called this method.
-  if( data->loadCompleted )
+  for( unsigned int i = 0; i < size; ++i )
   {
-    return;
+    if( mCache[i]->GetId() == id )
+    {
+      return i;
+    }
   }
 
-  NPatchBuffer::SetLoadedNPatchData( data, pixelBuffer );
+  return INVALID_CACHE_INDEX;
+}
 
-  while( data->observerList.Count() )
+bool NPatchLoader::GetNPatchData( const NPatchData::NPatchDataId id, const NPatchData*& data )
+{
+  int32_t cacheIndex = GetCacheIndexFromId(id);
+  if( cacheIndex != INVALID_CACHE_INDEX )
   {
-    TextureUploadObserver* observer = data->observerList[0];
-    observer->LoadComplete( loadSuccess, Devel::PixelBuffer(), url, preMultiplied );
-    data->observerList.Erase( data->observerList.begin() );
+    data = mCache[cacheIndex];
+    return true;
   }
+  data = nullptr;
+  return false;
 }
 
-bool NPatchLoader::GetNPatchData( std::size_t id, const Data*& data )
+void NPatchLoader::Remove( std::size_t id, TextureUploadObserver* textureObserver )
 {
-  if( ( id > UNINITIALIZED_ID )&&( id <= mCache.Count() ) )
+  int32_t cacheIndex = GetCacheIndexFromId(id);
+  if( cacheIndex == INVALID_CACHE_INDEX )
   {
-    data = mCache[ id - 1u ]; // id's start from 1u
-    return true;
+    return;
+  }
+
+  NPatchData* data;
+  data = mCache[cacheIndex];
+
+  data->RemoveObserver(textureObserver);
+
+  if(data->GetObserverCount() == 0)
+  {
+      mCache.Erase( mCache.Begin() + cacheIndex );
   }
-  data = NULL;
-  return false;
 }
 
 } // namespace Internal
index a3aa464..b2b4a7c 100644 (file)
@@ -24,6 +24,7 @@
 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/internal/visuals/npatch-data.h>
 #include <dali-toolkit/internal/visuals/texture-manager-impl.h>
 #include <dali-toolkit/devel-api/utility/npatch-utilities.h>
 
@@ -49,43 +50,6 @@ class NPatchLoader
 {
 public:
 
-  enum
-  {
-    UNINITIALIZED_ID = 0 ///< uninitialised id, use to initialize ids
-  };
-
-  struct Data
-  {
-    Data()
-    : url(),
-      textureSet(),
-      hash( 0 ),
-      croppedWidth( 0 ),
-      croppedHeight( 0 ),
-      border( 0, 0, 0, 0 ),
-      loadCompleted( false ),
-      renderingMap{ nullptr }
-    {}
-
-    ~Data();
-
-    using ObserverListType = Dali::Vector< TextureUploadObserver* >;
-
-    ObserverListType observerList;                 ///< Container used to store all observer clients of this Texture
-    std::string url;                               ///< Url of the N-Patch
-    TextureSet textureSet;                         ///< Texture containing the cropped image
-    NPatchUtility::StretchRanges stretchPixelsX;   ///< X stretch pixels
-    NPatchUtility::StretchRanges stretchPixelsY;   ///< Y stretch pixels
-    std::size_t hash;                              ///< Hash code for the Url
-    uint32_t croppedWidth;                         ///< Width of the cropped middle part of N-patch
-    uint32_t croppedHeight;                        ///< Height of the cropped middle part of N-patch
-    Rect< int > border;                            ///< The size of the border
-    bool loadCompleted;                            ///< True if the data loading is completed
-    void* renderingMap;                            ///< NPatch rendering data
-  };
-
-public:
-
   /**
    * Constructor
    */
@@ -113,21 +77,35 @@ public:
   /**
    * @brief Set loaded PixelBuffer and its information
    *
-   * @param [in] loadSuccess True if the texture load was successful (i.e. the resource is available). If false, then the resource failed to load.
    * @param [in] id cache data id
    * @param [in] pixelBuffer of loaded image
-   * @param [in] url           The url address of the loaded image.
    * @param [in] preMultiplied True if the image had pre-multiplied alpha applied
    */
-  void SetNPatchData( bool loadSuccess, std::size_t id, Devel::PixelBuffer& pixelBuffer, const Internal::VisualUrl& url, bool preMultiplied );
+  void SetNPatchData( std::size_t id, Devel::PixelBuffer& pixelBuffer, bool preMultiplied );
 
   /**
    * @brief Retrieve N patch data matching to an id
    * @param [in] id of data
-   * @param [out] data const pointer to the data
+   * @param [out] data const pointer to the NPatchData
    * @return true if data matching to id was really found
    */
-  bool GetNPatchData( std::size_t id, const Data*& data );
+  bool GetNPatchData( const NPatchData::NPatchDataId id, const NPatchData*& data );
+
+  /**
+   * @brief Remove a texture matching id.
+   * Erase the observer from the observer list of cache.
+   * If the observer list is empty, the textureSet will be reset.
+   *
+   * @param [in] id cache data id
+   * @param [in] textureObserver The NPatchVisual that requested loading.
+   */
+  void Remove( std::size_t id, TextureUploadObserver* textureObserver );
+
+private:
+
+  NPatchData::NPatchDataId GenerateUniqueNPatchDataId();
+
+  int32_t GetCacheIndexFromId( const NPatchData::NPatchDataId id );
 
 protected:
 
@@ -143,8 +121,8 @@ protected:
 
 private:
 
-  OwnerContainer< Data* > mCache;
-
+  NPatchData::NPatchDataId mCurrentNPatchDataId;
+  OwnerContainer< NPatchData* > mCache;
 };
 
 } // name Internal
index 5c03ac2..6dcf020 100644 (file)
@@ -24,7 +24,6 @@
 #include <dali/integration-api/debug.h>
 
 // INTERNAL INCLUDES
-#include <dali-toolkit/public-api/visuals/image-visual-properties.h>
 #include <dali-toolkit/devel-api/visuals/image-visual-properties-devel.h>
 #include <dali-toolkit/public-api/visuals/visual-properties.h>
 #include <dali-toolkit/internal/visuals/npatch-loader.h>
@@ -278,15 +277,15 @@ void NPatchVisual::LoadImages()
   TextureManager& textureManager = mFactoryCache.GetTextureManager();
   bool synchronousLoading = mImpl->mFlags & Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
 
-  if( NPatchLoader::UNINITIALIZED_ID == mId && mImageUrl.IsLocalResource() )
+  if( mId == NPatchData::INVALID_NPATCH_DATA_ID && mImageUrl.IsLocalResource() )
   {
     bool preMultiplyOnLoad = IsPreMultipliedAlphaEnabled() && !mImpl->mCustomShader ? true : false;
     mId = mLoader.Load( textureManager, this, mImageUrl.GetUrl(), mBorder, preMultiplyOnLoad, synchronousLoading );
 
-    const NPatchLoader::Data* data;
-    if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
+    const NPatchData* data;
+    if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
     {
-      EnablePreMultipliedAlpha( preMultiplyOnLoad );
+      EnablePreMultipliedAlpha( data->IsPreMultiplied() );
     }
   }
 
@@ -306,11 +305,11 @@ void NPatchVisual::GetNaturalSize( Vector2& naturalSize )
   naturalSize.y = 0u;
 
   // load now if not already loaded
-  const NPatchLoader::Data* data;
-  if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
+  const NPatchData* data;
+  if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() != NPatchData::LoadingState::LOADING )
   {
-    naturalSize.x = data->croppedWidth;
-    naturalSize.y = data->croppedHeight;
+    naturalSize.x = data->GetCroppedWidth();
+    naturalSize.y = data->GetCroppedHeight();
   }
   else
   {
@@ -386,6 +385,12 @@ void NPatchVisual::DoSetProperties( const Property::Map& propertyMap )
       mImpl->mFlags &= ~Impl::IS_SYNCHRONOUS_RESOURCE_LOADING;
     }
   }
+
+  Property::Value* releasePolicy = propertyMap.Find( Toolkit::ImageVisual::Property::RELEASE_POLICY, RELEASE_POLICY_NAME );
+  if( releasePolicy )
+  {
+    releasePolicy->Get( mReleasePolicy );
+  }
 }
 
 void NPatchVisual::DoSetOnScene( Actor& actor )
@@ -393,7 +398,7 @@ void NPatchVisual::DoSetOnScene( Actor& actor )
   // load when first go on stage
   LoadImages();
 
-  const NPatchLoader::Data* data;
+  const NPatchData* data;
   if( mLoader.GetNPatchData( mId, data ) )
   {
     Geometry geometry = CreateGeometry();
@@ -402,11 +407,11 @@ void NPatchVisual::DoSetOnScene( Actor& actor )
     mImpl->mRenderer = Renderer::New( geometry, shader );
 
     mPlacementActor = actor;
-    if( data->loadCompleted )
+    if( data->GetLoadingState() != NPatchData::LoadingState::LOADING )
     {
       if( RenderingAddOn::Get().IsValid() )
       {
-        RenderingAddOn::Get().SubmitRenderTask( mImpl->mRenderer, data->renderingMap );
+        RenderingAddOn::Get().SubmitRenderTask( mImpl->mRenderer, data->GetRenderingMap() );
       }
 
       ApplyTextureAndUniforms();
@@ -421,6 +426,13 @@ void NPatchVisual::DoSetOnScene( Actor& actor )
 
 void NPatchVisual::DoSetOffScene( Actor& actor )
 {
+  if((mId != NPatchData::INVALID_NPATCH_DATA_ID) && mReleasePolicy == Toolkit::ImageVisual::ReleasePolicy::DETACHED)
+  {
+    mLoader.Remove(mId, this);
+    mImpl->mResourceStatus = Toolkit::Visual::ResourceStatus::PREPARING;
+    mId = NPatchData::INVALID_NPATCH_DATA_ID;
+  }
+
   actor.RemoveRenderer( mImpl->mRenderer );
   mImpl->mRenderer.Reset();
   mPlacementActor.Reset();
@@ -443,6 +455,7 @@ void NPatchVisual::DoCreatePropertyMap( Property::Map& map ) const
   map.Insert( Toolkit::ImageVisual::Property::URL, mImageUrl.GetUrl() );
   map.Insert( Toolkit::ImageVisual::Property::BORDER_ONLY, mBorderOnly );
   map.Insert( Toolkit::ImageVisual::Property::BORDER, mBorder );
+  map.Insert( Toolkit::ImageVisual::Property::RELEASE_POLICY, mReleasePolicy );
 
   if( mAuxiliaryUrl.IsValid() )
   {
@@ -466,25 +479,31 @@ NPatchVisual::NPatchVisual( VisualFactoryCache& factoryCache )
   mLoader( factoryCache.GetNPatchLoader() ),
   mImageUrl(),
   mAuxiliaryUrl(),
-  mId( NPatchLoader::UNINITIALIZED_ID ),
+  mId(NPatchData::INVALID_NPATCH_DATA_ID),
   mBorderOnly( false ),
   mBorder(),
-  mAuxiliaryImageAlpha( 0.0f )
+  mAuxiliaryImageAlpha( 0.0f ),
+  mReleasePolicy( Toolkit::ImageVisual::ReleasePolicy::DETACHED )
 {
   EnablePreMultipliedAlpha( mFactoryCache.GetPreMultiplyOnLoad() );
 }
 
 NPatchVisual::~NPatchVisual()
 {
+  if((mId != NPatchData::INVALID_NPATCH_DATA_ID) && ( mReleasePolicy != Toolkit::ImageVisual::ReleasePolicy::NEVER ))
+  {
+    mLoader.Remove(mId, this);
+    mId = NPatchData::INVALID_NPATCH_DATA_ID;
+  }
 }
 
 Geometry NPatchVisual::CreateGeometry()
 {
   Geometry geometry;
-  const NPatchLoader::Data* data;
-  if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
+  const NPatchData* data;
+  if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
   {
-    if( data->stretchPixelsX.Size() == 1 && data->stretchPixelsY.Size() == 1 )
+    if( data->GetStretchPixelsX().Size() == 1 && data->GetStretchPixelsY().Size() == 1 )
     {
       if( DALI_UNLIKELY( mBorderOnly ) )
       {
@@ -492,13 +511,13 @@ Geometry NPatchVisual::CreateGeometry()
       }
       else
       {
-        if( data->renderingMap )
+        if( data->GetRenderingMap() )
         {
           uint32_t elementCount[2];
-          geometry = RenderingAddOn::Get().CreateGeometryGrid(data->renderingMap, Uint16Pair(3, 3), elementCount );
+          geometry = RenderingAddOn::Get().CreateGeometryGrid(data->GetRenderingMap(), Uint16Pair(3, 3), elementCount );
           if( mImpl->mRenderer )
           {
-            RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->renderingMap);
+            RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
           }
         }
         else
@@ -507,10 +526,10 @@ Geometry NPatchVisual::CreateGeometry()
         }
       }
     }
-    else if( data->stretchPixelsX.Size() > 0 || data->stretchPixelsY.Size() > 0)
+    else if( data->GetStretchPixelsX().Size() > 0 || data->GetStretchPixelsY().Size() > 0)
     {
-      Uint16Pair gridSize( 2 * data->stretchPixelsX.Size() + 1,  2 * data->stretchPixelsY.Size() + 1 );
-      if( !data->renderingMap )
+      Uint16Pair gridSize( 2 * data->GetStretchPixelsX().Size() + 1,  2 * data->GetStretchPixelsY().Size() + 1 );
+      if( !data->GetRenderingMap() )
       {
         geometry = !mBorderOnly ? CreateGridGeometry( gridSize ) : CreateBorderGeometry( gridSize );
       }
@@ -518,10 +537,10 @@ Geometry NPatchVisual::CreateGeometry()
       {
         uint32_t elementCount[2];
         geometry = !mBorderOnly ?
-                   RenderingAddOn::Get().CreateGeometryGrid(data->renderingMap, gridSize, elementCount ) : CreateBorderGeometry(gridSize );
+                   RenderingAddOn::Get().CreateGeometryGrid(data->GetRenderingMap(), gridSize, elementCount ) : CreateBorderGeometry(gridSize );
         if( mImpl->mRenderer )
         {
-          RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->renderingMap);
+          RenderingAddOn::Get().SubmitRenderTask(mImpl->mRenderer, data->GetRenderingMap());
         }
       }
     }
@@ -537,7 +556,7 @@ Geometry NPatchVisual::CreateGeometry()
 Shader NPatchVisual::CreateShader()
 {
   Shader shader;
-  const NPatchLoader::Data* data;
+  const NPatchData* data;
   // 0 is either no data (load failed?) or no stretch regions on image
   // for both cases we use the default shader
   NPatchUtility::StretchRanges::SizeType xStretchCount = 0;
@@ -551,8 +570,8 @@ Shader NPatchVisual::CreateShader()
   // ask loader for the regions
   if( mLoader.GetNPatchData( mId, data ) )
   {
-    xStretchCount = data->stretchPixelsX.Count();
-    yStretchCount = data->stretchPixelsY.Count();
+    xStretchCount = data->GetStretchPixelsX().Count();
+    yStretchCount = data->GetStretchPixelsY().Count();
   }
 
   if( DALI_LIKELY( !mImpl->mCustomShader ) )
@@ -616,25 +635,25 @@ Shader NPatchVisual::CreateShader()
 
 void NPatchVisual::ApplyTextureAndUniforms()
 {
-  const NPatchLoader::Data* data;
+  const NPatchData* data;
   TextureSet textureSet;
 
-  if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
+  if( mLoader.GetNPatchData( mId, data ) && data->GetLoadingState() == NPatchData::LoadingState::LOAD_COMPLETE )
   {
-    textureSet = data->textureSet;
+    textureSet = data->GetTextures();
 
-    if( data->stretchPixelsX.Size() == 1 && data->stretchPixelsY.Size() == 1 )
+    if( data->GetStretchPixelsX().Size() == 1 && data->GetStretchPixelsY().Size() == 1 )
     {
       //special case for 9 patch
-      Uint16Pair stretchX = data->stretchPixelsX[ 0 ];
-      Uint16Pair stretchY = data->stretchPixelsY[ 0 ];
+      Uint16Pair stretchX = data->GetStretchPixelsX()[ 0 ];
+      Uint16Pair stretchY = data->GetStretchPixelsY()[ 0 ];
 
       uint16_t stretchWidth = ( stretchX.GetY() >= stretchX.GetX() ) ? stretchX.GetY() - stretchX.GetX() : 0;
       uint16_t stretchHeight = ( stretchY.GetY() >= stretchY.GetX() ) ? stretchY.GetY() - stretchY.GetX() : 0;
 
       mImpl->mRenderer.RegisterProperty( "uFixed[0]", Vector2::ZERO );
       mImpl->mRenderer.RegisterProperty( "uFixed[1]", Vector2( stretchX.GetX(), stretchY.GetX()) );
-      mImpl->mRenderer.RegisterProperty( "uFixed[2]", Vector2( data->croppedWidth - stretchWidth, data->croppedHeight - stretchHeight ) );
+      mImpl->mRenderer.RegisterProperty( "uFixed[2]", Vector2( data->GetCroppedWidth() - stretchWidth, data->GetCroppedHeight() - stretchHeight ) );
       mImpl->mRenderer.RegisterProperty( "uStretchTotal", Vector2( stretchWidth, stretchHeight ) );
     }
     else
@@ -642,8 +661,8 @@ void NPatchVisual::ApplyTextureAndUniforms()
       mImpl->mRenderer.RegisterProperty( "uNinePatchFactorsX[0]", Vector2::ZERO );
       mImpl->mRenderer.RegisterProperty( "uNinePatchFactorsY[0]", Vector2::ZERO );
 
-      RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsX", data->stretchPixelsX, data->croppedWidth );
-      RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsY", data->stretchPixelsY, data->croppedHeight );
+      RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsX", data->GetStretchPixelsX(), data->GetCroppedWidth() );
+      RegisterStretchProperties( mImpl->mRenderer, "uNinePatchFactorsY", data->GetStretchPixelsY(), data->GetCroppedHeight() );
     }
   }
   else
@@ -664,10 +683,10 @@ void NPatchVisual::ApplyTextureAndUniforms()
     // If the auxiliary image is smaller than the un-stretched NPatch, use CPU resizing to enlarge it to the
     // same size as the unstretched NPatch. This will give slightly higher quality results than just relying
     // on GL interpolation alone.
-    if( mAuxiliaryPixelBuffer.GetWidth() < data->croppedWidth &&
-        mAuxiliaryPixelBuffer.GetHeight() < data->croppedHeight )
+    if( mAuxiliaryPixelBuffer.GetWidth() < data->GetCroppedWidth() &&
+        mAuxiliaryPixelBuffer.GetHeight() < data->GetCroppedHeight() )
     {
-      mAuxiliaryPixelBuffer.Resize( data->croppedWidth, data->croppedHeight );
+      mAuxiliaryPixelBuffer.Resize( data->GetCroppedWidth(), data->GetCroppedHeight() );
     }
 
     // Note, this resets mAuxiliaryPixelBuffer handle
@@ -838,7 +857,7 @@ Geometry NPatchVisual::CreateBorderGeometry( Uint16Pair gridSize )
 
 void NPatchVisual::SetResource()
 {
-  const NPatchLoader::Data* data;
+  const NPatchData* data;
   if( mImpl->mRenderer && mLoader.GetNPatchData( mId, data ) )
   {
     Geometry geometry = CreateGeometry();
@@ -860,35 +879,32 @@ void NPatchVisual::SetResource()
   }
 }
 
+void NPatchVisual::UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied )
+{
+  EnablePreMultipliedAlpha( preMultiplied );
+  if(!loadSuccess)
+  {
+    // Image loaded and ready to display
+    ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
+  }
+
+  if( mAuxiliaryPixelBuffer || !mAuxiliaryUrl.IsValid() )
+  {
+    SetResource();
+  }
+}
+
 void NPatchVisual::LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied )
 {
-  if( url.GetUrl() == mAuxiliaryUrl.GetUrl() )
+  if( loadSuccess && url.GetUrl() == mAuxiliaryUrl.GetUrl() )
   {
     mAuxiliaryPixelBuffer = pixelBuffer;
-    const NPatchLoader::Data* data;
-    if( mLoader.GetNPatchData( mId, data ) && data->loadCompleted )
-    {
-      SetResource();
-    }
+    SetResource();
   }
   else
   {
-    Devel::PixelBuffer loadedPixelBuffer;
-    if( loadSuccess )
-    {
-      loadedPixelBuffer = pixelBuffer;
-      EnablePreMultipliedAlpha( preMultiplied );
-    }
-    else
-    {
-      loadedPixelBuffer = LoadImageFromFile( mFactoryCache.GetTextureManager().GetBrokenImageUrl() );
-    }
-    mLoader.SetNPatchData( loadSuccess, mId, loadedPixelBuffer, url, preMultiplied );
-
-    if( mAuxiliaryPixelBuffer || !mAuxiliaryUrl.IsValid() )
-    {
-      SetResource();
-    }
+    // Image loaded and ready to display
+    ResourceReady( Toolkit::Visual::ResourceStatus::FAILED );
   }
 }
 
index 41252f6..19bdaff 100644 (file)
@@ -27,6 +27,7 @@
 #include <dali/public-api/object/weak-handle.h>
 
 // INTERNAL INCLUDES
+#include <dali-toolkit/public-api/visuals/image-visual-properties.h>
 #include <dali-toolkit/internal/visuals/texture-upload-observer.h>
 #include <dali-toolkit/internal/visuals/visual-base-impl.h>
 #include <dali-toolkit/internal/visuals/visual-url.h>
@@ -211,7 +212,7 @@ private:
    * To avoid rendering garbage pixels, renderer should be added to actor after the resources are ready.
    * This callback is the place to add the renderer as it would be called once the loading is finished.
    */
-  void UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied ) override {}
+  void UploadComplete( bool loadSuccess, int32_t textureId, TextureSet textureSet, bool useAtlasing, const Vector4& atlasRect, bool preMultiplied ) override;
 
   /**
    * @copydoc TextureUploadObserver::LoadComplete
@@ -222,16 +223,16 @@ private:
   void LoadComplete( bool loadSuccess, Devel::PixelBuffer pixelBuffer, const VisualUrl& url, bool preMultiplied ) override;
 
 private:
-
-  WeakHandle<Actor>  mPlacementActor;       ///< Weakhandle to contain Actor during texture loading
-  NPatchLoader&      mLoader;               ///< reference to N patch loader for fast access
-  VisualUrl          mImageUrl;             ///< The url to the N patch to load
-  VisualUrl          mAuxiliaryUrl;         ///< An auxiliary image that can be displayed on top of the N-Patch
-  std::size_t        mId;                   ///< id of the N patch (from loader/cache)
-  Devel::PixelBuffer mAuxiliaryPixelBuffer; ///< pixel buffer of the auxiliary mask image
-  bool               mBorderOnly;           ///< if only border is desired
-  Rect<int>          mBorder;               ///< The size of the border
-  float              mAuxiliaryImageAlpha;  ///< The alpha value for the auxiliary image only
+  WeakHandle<Actor>        mPlacementActor;                 ///< Weakhandle to contain Actor during texture loading
+  NPatchLoader&            mLoader;                         ///< reference to N patch loader for fast access
+  VisualUrl                mImageUrl;                       ///< The url to the N patch to load
+  VisualUrl                mAuxiliaryUrl;                   ///< An auxiliary image that can be displayed on top of the N-Patch
+  NPatchData::NPatchDataId mId;                             ///< id of the N patch (from loader/cache)
+  Devel::PixelBuffer       mAuxiliaryPixelBuffer;           ///< pixel buffer of the auxiliary mask image
+  bool                     mBorderOnly;                     ///< if only border is desired
+  Rect<int>                mBorder;                         ///< The size of the border
+  float                    mAuxiliaryImageAlpha;            ///< The alpha value for the auxiliary image only
+  Toolkit::ImageVisual::ReleasePolicy::Type mReleasePolicy; ///< The release policy to determine when an image should no longer be cached.
 };
 
 } // namespace Internal
index ea0abc1..bcde348 100644 (file)
@@ -999,7 +999,6 @@ void TextureManager::PostLoad( TextureInfo& textureInfo, Devel::PixelBuffer& pix
   }
   else
   {
-    // @todo If the load was unsuccessful, upload the broken image.
     textureInfo.loadState = LoadState::LOAD_FAILED;
     CheckForWaitingTexture( textureInfo );
     NotifyObservers( textureInfo, false );
@@ -1382,11 +1381,6 @@ void TextureManager::SetBrokenImageUrl(const std::string& brokenImageUrl)
   mBrokenImageUrl = brokenImageUrl;
 }
 
-const std::string TextureManager::GetBrokenImageUrl()
-{
-  return mBrokenImageUrl;
-}
-
 Geometry TextureManager::GetRenderGeometry(TextureId textureId, uint32_t& frontElements, uint32_t& backElements )
 {
   return RenderingAddOn::Get().IsValid() ?
index 28e0869..89a8ed0 100644 (file)
@@ -428,12 +428,6 @@ public:
   void SetBrokenImageUrl(const std::string& brokenImageUrl);
 
   /**
-   * @brief Get an image to be used when a visual has failed to correctly render
-   * @return Returns The broken image url.
-   */
-  const std::string GetBrokenImageUrl();
-
-  /**
    * @brief Returns the geometry associated with texture.
    * @param[in] textureId Id of the texture
    * @param[out] frontElements number of front elements
index fc962bd..67ab688 100644 (file)
@@ -242,12 +242,14 @@ void Internal::Visual::Base::Impl::CustomShader::CreatePropertyMap( Property::Ma
 }
 
 Internal::Visual::Base::Impl::Transform::Transform()
-: mOffset( 0.0f,0.0f ),
-  mSize( 1.0f,1.0f ),
-  mExtraSize( 0.0f,0.0f ),
-  mOffsetSizeMode( 0.0f,0.0f,0.0f,0.0f ),
-  mOrigin( Toolkit::Align::TOP_BEGIN ),
-  mAnchorPoint( Toolkit::Align::TOP_BEGIN )
+: mOffset(0.0f, 0.0f),
+  mSize(1.0f, 1.0f),
+  mExtraSize(0.0f, 0.0f),
+  mOffsetSizeMode(0.0f, 0.0f, 0.0f, 0.0f),
+  mOrigin(Toolkit::Align::TOP_BEGIN),
+  mAnchorPoint(Toolkit::Align::TOP_BEGIN),
+  mOffsetIndex(Property::INVALID_INDEX),
+  mSizeIndex(Property::INVALID_INDEX)
 {
 }
 
@@ -378,8 +380,8 @@ void Internal::Visual::Base::Impl::Transform::GetPropertyMap( Property::Map& map
 
 void Internal::Visual::Base::Impl::Transform::RegisterUniforms( Dali::Renderer renderer, Toolkit::Direction::Type direction )
 {
-  renderer.RegisterProperty( SIZE, mSize );
-  renderer.RegisterProperty( OFFSET, direction == Toolkit::Direction::LEFT_TO_RIGHT ? mOffset : mOffset * Vector2(-1.0f,1.0f));
+  mSizeIndex   = renderer.RegisterProperty(SIZE, mSize);
+  mOffsetIndex = renderer.RegisterProperty(OFFSET, direction == Toolkit::Direction::LEFT_TO_RIGHT ? mOffset : mOffset * Vector2(-1.0f, 1.0f));
   renderer.RegisterProperty( OFFSET_SIZE_MODE, mOffsetSizeMode );
   renderer.RegisterProperty( ORIGIN, PointToVector2( mOrigin, direction ) - Vector2(0.5,0.5) );
   renderer.RegisterProperty( ANCHOR_POINT, Vector2(0.5,0.5) - PointToVector2( mAnchorPoint, direction ) );
index a1056ac..718b5e4 100644 (file)
@@ -116,6 +116,8 @@ struct Base::Impl
     Vector4 mOffsetSizeMode;
     Toolkit::Align::Type mOrigin;
     Toolkit::Align::Type mAnchorPoint;
+    Property::Index      mOffsetIndex;
+    Property::Index      mSizeIndex;
   };
 
   Renderer        mRenderer;
index 6e04f0a..e8e984f 100755 (executable)
@@ -345,6 +345,14 @@ void Visual::Base::SetOffScene( Actor& actor )
       // Update values from Renderer
       mImpl->mMixColor   = mImpl->mRenderer.GetProperty<Vector3>(mImpl->mMixColorIndex);
       mImpl->mMixColor.a = mImpl->mRenderer.GetProperty<float>(DevelRenderer::Property::OPACITY);
+      if(mImpl->mTransform.mOffsetIndex != Property::INVALID_INDEX)
+      {
+        mImpl->mTransform.mOffset = mImpl->mRenderer.GetProperty<Vector2>(mImpl->mTransform.mOffsetIndex);
+      }
+      if(mImpl->mTransform.mSizeIndex != Property::INVALID_INDEX)
+      {
+        mImpl->mTransform.mSize = mImpl->mRenderer.GetProperty<Vector2>(mImpl->mTransform.mSizeIndex);
+      }
       if(mImpl->mCornerRadiusIndex != Property::INVALID_INDEX)
       {
         mImpl->mCornerRadius = mImpl->mRenderer.GetProperty<float>(mImpl->mCornerRadiusIndex);
@@ -365,6 +373,14 @@ void Visual::Base::CreatePropertyMap( Property::Map& map ) const
     // Update values from Renderer
     mImpl->mMixColor   = mImpl->mRenderer.GetProperty<Vector3>(mImpl->mMixColorIndex);
     mImpl->mMixColor.a = mImpl->mRenderer.GetProperty<float>(DevelRenderer::Property::OPACITY);
+    if(mImpl->mTransform.mOffsetIndex != Property::INVALID_INDEX)
+    {
+      mImpl->mTransform.mOffset = mImpl->mRenderer.GetProperty<Vector2>(mImpl->mTransform.mOffsetIndex);
+    }
+    if(mImpl->mTransform.mSizeIndex != Property::INVALID_INDEX)
+    {
+      mImpl->mTransform.mSize = mImpl->mRenderer.GetProperty<Vector2>(mImpl->mTransform.mSizeIndex);
+    }
     if(mImpl->mCornerRadiusIndex != Property::INVALID_INDEX)
     {
       mImpl->mCornerRadius = mImpl->mRenderer.GetProperty<float>(mImpl->mCornerRadiusIndex);
@@ -777,6 +793,14 @@ Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
     {
       return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
     }
+    else if(key.indexKey == Toolkit::Visual::Transform::Property::OFFSET)
+    {
+      return Dali::Property(mImpl->mRenderer, OFFSET);
+    }
+    else if(key.indexKey == Toolkit::Visual::Transform::Property::SIZE)
+    {
+      return Dali::Property(mImpl->mRenderer, SIZE);
+    }
   }
   else
   {
@@ -788,6 +812,14 @@ Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
     {
       return Dali::Property(mImpl->mRenderer, DevelRenderer::Property::OPACITY);
     }
+    else if(key.stringKey == OFFSET)
+    {
+      return Dali::Property(mImpl->mRenderer, OFFSET);
+    }
+    else if(key.stringKey == SIZE)
+    {
+      return Dali::Property(mImpl->mRenderer, SIZE);
+    }
   }
 
   // Other cases
@@ -799,8 +831,10 @@ Dali::Property Visual::Base::GetPropertyObject(Dali::Property::Key key)
       // Register CORNER_RADIUS property
       mImpl->mCornerRadiusIndex = mImpl->mRenderer.RegisterProperty(DevelVisual::Property::CORNER_RADIUS, CORNER_RADIUS, mImpl->mCornerRadius);
       mImpl->mRenderer.RegisterProperty(CORNER_RADIUS_POLICY, mImpl->mCornerRadiusPolicy);
-      index = mImpl->mCornerRadiusIndex;
 
+      mImpl->mRenderer.SetProperty(Renderer::Property::BLEND_MODE, BlendMode::ON);
+
+      index = mImpl->mCornerRadiusIndex;
       mImpl->mNeedCornerRadius = true;
 
       // Change shader
index 44ba50a..db18140 100644 (file)
@@ -29,7 +29,7 @@ namespace Toolkit
 {
 const unsigned int TOOLKIT_MAJOR_VERSION = 2;
 const unsigned int TOOLKIT_MINOR_VERSION = 0;
-const unsigned int TOOLKIT_MICRO_VERSION = 5;
+const unsigned int TOOLKIT_MICRO_VERSION = 7;
 const char* const  TOOLKIT_BUILD_DATE    = __DATE__ " " __TIME__;
 
 #ifdef DEBUG_ENABLED
index bea661d..6f19efc 100644 (file)
@@ -163,7 +163,7 @@ enum Type
 {
   /**
    * @brief Offset of the visual, which can be either relative (percentage [0.0f to 1.0f] of the parent) or absolute (in world units).
-   * @details Name "offset", type Property::VECTOR2.
+   * @details Name "offset", type Property::VECTOR2, animatable.
    * @SINCE_1_2.60
    *
    * @see OFFSET_POLICY
@@ -172,7 +172,7 @@ enum Type
 
   /**
    * @brief Size of the visual, which can be either relative (percentage [0.0f to 1.0f] of the parent) or absolute (in world units).
-   * @details Name "size", type Property::VECTOR2.
+   * @details Name "size", type Property::VECTOR2, animatable.
    * @see SIZE_POLICY
    */
   SIZE,
index 2e17d85..4b468e5 100644 (file)
@@ -1,6 +1,6 @@
 Name:       dali2-toolkit
 Summary:    Dali 3D engine Toolkit
-Version:    2.0.5
+Version:    2.0.7
 Release:    1
 Group:      System/Libraries
 License:    Apache-2.0 and BSD-3-Clause and MIT
@@ -80,6 +80,28 @@ Requires:   %{name} = %{version}-%{release}
 Application development package for Dali 3D engine toolkit - headers and package config
 
 ##############################
+# dali-scene-loader
+##############################
+%define dali2_scene_loader dali2-scene-loader
+%package -n %{dali2_scene_loader}
+Summary:    DLI scene loading library
+Group:      System/Libraries
+License:    Apache-2.0
+
+BuildRequires:  pkgconfig(dali2-toolkit)
+
+%description -n %{dali2_scene_loader}
+Provides functionality for loading and displaying DLI format scenes. See README.md for more details.
+
+%package -n %{dali2_scene_loader}-devel
+Summary:    Development components for dali-scene-loader
+Group:      Development/Building
+Requires:   %{dali2_scene_loader} = %{version}-%{release}
+
+%description -n %{dali2_scene_loader}-devel
+Development components for dali-scene-loader.
+
+##############################
 # Preparation
 ##############################
 %prep
@@ -93,6 +115,8 @@ Application development package for Dali 3D engine toolkit - headers and package
 %define dali_toolkit_style_files    %{dali_data_ro_dir}/toolkit/styles/
 %define dev_include_path %{_includedir}
 
+%define dali_xml_file_dir     %TZ_SYS_RO_PACKAGES
+
 # PO
 {
 cd %{_builddir}/dali2-toolkit-%{version}/dali-toolkit/po
@@ -116,6 +140,10 @@ CXXFLAGS+=" --coverage "
 LDFLAGS+=" --coverage "
 %endif
 
+%ifarch %{arm}
+CXXFLAGS+=" -D_ARCH_ARM_"
+%endif
+
 libtoolize --force
 cd %{_builddir}/dali2-toolkit-%{version}/build/tizen
 
@@ -388,3 +416,18 @@ esac
 %{dali_toolkit_style_files}/1920x1080/*
 %{dali_toolkit_style_files}/default-feedback-theme.json
 %{_datadir}/locale/*/LC_MESSAGES/*
+
+%files -n %{dali2_scene_loader}
+%if 0%{?enable_dali_smack_rules}
+%manifest dali-scene-loader.manifest-smack
+%else
+%manifest dali-scene-loader.manifest
+%endif
+%defattr(-,root,root,-)
+%{_libdir}/lib%{dali2_scene_loader}.so
+%license LICENSE
+
+%files -n %{dali2_scene_loader}-devel
+%defattr(-,root,root,-)
+%{_includedir}/dali-scene-loader/public-api/*
+%{_libdir}/pkgconfig/dali2-scene-loader.pc